[libvirt] PATCH: 0/25: Full thread support for libvirt public API

This patch series is (mostly) aimed at providing full support for threads in the libvirt API. Specifically we remove the restriction that a single virConnectPtr is tied to a thread. Multiple threads can safely use the same object (provided one doesn't free it behind another's back :-P ) This is tested to compile on Linux + Win32 (MinGW), hopefully doesn't break Solaris, but I'll be testing that soon too. I've tortured the patches in various nasty ways are they're now stable enough to let loose and probably merge for wider testing. It'll probably be easier to just fix future problems in tree, rather than maintain this patchset longer. The full patchset is also available here http://berrange.fedorapeople.org/libvirt-threads-2009-01-13/ Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

This patches provides a minimal implementation for virKill on Win32. We don't particularly need this, but it avoids a need to #ifdef out code elsewhere and may come in handy. util.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) Daniel diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -1379,5 +1379,50 @@ int virKillProcess(pid_t pid, int sig) return -1; } +#ifdef WIN32 + /* Mingw / Windows don't have many signals (AFAIK) */ + switch (sig) { + case SIGINT: + /* This does a Ctrl+C equiv */ + if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid)) { + errno = ESRCH; + return -1; + } + break; + + case SIGTERM: + /* Since TerminateProcess is closer to SIG_KILL, we do + * a Ctrl+Break equiv which is more pleasant like the + * good old unix SIGTERM/HUP + */ + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid)) { + errno = ESRCH; + return -1; + } + break; + + default: + { + HANDLE proc; + proc = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (!proc) { + errno = ESRCH; /* Not entirely accurate, but close enough */ + return -1; + } + + /* + * TerminateProcess is more or less equiv to SIG_KILL, in that + * a process can't trap / block it + */ + if (!TerminateProcess(proc, sig)) { + errno = ESRCH; + return -1; + } + CloseHandle(proc); + } + } + return 0; +#else return kill(pid, sig); +#endif } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:38:11PM +0000, Daniel P. Berrange wrote:
This patches provides a minimal implementation for virKill on Win32. We don't particularly need this, but it avoids a need to #ifdef out code elsewhere and may come in handy.
Looks fine to me but I'm unable to asser the correctness of the Win32 code :) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patches provides a minimal implementation for virKill on Win32. We don't particularly need this, but it avoids a need to #ifdef out code elsewhere and may come in handy.
ACK, looks reasonable. (caveat: I haven't read documentation for any of those MS-specific functions)
util.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
Daniel
diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -1379,5 +1379,50 @@ int virKillProcess(pid_t pid, int sig) return -1; }
+#ifdef WIN32 + /* Mingw / Windows don't have many signals (AFAIK) */ + switch (sig) { + case SIGINT: + /* This does a Ctrl+C equiv */ + if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid)) { + errno = ESRCH; + return -1; + } + break; + + case SIGTERM: + /* Since TerminateProcess is closer to SIG_KILL, we do + * a Ctrl+Break equiv which is more pleasant like the + * good old unix SIGTERM/HUP + */ + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid)) { + errno = ESRCH; + return -1; + } + break; + + default: + { + HANDLE proc; + proc = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (!proc) { + errno = ESRCH; /* Not entirely accurate, but close enough */ + return -1; + } + + /* + * TerminateProcess is more or less equiv to SIG_KILL, in that + * a process can't trap / block it + */ + if (!TerminateProcess(proc, sig)) { + errno = ESRCH; + return -1; + } + CloseHandle(proc); + } + } + return 0; +#else return kill(pid, sig); +#endif }

On Tue, Jan 13, 2009 at 05:38:11PM +0000, Daniel P. Berrange wrote:
This patches provides a minimal implementation for virKill on Win32. We don't particularly need this, but it avoids a need to #ifdef out code elsewhere and may come in handy.
util.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
+1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Thu, Jan 15, 2009 at 04:52:26PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:38:11PM +0000, Daniel P. Berrange wrote:
This patches provides a minimal implementation for virKill on Win32. We don't particularly need this, but it avoids a need to #ifdef out code elsewhere and may come in handy.
util.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
+1.
Committed this patch. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

The forthcoming set of patches will do alot of work relating to threads, which requires mutual exclusion primitives. This will include mutexes, condition variables and thread locals. We spefically do not need the ability to actually create thread though, at least not in the libvirt.so, only in the daemon. We currently stub out all the mutex calls to no-ops when pthread.h is missing. This is undesirable because it means we cannot clearly state the thread safety rules for use of libvirt. Saying it is thread safe on Linux, but not on Windows is IMHO not acceptable if we want to have portability of apps using libvirt. So this patch - Adds an internal API for mutexes, condition variables and thread locals - Implements this API for pthreads (covers Liunux, Solaris, and all BSD variants). - Implements this API for Win32 threads (covers Win32 :-) - Updates all code to use these internal APIs instead of pthread-XXX directly. - Adds missing mutex destroy calls from earlier patches. The pthreads implementation is utterly trivial, since I purposely designed the internal API to map 1-to-1 onto pthreads, albeit with many unneccessary bits not exposed. The Win32 implementatioin is more fun. Mutexes are trivial, I just followed the libxml2/glib impls for that. Thread locals are harder since Windows doesn't provide for any destructor to be called when a thread exits, meaning there's a potential memory leak. To address this I maintain some state for Win32 thread locals, in particular the destructor callback. I then added a DllMain() impl, which triggers upon thread-exit and explicitly calls the destructors. This is what DBus/win32-pthread does for thread locals. Finally condition variables are also hard because there's no native condition variable impl on Windows. I followed the GLib condition variable impl for this which uses a queue of waiters, a mutex and a Win32 event for wakeup. Not all that hard in the end. I did consider whether we could use win32-pthreads, but there's a few things I wasn't happy with. There are several different binary DLLs you have to choose between depending on what behaviour you want to C++ apps + exceptions. libvirt really shouldn't have to make that choice. More annoyingly, its pthread.h #include's its own config.h, so it seriously pollutes the global namespace with configure settings that clash with our own. This is a show stopper really. We also don't need the full ability to create threads, so wrapping Win32 thread natively wasn't very hard and that's what nearly every other app already does, so by using Win32 threads directly we should get better interoperabiltiy with other libraries doing the same. mingw32-libvirt.spec.in | 1 po/POTFILES.in | 1 proxy/Makefile.am | 1 qemud/qemud.c | 80 ++++++++++------ qemud/qemud.h | 7 - qemud/remote.c | 72 +++++++-------- src/Makefile.am | 7 + src/datatypes.c | 77 ++++++++-------- src/datatypes.h | 4 src/domain_conf.c | 26 ++--- src/domain_conf.h | 3 src/internal.h | 13 -- src/libvirt.c | 41 ++++++++ src/libvirt_private.syms | 12 ++ src/logging.c | 14 +- src/lxc_conf.h | 3 src/lxc_driver.c | 10 +- src/network_conf.c | 25 ++--- src/network_conf.h | 3 src/network_driver.c | 12 +- src/node_device.c | 4 src/node_device_conf.c | 27 ++--- src/node_device_conf.h | 5 - src/node_device_devkit.c | 8 + src/node_device_hal.c | 6 + src/openvz_conf.c | 13 ++ src/openvz_conf.h | 3 src/openvz_driver.c | 4 src/qemu_conf.h | 3 src/qemu_driver.c | 11 +- src/storage_conf.c | 26 ++--- src/storage_conf.h | 5 - src/storage_driver.c | 10 +- src/test.c | 35 ++++--- src/threads-pthread.c | 117 ++++++++++++++++++++++++ src/threads-pthread.h | 36 +++++++ src/threads-win32.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++ src/threads-win32.h | 39 ++++++++ src/threads.c | 34 +++++++ src/threads.h | 72 +++++++++++++++ src/uml_conf.h | 3 src/uml_driver.c | 10 +- src/virsh.c | 1 43 files changed, 871 insertions(+), 236 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -14,6 +14,7 @@ src/lxc_driver.c src/network_conf.c src/network_driver.c src/node_device.c +src/node_device_conf.c src/openvz_conf.c src/openvz_driver.c src/proxy_internal.c diff --git a/proxy/Makefile.am b/proxy/Makefile.am --- a/proxy/Makefile.am +++ b/proxy/Makefile.am @@ -12,6 +12,7 @@ libexec_PROGRAMS = libvirt_proxy libvirt_proxy_SOURCES = libvirt_proxy.c @top_srcdir@/src/xend_internal.c \ @top_srcdir@/src/xen_internal.c @top_srcdir@/src/virterror.c \ @top_srcdir@/src/sexpr.c \ + @top_srcdir@/src/threads.c \ @top_srcdir@/src/xs_internal.c @top_srcdir@/src/buf.c \ @top_srcdir@/src/capabilities.c \ @top_srcdir@/src/memory.c \ diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -268,11 +268,11 @@ qemudDispatchSignalEvent(int watch ATTRI siginfo_t siginfo; int ret; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); if (saferead(server->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) { VIR_ERROR(_("Failed to read from signal pipe: %s"), strerror(errno)); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); return; } @@ -300,7 +300,7 @@ qemudDispatchSignalEvent(int watch ATTRI if (ret != 0) server->shutdown = 1; - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); } int qemudSetCloseExec(int fd) { @@ -688,9 +688,14 @@ static struct qemud_server *qemudInitial return NULL; } - if (pthread_mutex_init(&server->lock, NULL) != 0) { + if (virMutexInit(&server->lock) < 0) { + VIR_ERROR("%s", _("cannot initialize mutex")); VIR_FREE(server); - return NULL; + } + if (virCondInit(&server->job) < 0) { + VIR_ERROR("%s", _("cannot initialize condition variable")); + virMutexDestroy(&server->lock); + VIR_FREE(server); } server->sigread = sigread; @@ -1117,8 +1122,11 @@ static int qemudDispatchServer(struct qe if (VIR_ALLOC(client) < 0) goto cleanup; - if (pthread_mutex_init(&client->lock, NULL) != 0) + if (virMutexInit(&client->lock) < 0) { + VIR_ERROR("%s", _("cannot initialize mutex")); + VIR_FREE(client); goto cleanup; + } client->magic = QEMUD_CLIENT_MAGIC; client->fd = fd; @@ -1233,12 +1241,12 @@ static struct qemud_client *qemudPending { int i; for (i = 0 ; i < server->nclients ; i++) { - pthread_mutex_lock(&server->clients[i]->lock); + virMutexLock(&server->clients[i]->lock); if (server->clients[i]->mode == QEMUD_MODE_WAIT_DISPATCH) { /* Delibrately don't unlock client - caller wants the lock */ return server->clients[i]; } - pthread_mutex_unlock(&server->clients[i]->lock); + virMutexUnlock(&server->clients[i]->lock); } return NULL; } @@ -1250,10 +1258,14 @@ static void *qemudWorker(void *data) while (1) { struct qemud_client *client; int len; - pthread_mutex_lock(&server->lock); - while ((client = qemudPendingJob(server)) == NULL) - pthread_cond_wait(&server->job, &server->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + while ((client = qemudPendingJob(server)) == NULL) { + if (virCondWait(&server->job, &server->lock) < 0) { + virMutexUnlock(&server->lock); + return NULL; + } + } + virMutexUnlock(&server->lock); /* We own a locked client now... */ client->mode = QEMUD_MODE_IN_DISPATCH; @@ -1271,8 +1283,8 @@ static void *qemudWorker(void *data) qemudDispatchClientFailure(server, client); client->refs--; - pthread_mutex_unlock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&client->lock); + virMutexUnlock(&server->lock); } } @@ -1444,7 +1456,7 @@ static void qemudDispatchClientRead(stru if (qemudRegisterClientEvent(server, client, 1) < 0) qemudDispatchClientFailure(server, client); - pthread_cond_signal(&server->job); + virCondSignal(&server->job); break; } @@ -1627,7 +1639,7 @@ qemudDispatchClientEvent(int watch, int struct qemud_client *client = NULL; int i; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); for (i = 0 ; i < server->nclients ; i++) { if (server->clients[i]->watch == watch) { @@ -1637,12 +1649,12 @@ qemudDispatchClientEvent(int watch, int } if (!client) { - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); return; } - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); if (client->fd != fd) return; @@ -1653,7 +1665,7 @@ qemudDispatchClientEvent(int watch, int qemudDispatchClientRead(server, client); else qemudDispatchClientFailure(server, client); - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); } static int qemudRegisterClientEvent(struct qemud_server *server, @@ -1703,7 +1715,7 @@ qemudDispatchServerEvent(int watch, int struct qemud_server *server = (struct qemud_server *)opaque; struct qemud_socket *sock; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); sock = server->sockets; @@ -1717,7 +1729,7 @@ qemudDispatchServerEvent(int watch, int if (sock && sock->fd == fd && events) qemudDispatchServer(server, sock); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); } @@ -1752,7 +1764,7 @@ static int qemudRunLoop(struct qemud_ser int timerid = -1; int ret = -1, i; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); server->nworkers = min_workers; if (VIR_ALLOC_N(server->workers, server->nworkers) < 0) { @@ -1783,21 +1795,22 @@ static int qemudRunLoop(struct qemud_ser DEBUG("Scheduling shutdown timer %d", timerid); } - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); if (qemudOneLoop() < 0) break; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); reprocess: for (i = 0 ; i < server->nclients ; i++) { int inactive; - pthread_mutex_lock(&server->clients[i]->lock); + virMutexLock(&server->clients[i]->lock); inactive = server->clients[i]->fd == -1 && server->clients[i]->refs == 0; - pthread_mutex_unlock(&server->clients[i]->lock); + virMutexUnlock(&server->clients[i]->lock); if (inactive) { if (server->clients[i]->conn) virConnectClose(server->clients[i]->conn); + virMutexDestroy(&server->clients[i]->lock); VIR_FREE(server->clients[i]); server->nclients--; if (i < server->nclients) { @@ -1826,13 +1839,13 @@ static int qemudRunLoop(struct qemud_ser for (i = 0 ; i < server->nworkers ; i++) { pthread_t thread = server->workers[i]; - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); pthread_join(thread, NULL); - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); } free(server->workers); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); return ret; } @@ -1862,7 +1875,12 @@ static void qemudCleanup(struct qemud_se virStateCleanup(); - free(server); + if (virCondDestroy(&server->job) < 0) { + ; + } + virMutexDestroy(&server->lock); + + VIR_FREE(server); } /* Allocate an array of malloc'd strings from the config file, filename diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -46,6 +46,7 @@ #include <rpc/xdr.h> #include "remote_protocol.h" #include "logging.h" +#include "threads.h" #ifdef __GNUC__ #ifdef HAVE_ANSIDECL_H @@ -88,7 +89,7 @@ enum qemud_sock_type { /* Stores the per-client connection state */ struct qemud_client { - PTHREAD_MUTEX_T(lock); + virMutex lock; int magic; @@ -149,8 +150,8 @@ struct qemud_socket { /* Main server state */ struct qemud_server { - pthread_mutex_t lock; - pthread_cond_t job; + virMutex lock; + virCond job; int nworkers; pthread_t *workers; diff --git a/qemud/remote.c b/qemud/remote.c --- a/qemud/remote.c +++ b/qemud/remote.c @@ -301,7 +301,7 @@ remoteDispatchClientRequest (struct qemu /* Call function. */ conn = client->conn; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); /* * When the RPC handler is called: @@ -315,9 +315,9 @@ remoteDispatchClientRequest (struct qemu */ rv = (data->fn)(server, client, conn, &rerr, &args, &ret); - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); xdr_free (data->args_filter, (char*)&args); @@ -412,9 +412,9 @@ remoteDispatchOpen (struct qemud_server return -1; } - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); name = args->name ? *args->name : NULL; @@ -433,7 +433,7 @@ remoteDispatchOpen (struct qemud_server remoteDispatchConnError(rerr, NULL); rc = client->conn ? 0 : -1; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return rc; } @@ -450,13 +450,13 @@ remoteDispatchClose (struct qemud_server remote_error *rerr ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED) { - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); client->closing = 1; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; } @@ -2472,11 +2472,11 @@ remoteDispatchAuthList (struct qemud_ser remoteDispatchOOMError(rerr); return -1; } - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); ret->types.types_val[0] = client->auth; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; } @@ -2535,9 +2535,9 @@ remoteDispatchAuthSaslInit (struct qemud socklen_t salen; char *localAddr, *remoteAddr; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); REMOTE_DEBUG("Initialize SASL auth %d", client->fd); if (client->auth != REMOTE_AUTH_SASL || @@ -2663,13 +2663,13 @@ remoteDispatchAuthSaslInit (struct qemud goto authfail; } - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); error: - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } @@ -2787,9 +2787,9 @@ remoteDispatchAuthSaslStart (struct qemu unsigned int serveroutlen; int err; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); REMOTE_DEBUG("Start SASL auth %d", client->fd); if (client->auth != REMOTE_AUTH_SASL || @@ -2851,13 +2851,13 @@ remoteDispatchAuthSaslStart (struct qemu client->auth = REMOTE_AUTH_NONE; } - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); error: - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } @@ -2874,9 +2874,9 @@ remoteDispatchAuthSaslStep (struct qemud unsigned int serveroutlen; int err; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); REMOTE_DEBUG("Step SASL auth %d", client->fd); if (client->auth != REMOTE_AUTH_SASL || @@ -2939,13 +2939,13 @@ remoteDispatchAuthSaslStep (struct qemud client->auth = REMOTE_AUTH_NONE; } - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); error: - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } @@ -3011,9 +3011,9 @@ remoteDispatchAuthPolkit (struct qemud_s DBusError err; const char *action; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); action = client->readonly ? "org.libvirt.unix.monitor" : @@ -3091,12 +3091,12 @@ remoteDispatchAuthPolkit (struct qemud_s ret->complete = 1; client->auth = REMOTE_AUTH_NONE; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,14 +46,19 @@ UTIL_SOURCES = \ event.c event.h \ hash.c hash.h \ iptables.c iptables.h \ + logging.c logging.h \ memory.c memory.h \ qparams.c qparams.h \ + threads.c threads.h \ + threads-pthread.h \ + threads-win32.h \ uuid.c uuid.h \ util.c util.h \ virterror.c virterror_internal.h \ - logging.c logging.h \ xml.c xml.h +EXTRA_DIST += threads-pthread.c threads-win32.c + # Internal generic driver infrastructure DRIVER_SOURCES = \ driver.c driver.h \ diff --git a/src/datatypes.c b/src/datatypes.c --- a/src/datatypes.c +++ b/src/datatypes.c @@ -123,6 +123,11 @@ virGetConnect(void) { virLibConnError(NULL, VIR_ERR_NO_MEMORY, _("allocating connection")); goto failed; } + if (virMutexInit(&ret->lock) < 0) { + VIR_FREE(ret); + goto failed; + } + ret->magic = VIR_CONNECT_MAGIC; ret->driver = NULL; ret->networkDriver = NULL; @@ -144,8 +149,6 @@ virGetConnect(void) { if (ret->nodeDevices == NULL) goto failed; - pthread_mutex_init(&ret->lock, NULL); - ret->refs = 1; return(ret); @@ -162,7 +165,7 @@ failed: if (ret->nodeDevices != NULL) virHashFree(ret->nodeDevices, (virHashDeallocator) virNodeDeviceFree); - pthread_mutex_destroy(&ret->lock); + virMutexDestroy(&ret->lock); VIR_FREE(ret); } return(NULL); @@ -197,8 +200,8 @@ virReleaseConnect(virConnectPtr conn) { xmlFreeURI(conn->uri); - pthread_mutex_unlock(&conn->lock); - pthread_mutex_destroy(&conn->lock); + virMutexUnlock(&conn->lock); + virMutexDestroy(&conn->lock); VIR_FREE(conn); } @@ -219,7 +222,7 @@ virUnrefConnect(virConnectPtr conn) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); DEBUG("unref connection %p %d", conn, conn->refs); conn->refs--; refs = conn->refs; @@ -228,7 +231,7 @@ virUnrefConnect(virConnectPtr conn) { /* Already unlocked mutex */ return (0); } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return (refs); } @@ -253,7 +256,7 @@ virGetDomain(virConnectPtr conn, const c virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); /* TODO search by UUID first as they are better differenciators */ @@ -286,11 +289,11 @@ virGetDomain(virConnectPtr conn, const c DEBUG("Existing hash entry %p: refs now %d", ret, ret->refs+1); } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -337,7 +340,7 @@ virReleaseDomain(virDomainPtr domain) { return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -358,7 +361,7 @@ virUnrefDomain(virDomainPtr domain) { virLibConnError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&domain->conn->lock); + virMutexLock(&domain->conn->lock); DEBUG("unref domain %p %s %d", domain, domain->name, domain->refs); domain->refs--; refs = domain->refs; @@ -368,7 +371,7 @@ virUnrefDomain(virDomainPtr domain) { return (0); } - pthread_mutex_unlock(&domain->conn->lock); + virMutexUnlock(&domain->conn->lock); return (refs); } @@ -393,7 +396,7 @@ virGetNetwork(virConnectPtr conn, const virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); /* TODO search by UUID first as they are better differenciators */ @@ -422,11 +425,11 @@ virGetNetwork(virConnectPtr conn, const conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -473,7 +476,7 @@ virReleaseNetwork(virNetworkPtr network) return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -494,7 +497,7 @@ virUnrefNetwork(virNetworkPtr network) { virLibConnError(network->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&network->conn->lock); + virMutexLock(&network->conn->lock); DEBUG("unref network %p %s %d", network, network->name, network->refs); network->refs--; refs = network->refs; @@ -504,7 +507,7 @@ virUnrefNetwork(virNetworkPtr network) { return (0); } - pthread_mutex_unlock(&network->conn->lock); + virMutexUnlock(&network->conn->lock); return (refs); } @@ -530,7 +533,7 @@ virGetStoragePool(virConnectPtr conn, co virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); /* TODO search by UUID first as they are better differenciators */ @@ -559,11 +562,11 @@ virGetStoragePool(virConnectPtr conn, co conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -606,7 +609,7 @@ virReleaseStoragePool(virStoragePoolPtr return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -627,7 +630,7 @@ virUnrefStoragePool(virStoragePoolPtr po virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&pool->conn->lock); + virMutexLock(&pool->conn->lock); DEBUG("unref pool %p %s %d", pool, pool->name, pool->refs); pool->refs--; refs = pool->refs; @@ -637,7 +640,7 @@ virUnrefStoragePool(virStoragePoolPtr po return (0); } - pthread_mutex_unlock(&pool->conn->lock); + virMutexUnlock(&pool->conn->lock); return (refs); } @@ -664,7 +667,7 @@ virGetStorageVol(virConnectPtr conn, con virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); ret = (virStorageVolPtr) virHashLookup(conn->storageVols, key); if (ret == NULL) { @@ -695,11 +698,11 @@ virGetStorageVol(virConnectPtr conn, con conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret->pool); @@ -744,7 +747,7 @@ virReleaseStorageVol(virStorageVolPtr vo return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -765,7 +768,7 @@ virUnrefStorageVol(virStorageVolPtr vol) virLibConnError(vol->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&vol->conn->lock); + virMutexLock(&vol->conn->lock); DEBUG("unref vol %p %s %d", vol, vol->name, vol->refs); vol->refs--; refs = vol->refs; @@ -775,7 +778,7 @@ virUnrefStorageVol(virStorageVolPtr vol) return (0); } - pthread_mutex_unlock(&vol->conn->lock); + virMutexUnlock(&vol->conn->lock); return (refs); } @@ -801,7 +804,7 @@ virGetNodeDevice(virConnectPtr conn, con virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); ret = (virNodeDevicePtr) virHashLookup(conn->nodeDevices, name); if (ret == NULL) { @@ -825,11 +828,11 @@ virGetNodeDevice(virConnectPtr conn, con conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -872,7 +875,7 @@ virReleaseNodeDevice(virNodeDevicePtr de return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -889,7 +892,7 @@ int virUnrefNodeDevice(virNodeDevicePtr dev) { int refs; - pthread_mutex_lock(&dev->conn->lock); + virMutexLock(&dev->conn->lock); DEBUG("unref dev %p %s %d", dev, dev->name, dev->refs); dev->refs--; refs = dev->refs; @@ -899,6 +902,6 @@ virUnrefNodeDevice(virNodeDevicePtr dev) return (0); } - pthread_mutex_unlock(&dev->conn->lock); + virMutexUnlock(&dev->conn->lock); return (refs); } diff --git a/src/datatypes.h b/src/datatypes.h --- a/src/datatypes.h +++ b/src/datatypes.h @@ -26,7 +26,7 @@ #include "hash.h" #include "driver.h" - +#include "threads.h" /** * VIR_CONNECT_MAGIC: @@ -125,7 +125,7 @@ struct _virConnect { * count of any virDomain/virNetwork object associated with * this connection */ - PTHREAD_MUTEX_T (lock); + virMutex lock; virHashTablePtr domains; /* hash table for known domains */ virHashTablePtr networks; /* hash table for known domains */ virHashTablePtr storagePools;/* hash table for known storage pools */ diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -433,6 +433,8 @@ void virDomainObjFree(virDomainObjPtr do VIR_FREE(dom->monitorpath); VIR_FREE(dom->vcpupids); + virMutexDestroy(&dom->lock); + VIR_FREE(dom); } @@ -471,7 +473,13 @@ virDomainObjPtr virDomainAssignDef(virCo return NULL; } - pthread_mutex_init(&domain->lock, NULL); + if (virMutexInit(&domain->lock) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(domain); + return NULL; + } + virDomainObjLock(domain); domain->state = VIR_DOMAIN_SHUTOFF; domain->def = def; @@ -3660,26 +3668,14 @@ const char *virDomainDefDefaultEmulator( } -#ifdef HAVE_PTHREAD_H - void virDomainObjLock(virDomainObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virDomainObjUnlock(virDomainObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else -void virDomainObjLock(virDomainObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virDomainObjUnlock(virDomainObjPtr obj ATTRIBUTE_UNUSED) -{ -} -#endif - #endif /* ! PROXY */ diff --git a/src/domain_conf.h b/src/domain_conf.h --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -31,6 +31,7 @@ #include "internal.h" #include "capabilities.h" #include "util.h" +#include "threads.h" /* Different types of hypervisor */ /* NB: Keep in sync with virDomainVirtTypeToString impl */ @@ -456,7 +457,7 @@ struct _virDomainDef { typedef struct _virDomainObj virDomainObj; typedef virDomainObj *virDomainObjPtr; struct _virDomainObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; int stdin_fd; int stdout_fd; diff --git a/src/internal.h b/src/internal.h --- a/src/internal.h +++ b/src/internal.h @@ -12,19 +12,6 @@ #include <sys/syslimits.h> #endif -#ifdef HAVE_PTHREAD_H -#include <pthread.h> -#define PTHREAD_MUTEX_T(v) pthread_mutex_t v -#else -/* Mutex functions disappear if we don't have pthread. */ -#define PTHREAD_MUTEX_T(v) /*empty*/ -#define pthread_mutex_init(lk,p) /*empty*/ -#define pthread_mutex_destroy(lk) /*empty*/ -#define pthread_mutex_lock(lk) /*empty*/ -#define pthread_mutex_unlock(lk) /*empty*/ -#define pthread_sigmask(h, s, o) sigprocmask((h), (s), (o)) -#endif - /* The library itself is allowed to use deprecated functions / * variables, so effectively undefine the deprecated attribute * which would otherwise be defined in libvirt.h. diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -253,8 +253,12 @@ virInitialize(void) #endif if (initialized) return(0); + initialized = 1; + if (virThreadInitialize() < 0) + return -1; + #ifdef ENABLE_DEBUG debugEnv = getenv("LIBVIRT_DEBUG"); if (debugEnv && *debugEnv && *debugEnv != '0') { @@ -316,6 +320,43 @@ virInitialize(void) return(0); } +#ifdef WIN32 +BOOL WINAPI +DllMain (HINSTANCE instance, DWORD reason, LPVOID ignore); + +BOOL WINAPI +DllMain (HINSTANCE instance ATTRIBUTE_UNUSED, + DWORD reason, + LPVOID ignore ATTRIBUTE_UNUSED) +{ + switch (reason) { + case DLL_PROCESS_ATTACH: + fprintf(stderr, "Initializing DLL\n"); + virInitialize(); + break; + + case DLL_THREAD_ATTACH: + fprintf(stderr, "Thread start\n"); + /* Nothing todo in libvirt yet */ + break; + + case DLL_THREAD_DETACH: + fprintf(stderr, "Thread exit\n"); + /* Release per-thread local data */ + virThreadOnExit(); + break; + + case DLL_PROCESS_DETACH: + fprintf(stderr, "Process exit\n"); + /* Don't bother releasing per-thread data + since (hopefully) windows cleans up + everything on process exit */ + break; + } + + return TRUE; +} +#endif /** diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -261,6 +261,18 @@ virStoragePoolObjLock; virStoragePoolObjUnlock; +# threads.h +virMutexInit; +virMutexDestroy; +virMutexLock; +virMutexUnlock; + +virCondInit; +virCondDestroy; +virCondWait; +virCondSignal; +virCondBroadcast; + # util.h virFileReadAll; virStrToLong_i; diff --git a/src/logging.c b/src/logging.c --- a/src/logging.c +++ b/src/logging.c @@ -37,6 +37,7 @@ #include "logging.h" #include "memory.h" #include "util.h" +#include "threads.h" #ifdef ENABLE_DEBUG int debugFlag = 0; @@ -129,15 +130,15 @@ static int virLogResetOutputs(void); /* * Logs accesses must be serialized though a mutex */ -PTHREAD_MUTEX_T(virLogMutex); +virMutex virLogMutex; static void virLogLock(void) { - pthread_mutex_lock(&virLogMutex); + virMutexLock(&virLogMutex); } static void virLogUnlock(void) { - pthread_mutex_unlock(&virLogMutex); + virMutexUnlock(&virLogMutex); } @@ -167,8 +168,11 @@ static int virLogInitialized = 0; int virLogStartup(void) { if (virLogInitialized) return(-1); + + if (virMutexInit(&virLogMutex) < 0) + return -1; + virLogInitialized = 1; - pthread_mutex_init(&virLogMutex, NULL); virLogLock(); virLogLen = 0; virLogStart = 0; @@ -214,7 +218,7 @@ void virLogShutdown(void) { virLogStart = 0; virLogEnd = 0; virLogUnlock(); - pthread_mutex_destroy(&virLogMutex); + virMutexDestroy(&virLogMutex); virLogInitialized = 0; } diff --git a/src/lxc_conf.h b/src/lxc_conf.h --- a/src/lxc_conf.h +++ b/src/lxc_conf.h @@ -29,6 +29,7 @@ #include "internal.h" #include "domain_conf.h" #include "capabilities.h" +#include "threads.h" #define LXC_CONFIG_DIR SYSCONF_DIR "/libvirt/lxc" #define LXC_STATE_DIR LOCAL_STATE_DIR "/run/libvirt/lxc" @@ -36,7 +37,7 @@ typedef struct __lxc_driver lxc_driver_t; struct __lxc_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; virCapsPtr caps; diff --git a/src/lxc_driver.c b/src/lxc_driver.c --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -57,11 +57,11 @@ static lxc_driver_t *lxc_driver = NULL; static void lxcDriverLock(lxc_driver_t *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void lxcDriverUnlock(lxc_driver_t *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } @@ -1135,7 +1135,10 @@ static int lxcStartup(void) if (VIR_ALLOC(lxc_driver) < 0) { return -1; } - pthread_mutex_init(&lxc_driver->lock, NULL); + if (virMutexInit(&lxc_driver->lock) < 0) { + VIR_FREE(lxc_driver); + return -1; + } lxcDriverLock(lxc_driver); /* Check that this is a container enabled kernel */ @@ -1228,6 +1231,7 @@ static int lxcShutdown(void) VIR_FREE(lxc_driver->stateDir); VIR_FREE(lxc_driver->logDir); lxcDriverUnlock(lxc_driver); + virMutexDestroy(&lxc_driver->lock); VIR_FREE(lxc_driver); lxc_driver = NULL; diff --git a/src/network_conf.c b/src/network_conf.c --- a/src/network_conf.c +++ b/src/network_conf.c @@ -126,6 +126,8 @@ void virNetworkObjFree(virNetworkObjPtr VIR_FREE(net->configFile); VIR_FREE(net->autostartLink); + virMutexDestroy(&net->lock); + VIR_FREE(net); } @@ -163,7 +165,12 @@ virNetworkObjPtr virNetworkAssignDef(vir virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL); return NULL; } - pthread_mutex_init(&network->lock, NULL); + if (virMutexInit(&network->lock) < 0) { + virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(network); + return NULL; + } virNetworkObjLock(network); network->def = def; @@ -823,25 +830,13 @@ int virNetworkDeleteConfig(virConnectPtr return 0; } -#ifdef HAVE_PTHREAD_H - void virNetworkObjLock(virNetworkObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virNetworkObjUnlock(virNetworkObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else -void virNetworkObjLock(virNetworkObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virNetworkObjUnlock(virNetworkObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -#endif diff --git a/src/network_conf.h b/src/network_conf.h --- a/src/network_conf.h +++ b/src/network_conf.h @@ -29,6 +29,7 @@ #include <libxml/xpath.h> #include "internal.h" +#include "threads.h" /* 2 possible types of forwarding */ enum virNetworkForwardType { @@ -82,7 +83,7 @@ struct _virNetworkDef { typedef struct _virNetworkObj virNetworkObj; typedef virNetworkObj *virNetworkObjPtr; struct _virNetworkObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; pid_t dnsmasqPid; unsigned int active : 1; diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -59,7 +59,7 @@ /* Main driver state */ struct network_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; virNetworkObjList networks; @@ -73,11 +73,11 @@ struct network_driver { static void networkDriverLock(struct network_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void networkDriverUnlock(struct network_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static int networkShutdown(void); @@ -134,7 +134,10 @@ networkStartup(void) { if (VIR_ALLOC(driverState) < 0) goto error; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + goto error; + } networkDriverLock(driverState); if (!uid) { @@ -290,6 +293,7 @@ networkShutdown(void) { iptablesContextFree(driverState->iptables); networkDriverUnlock(driverState); + virMutexDestroy(&driverState->lock); VIR_FREE(driverState); diff --git a/src/node_device.c b/src/node_device.c --- a/src/node_device.c +++ b/src/node_device.c @@ -48,12 +48,12 @@ static int dev_has_cap(const virNodeDevi void nodeDeviceLock(virDeviceMonitorStatePtr driver) { DEBUG("LOCK node %p", driver); - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } void nodeDeviceUnlock(virDeviceMonitorStatePtr driver) { DEBUG("UNLOCK node %p", driver); - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static int nodeNumOfDevices(virConnectPtr conn, diff --git a/src/node_device_conf.c b/src/node_device_conf.c --- a/src/node_device_conf.c +++ b/src/node_device_conf.c @@ -99,6 +99,8 @@ void virNodeDeviceObjFree(virNodeDeviceO if (dev->privateFree) (*dev->privateFree)(dev->privateData); + virMutexDestroy(&dev->lock); + VIR_FREE(dev); } @@ -128,12 +130,18 @@ virNodeDeviceObjPtr virNodeDeviceAssignD return NULL; } - pthread_mutex_init(&device->lock, NULL); + if (virMutexInit(&device->lock) < 0) { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(device); + return NULL; + } virNodeDeviceObjLock(device); device->def = def; if (VIR_REALLOC_N(devs->objs, devs->count+1) < 0) { device->def = NULL; + virNodeDeviceObjUnlock(device); virNodeDeviceObjFree(device); virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL); return NULL; @@ -408,26 +416,13 @@ void virNodeDevCapsDefFree(virNodeDevCap } -#ifdef HAVE_PTHREAD_H - void virNodeDeviceObjLock(virNodeDeviceObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else - -void virNodeDeviceObjLock(virNodeDeviceObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -#endif diff --git a/src/node_device_conf.h b/src/node_device_conf.h --- a/src/node_device_conf.h +++ b/src/node_device_conf.h @@ -26,6 +26,7 @@ #include "internal.h" #include "util.h" +#include "threads.h" enum virNodeDevCapType { /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */ @@ -142,7 +143,7 @@ struct _virNodeDeviceDef { typedef struct _virNodeDeviceObj virNodeDeviceObj; typedef virNodeDeviceObj *virNodeDeviceObjPtr; struct _virNodeDeviceObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; virNodeDeviceDefPtr def; /* device definition */ void *privateData; /* driver-specific private data */ @@ -160,7 +161,7 @@ struct _virNodeDeviceObjList { typedef struct _virDeviceMonitorState virDeviceMonitorState; typedef virDeviceMonitorState *virDeviceMonitorStatePtr; struct _virDeviceMonitorState { - PTHREAD_MUTEX_T(lock); + virMutex lock; virNodeDeviceObjList devs; /* currently-known devices */ void *privateData; /* driver-specific private data */ diff --git a/src/node_device_devkit.c b/src/node_device_devkit.c --- a/src/node_device_devkit.c +++ b/src/node_device_devkit.c @@ -298,7 +298,10 @@ static int devkitDeviceMonitorStartup(vo if (VIR_ALLOC(driverState) < 0) return -1; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + return -1; + } g_type_init(); @@ -375,7 +378,8 @@ static int devkitDeviceMonitorShutdown(v virNodeDeviceObjListFree(&driverState->devs); if (devkit_client) g_object_unref(devkit_client); - nodeDeviceLock(driverState); + nodeDeviceUnlock(driverState); + virMutexDestroy(&driveState->lock); VIR_FREE(driverState); return 0; } diff --git a/src/node_device_hal.c b/src/node_device_hal.c --- a/src/node_device_hal.c +++ b/src/node_device_hal.c @@ -678,7 +678,10 @@ static int halDeviceMonitorStartup(void) if (VIR_ALLOC(driverState) < 0) return -1; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + return -1; + } nodeDeviceLock(driverState); /* Allocate and initialize a new HAL context */ @@ -770,6 +773,7 @@ static int halDeviceMonitorShutdown(void (void)libhal_ctx_shutdown(hal_ctx, NULL); (void)libhal_ctx_free(hal_ctx); nodeDeviceUnlock(driverState); + virMutexDestroy(&driverState->lock); VIR_FREE(driverState); return 0; } diff --git a/src/openvz_conf.c b/src/openvz_conf.c --- a/src/openvz_conf.c +++ b/src/openvz_conf.c @@ -392,11 +392,18 @@ int openvzLoadDomains(struct openvz_driv goto cleanup; } - if (VIR_ALLOC(dom) < 0 || - VIR_ALLOC(dom->def) < 0) + if (VIR_ALLOC(dom) < 0) goto no_memory; - pthread_mutex_init(&dom->lock, NULL); + if (virMutexInit(&dom->lock) < 0) { + openvzError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(dom); + goto cleanup; + } + + if (VIR_ALLOC(dom->def) < 0) + goto no_memory; if (STREQ(status, "stopped")) dom->state = VIR_DOMAIN_SHUTOFF; diff --git a/src/openvz_conf.h b/src/openvz_conf.h --- a/src/openvz_conf.h +++ b/src/openvz_conf.h @@ -30,6 +30,7 @@ #include "internal.h" #include "domain_conf.h" +#include "threads.h" enum { OPENVZ_WARN, OPENVZ_ERR }; @@ -53,7 +54,7 @@ enum { OPENVZ_WARN, OPENVZ_ERR }; #define VZCTL_BRIDGE_MIN_VERSION ((3 * 1000 * 1000) + (0 * 1000) + 22 + 1) struct openvz_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; virCapsPtr caps; virDomainObjList domains; diff --git a/src/openvz_driver.c b/src/openvz_driver.c --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -70,12 +70,12 @@ static int openvzDomainSetVcpusInternal( static void openvzDriverLock(struct openvz_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void openvzDriverUnlock(struct openvz_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } struct openvz_driver ovz_driver; diff --git a/src/qemu_conf.h b/src/qemu_conf.h --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -32,6 +32,7 @@ #include "network_conf.h" #include "domain_conf.h" #include "domain_event.h" +#include "threads.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -51,7 +52,7 @@ enum qemud_cmd_flags { /* Main driver state */ struct qemud_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; unsigned int qemuVersion; int nextvmid; diff --git a/src/qemu_driver.c b/src/qemu_driver.c --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -78,11 +78,11 @@ static int qemudShutdown(void); static void qemuDriverLock(struct qemud_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void qemuDriverUnlock(struct qemud_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static int qemudSetCloseExec(int fd) { @@ -273,7 +273,11 @@ qemudStartup(void) { if (VIR_ALLOC(qemu_driver) < 0) return -1; - pthread_mutex_init(&qemu_driver->lock, NULL); + if (virMutexInit(&qemu_driver->lock) < 0) { + qemudLog(QEMUD_ERROR, "%s", _("cannot initialize mutex")); + VIR_FREE(qemu_driver); + return -1; + } qemuDriverLock(qemu_driver); /* Don't have a dom0 so start from 1 */ @@ -482,6 +486,7 @@ qemudShutdown(void) { brShutdown(qemu_driver->brctl); qemuDriverUnlock(qemu_driver); + virMutexDestroy(&qemu_driver->lock); VIR_FREE(qemu_driver); return 0; diff --git a/src/storage_conf.c b/src/storage_conf.c --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -297,6 +297,9 @@ virStoragePoolObjFree(virStoragePoolObjP VIR_FREE(obj->configFile); VIR_FREE(obj->autostartLink); + + virMutexDestroy(&obj->lock); + VIR_FREE(obj); } @@ -1259,13 +1262,19 @@ virStoragePoolObjAssignDef(virConnectPtr return NULL; } - pthread_mutex_init(&pool->lock, NULL); + if (virMutexInit(&pool->lock) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(pool); + return NULL; + } virStoragePoolObjLock(pool); pool->active = 0; pool->def = def; if (VIR_REALLOC_N(pools->objs, pools->count+1) < 0) { pool->def = NULL; + virStoragePoolObjUnlock(pool); virStoragePoolObjFree(pool); virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL); return NULL; @@ -1530,23 +1539,12 @@ char *virStoragePoolSourceListFormat(vir } -#ifdef HAVE_PTHREAD_H - void virStoragePoolObjLock(virStoragePoolObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virStoragePoolObjUnlock(virStoragePoolObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else -void virStoragePoolObjLock(virStoragePoolObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virStoragePoolObjUnlock(virStoragePoolObjPtr obj ATTRIBUTE_UNUSED) -{ -} -#endif diff --git a/src/storage_conf.h b/src/storage_conf.h --- a/src/storage_conf.h +++ b/src/storage_conf.h @@ -26,6 +26,7 @@ #include "internal.h" #include "util.h" +#include "threads.h" /* Shared structs */ @@ -223,7 +224,7 @@ typedef struct _virStoragePoolObj virSto typedef virStoragePoolObj *virStoragePoolObjPtr; struct _virStoragePoolObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; char *configFile; char *autostartLink; @@ -250,7 +251,7 @@ typedef struct _virStorageDriverState vi typedef virStorageDriverState *virStorageDriverStatePtr; struct _virStorageDriverState { - PTHREAD_MUTEX_T(lock); + virMutex lock; virStoragePoolObjList pools; diff --git a/src/storage_driver.c b/src/storage_driver.c --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -49,11 +49,11 @@ static int storageDriverShutdown(void); static void storageDriverLock(virStorageDriverStatePtr driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void storageDriverUnlock(virStorageDriverStatePtr driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static void @@ -113,7 +113,10 @@ storageDriverStartup(void) { if (VIR_ALLOC(driverState) < 0) return -1; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + return -1; + } storageDriverLock(driverState); if (!uid) { @@ -266,6 +269,7 @@ storageDriverShutdown(void) { VIR_FREE(driverState->configDir); VIR_FREE(driverState->autostartDir); storageDriverUnlock(driverState); + virMutexDestroy(&driverState->lock); VIR_FREE(driverState); return 0; diff --git a/src/test.c b/src/test.c --- a/src/test.c +++ b/src/test.c @@ -44,6 +44,7 @@ #include "domain_conf.h" #include "storage_conf.h" #include "xml.h" +#include "threads.h" #define MAX_CPUS 128 @@ -58,7 +59,7 @@ typedef struct _testCell *testCellPtr; #define MAX_CELLS 128 struct _testConn { - PTHREAD_MUTEX_T(lock); + virMutex lock; char path[PATH_MAX]; int nextDomID; @@ -93,20 +94,15 @@ static const virNodeInfo defaultNodeInfo virReportErrorHelper(conn, VIR_FROM_TEST, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) -#ifdef HAVE_THREAD_H static void testDriverLock(testConnPtr driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void testDriverUnlock(testConnPtr driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } -#else -static void testDriverLock(testConnPtr driver ATTRIBUTE_UNUSED) {} -static void testDriverUnlock(testConnPtr driver ATTRIBUTE_UNUSED) {} -#endif static virCapsPtr testBuildCapabilities(virConnectPtr conn) { @@ -216,9 +212,15 @@ static int testOpenDefault(virConnectPtr testError(conn, VIR_ERR_NO_MEMORY, "testConn"); return VIR_DRV_OPEN_ERROR; } + if (virMutexInit(&privconn->lock) < 0) { + testError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(privconn); + return VIR_DRV_OPEN_ERROR; + } + + testDriverLock(privconn); conn->privateData = privconn; - pthread_mutex_init(&privconn->lock, NULL); - testDriverLock(privconn); if (gettimeofday(&tv, NULL) < 0) { testError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("getting time of day")); @@ -282,6 +284,7 @@ static int testOpenDefault(virConnectPtr virStoragePoolObjUnlock(poolobj); testDriverUnlock(privconn); + return VIR_DRV_OPEN_SUCCESS; error: @@ -290,6 +293,7 @@ error: virStoragePoolObjListFree(&privconn->pools); virCapabilitiesFree(privconn->caps); testDriverUnlock(privconn); + conn->privateData = NULL; VIR_FREE(privconn); return VIR_DRV_OPEN_ERROR; } @@ -335,9 +339,15 @@ static int testOpenFromFile(virConnectPt testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); return VIR_DRV_OPEN_ERROR; } + if (virMutexInit(&privconn->lock) < 0) { + testError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(privconn); + return VIR_DRV_OPEN_ERROR; + } + + testDriverLock(privconn); conn->privateData = privconn; - pthread_mutex_init(&privconn->lock, NULL); - testDriverLock(privconn); if (!(privconn->caps = testBuildCapabilities(conn))) goto error; @@ -643,6 +653,7 @@ static int testClose(virConnectPtr conn) virNetworkObjListFree(&privconn->networks); virStoragePoolObjListFree(&privconn->pools); testDriverUnlock(privconn); + virMutexDestroy(&privconn->lock); VIR_FREE (privconn); conn->privateData = NULL; diff --git a/src/threads-pthread.c b/src/threads-pthread.c new file mode 100644 --- /dev/null +++ b/src/threads-pthread.c @@ -0,0 +1,117 @@ +/* + * threads-pthread.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * + */ + +#include <config.h> + + +/* Nothing special required for pthreads */ +int virThreadInitialize(void) +{ + return 0; +} + +void virThreadOnExit(void) +{ +} + + +int virMutexInit(virMutexPtr m) +{ + if (pthread_mutex_init(&m->lock, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virMutexDestroy(virMutexPtr m) +{ + pthread_mutex_destroy(&m->lock); +} + +void virMutexLock(virMutexPtr m){ + pthread_mutex_lock(&m->lock); +} + +void virMutexUnlock(virMutexPtr m) +{ + pthread_mutex_unlock(&m->lock); +} + + + +int virCondInit(virCondPtr c) +{ + if (pthread_cond_init(&c->cond, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +int virCondDestroy(virCondPtr c) +{ + if (pthread_cond_destroy(&c->cond) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +int virCondWait(virCondPtr c, virMutexPtr m) +{ + if (pthread_cond_wait(&c->cond, &m->lock) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virCondSignal(virCondPtr c) +{ + pthread_cond_signal(&c->cond); +} + +void virCondBroadcast(virCondPtr c) +{ + pthread_cond_broadcast(&c->cond); +} + + +int virThreadLocalInit(virThreadLocalPtr l, + virThreadLocalCleanup c) +{ + if (pthread_key_create(&l->key, c) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void *virThreadLocalGet(virThreadLocalPtr l) +{ + return pthread_getspecific(l->key); +} + +void virThreadLocalSet(virThreadLocalPtr l, void *val) +{ + pthread_setspecific(l->key, val); +} diff --git a/src/threads-pthread.h b/src/threads-pthread.h new file mode 100644 --- /dev/null +++ b/src/threads-pthread.h @@ -0,0 +1,36 @@ +/* + * threads.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * + */ + +#include "internal.h" + +#include <pthread.h> + +struct virMutex { + pthread_mutex_t lock; +}; + +struct virCond { + pthread_cond_t cond; +}; + +struct virThreadLocal { + pthread_key_t key; +}; diff --git a/src/threads-win32.c b/src/threads-win32.c new file mode 100644 --- /dev/null +++ b/src/threads-win32.c @@ -0,0 +1,223 @@ +/* + * threads-win32.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * + */ + +#include <config.h> + +#include "memory.h" + +struct virThreadLocalData { + DWORD key; + virThreadLocalCleanup cleanup; +}; +typedef struct virThreadLocalData virThreadLocalData; +typedef virThreadLocalData *virThreadLocalDataPtr; + +virMutex virThreadLocalLock; +unsigned int virThreadLocalCount = 0; +virThreadLocalDataPtr virThreadLocalList = NULL; + + +virThreadLocal virCondEvent; + +void virCondEventCleanup(void *data); + +int virThreadInitialize(void) +{ + virMutexInit(&virThreadLocalLock); + virThreadLocalInit(&virCondEvent, virCondEventCleanup); + + return 0; +} + +void virThreadOnExit(void) +{ + unsigned int i; + virMutexLock(&virThreadLocalLock); + for (i = 0 ; i < virThreadLocalCount ; i++) { + if (virThreadLocalList[i].cleanup) { + void *data = TlsGetValue(virThreadLocalList[i].key); + if (data) { + TlsSetValue(virThreadLocalList[i].key, NULL); + + (virThreadLocalList[i].cleanup)(data); + } + } + } + virMutexUnlock(&virThreadLocalLock); +} + + +int virMutexInit(virMutexPtr m) +{ + if (!(m->lock = CreateMutex(NULL, FALSE, NULL))) { + errno = ESRCH; + return -1; + } + return 0; +} + +void virMutexDestroy(virMutexPtr m) +{ + CloseHandle(m->lock); +} + +void virMutexLock(virMutexPtr m) +{ + WaitForSingleObject(m->lock, INFINITE); +} + +void virMutexUnlock(virMutexPtr m) +{ + ReleaseMutex(m->lock); +} + + + +int virCondInit(virCondPtr c) +{ + c->waiters = NULL; + if (virMutexInit(&c->lock) < 0) + return -1; + return 0; +} + +int virCondDestroy(virCondPtr c) +{ + if (c->waiters) { + errno = EINVAL; + return -1; + } + virMutexDestroy(&c->lock); + return 0; +} + +void virCondEventCleanup(void *data) +{ + HANDLE event = data; + CloseHandle(event); +} + +int virCondWait(virCondPtr c, virMutexPtr m) +{ + HANDLE event = virThreadLocalGet(&virCondEvent); + + if (!event) { + event = CreateEvent(0, FALSE, FALSE, NULL); + if (!event) { + return -1; + } + virThreadLocalSet(&virCondEvent, event); + } + + virMutexLock(&c->lock); + + if (VIR_REALLOC_N(c->waiters, c->nwaiters + 1) < 0) { + virMutexUnlock(&c->lock); + return -1; + } + c->waiters[c->nwaiters] = event; + c->nwaiters++; + + virMutexUnlock(&c->lock); + + virMutexUnlock(m); + + if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED) { + virMutexLock(m); + errno = EINVAL; + return -1; + } + + virMutexLock(m); + return 0; +} + +void virCondSignal(virCondPtr c) +{ + virMutexLock(&c->lock); + + if (c->nwaiters) { + HANDLE event = c->waiters[0]; + if (c->nwaiters > 1) + memmove(c->waiters, + c->waiters + 1, + sizeof(c->waiters[0]) * (c->nwaiters-1)); + if (VIR_REALLOC_N(c->waiters, c->nwaiters - 1) < 0) { + ; + } + c->nwaiters--; + SetEvent(event); + } + + virMutexUnlock(&c->lock); +} + +void virCondBroadcast(virCondPtr c) +{ + virMutexLock(&c->lock); + + if (c->nwaiters) { + unsigned int i; + for (i = 0 ; i < c->nwaiters ; i++) { + HANDLE event = c->waiters[i]; + SetEvent(event); + } + VIR_FREE(c->waiters); + c->nwaiters = 0; + } + + virMutexUnlock(&c->lock); +} + + + +int virThreadLocalInit(virThreadLocalPtr l, + virThreadLocalCleanup c) +{ + if ((l->key = TlsAlloc()) == TLS_OUT_OF_INDEXES) { + errno = ESRCH; + return -1; + } + TlsSetValue(l->key, NULL); + + if (c) { + virMutexLock(&virThreadLocalLock); + if (VIR_REALLOC_N(virThreadLocalList, + virThreadLocalCount + 1) < 0) + return -1; + virThreadLocalList[virThreadLocalCount].key = l->key; + virThreadLocalList[virThreadLocalCount].cleanup = c; + virThreadLocalCount++; + virMutexUnlock(&virThreadLocalLock); + } + + return 0; +} + +void *virThreadLocalGet(virThreadLocalPtr l) +{ + return TlsGetValue(l->key); +} + +void virThreadLocalSet(virThreadLocalPtr l, void *val) +{ + TlsSetValue(l->key, val); +} diff --git a/src/threads-win32.h b/src/threads-win32.h new file mode 100644 --- /dev/null +++ b/src/threads-win32.h @@ -0,0 +1,39 @@ +/* + * threads-win32.h basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * + */ + +#include "internal.h" + +#include <windows.h> + +struct virMutex { + HANDLE lock; +}; + +struct virCond { + virMutex lock; + unsigned int nwaiters; + HANDLE *waiters; +}; + + +struct virThreadLocal { + DWORD key; +}; diff --git a/src/threads.c b/src/threads.c new file mode 100644 --- /dev/null +++ b/src/threads.c @@ -0,0 +1,34 @@ +/* + * threads.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * + */ + +#include <config.h> + +#include "threads.h" + +#ifdef HAVE_PTHREAD_H +#include "threads-pthread.c" +#else +#ifdef WIN32 +#include "threads-win32.c" +#else +#error "Either pthreads or Win32 threads are required" +#endif +#endif diff --git a/src/threads.h b/src/threads.h new file mode 100644 --- /dev/null +++ b/src/threads.h @@ -0,0 +1,72 @@ +/* + * threads.h: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * + */ + +#ifndef __THREADS_H_ +#define __THREADS_H_ + +#include "internal.h" + +typedef struct virMutex virMutex; +typedef virMutex *virMutexPtr; + +typedef struct virCond virCond; +typedef virCond *virCondPtr; + +typedef struct virThreadLocal virThreadLocal; +typedef virThreadLocal *virThreadLocalPtr; + + +int virThreadInitialize(void) ATTRIBUTE_RETURN_CHECK; +void virThreadOnExit(void); + +int virMutexInit(virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +void virMutexDestroy(virMutexPtr m); + +void virMutexLock(virMutexPtr m); +void virMutexUnlock(virMutexPtr m); + + + +int virCondInit(virCondPtr c) ATTRIBUTE_RETURN_CHECK; +int virCondDestroy(virCondPtr c) ATTRIBUTE_RETURN_CHECK; + +int virCondWait(virCondPtr c, virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +void virCondSignal(virCondPtr c); +void virCondBroadcast(virCondPtr c); + + +typedef void (*virThreadLocalCleanup)(void *); +int virThreadLocalInit(virThreadLocalPtr l, + virThreadLocalCleanup c) ATTRIBUTE_RETURN_CHECK; +void *virThreadLocalGet(virThreadLocalPtr l); +void virThreadLocalSet(virThreadLocalPtr l, void*); + +#ifdef HAVE_PTHREAD_H +#include "threads-pthread.h" +#else +#ifdef WIN32 +#include "threads-win32.h" +#else +#error "Either pthreads or Win32 threads are required" +#endif +#endif + +#endif diff --git a/src/uml_conf.h b/src/uml_conf.h --- a/src/uml_conf.h +++ b/src/uml_conf.h @@ -30,6 +30,7 @@ #include "network_conf.h" #include "domain_conf.h" #include "virterror_internal.h" +#include "threads.h" #define umlDebug(fmt, ...) do {} while(0) @@ -39,7 +40,7 @@ /* Main driver state */ struct uml_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; unsigned int umlVersion; int nextvmid; diff --git a/src/uml_driver.c b/src/uml_driver.c --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -74,11 +74,11 @@ static int umlShutdown(void); static void umlDriverLock(struct uml_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void umlDriverUnlock(struct uml_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } @@ -314,7 +314,10 @@ umlStartup(void) { if (VIR_ALLOC(uml_driver) < 0) return -1; - pthread_mutex_init(¨_driver->lock, NULL); + if (virMutexInit(¨_driver->lock) < 0) { + VIR_FREE(uml_driver); + return -1; + } umlDriverLock(uml_driver); /* Don't have a dom0 so start from 1 */ @@ -501,6 +504,7 @@ umlShutdown(void) { brShutdown(uml_driver->brctl); umlDriverUnlock(uml_driver); + virMutexDestroy(¨_driver->lock); VIR_FREE(uml_driver); return 0; diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -6500,6 +6500,7 @@ _vshStrdup(vshControl *ctl, const char * /* * Initialize connection. */ + static int vshInit(vshControl *ctl) { -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:38:55PM +0000, Daniel P. Berrange wrote:
The forthcoming set of patches will do alot of work relating to threads, which requires mutual exclusion primitives. This will include mutexes, condition variables and thread locals. We spefically do not need the ability to actually create thread though, at least not in the libvirt.so, only in the daemon.
We currently stub out all the mutex calls to no-ops when pthread.h is missing. This is undesirable because it means we cannot clearly state the thread safety rules for use of libvirt. Saying it is thread safe on Linux, but not on Windows is IMHO not acceptable if we want to have portability of apps using libvirt.
So this patch
- Adds an internal API for mutexes, condition variables and thread locals - Implements this API for pthreads (covers Liunux, Solaris, and all BSD variants). - Implements this API for Win32 threads (covers Win32 :-) - Updates all code to use these internal APIs instead of pthread-XXX directly. - Adds missing mutex destroy calls from earlier patches.
The pthreads implementation is utterly trivial, since I purposely designed the internal API to map 1-to-1 onto pthreads, albeit with many unneccessary bits not exposed.
Agreed with design and principle, that's basically what we ended up doing for libxml2 but in a more limited way.
The Win32 implementatioin is more fun. Mutexes are trivial, I just followed the libxml2/glib impls for that. Thread locals are harder since Windows doesn't provide for any destructor to be called when a thread exits, meaning there's a potential memory leak. To address this I maintain some state for Win32 thread locals, in particular the destructor callback. I then added a DllMain() impl, which triggers upon thread-exit and explicitly calls the destructors. This is what DBus/win32-pthread does for thread locals. Finally condition variables are also hard because there's no native condition variable impl on Windows. I followed the GLib condition variable impl for this which uses a queue of waiters, a mutex and a Win32 event for wakeup. Not all that hard in the end.
Well thread local storage is always a bit painful, whatever the platform.
I did consider whether we could use win32-pthreads, but there's a few things I wasn't happy with. There are several different binary DLLs you have to choose between depending on what behaviour you want to C++ apps + exceptions. libvirt really shouldn't have to make that choice. More annoyingly, its pthread.h #include's its own config.h, so it seriously pollutes the global namespace with configure settings that clash with our own. This is a show stopper really. We also don't need the full ability to create threads, so wrapping Win32 thread natively wasn't very hard and that's what nearly every other app already does, so by using Win32 threads directly we should get better interoperabiltiy with other libraries doing the same.
Agreed again, when the base OS has the API with proper semantic it's really better to go native. went though the patch, most of it is well replacement from old pthread_* to the new calls. The only thing which surprized me was the Init() routines error, it's the caller which reports the error instead of the callee, I'm wondering a bit why doing it that way. but looks fine, +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

+1 to this. Implementation looks reasonable, and I agree with your decision not to use win32-pthreads. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Thu, Jan 15, 2009 at 04:55:26PM +0000, Richard W.M. Jones wrote:
+1 to this. Implementation looks reasonable, and I agree with your decision not to use win32-pthreads.
Thanks, committed this one. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

THis patch removes use of macros for accessing the privateDtaa on a connection because they obscure data access making it harder to visually validate correct thread locking remote_internal.c | 272 ++++++++++++++++++++++-------------------------------- 1 file changed, 114 insertions(+), 158 deletions(-) Daniel diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -86,14 +86,9 @@ #include "util.h" #include "event.h" -/* Per-connection private data. */ -#define MAGIC 999 /* private_data->magic if OK */ -#define DEAD 998 /* private_data->magic if dead/closed */ - static int inside_daemon = 0; struct private_data { - int magic; /* Should be MAGIC or DEAD. */ int sock; /* Socket. */ int watch; /* File handle watch */ pid_t pid; /* PID of tunnel process */ @@ -118,39 +113,6 @@ struct private_data { /* Timer for flushing domainEvents queue */ int eventFlushTimer; }; - -#define GET_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->privateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - } - -#define GET_NETWORK_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->networkPrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - } - -#define GET_STORAGE_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->storagePrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - "tried to use a closed or uninitialised handle"); \ - return (retcode); \ - } - -#define GET_DEVMON_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->devMonPrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - } - enum { REMOTE_CALL_IN_OPEN = 1, @@ -917,14 +879,12 @@ remoteOpen (virConnectPtr conn, } } - priv->magic = DEAD; priv->sock = -1; ret = doRemoteOpen(conn, priv, auth, rflags); if (ret != VIR_DRV_OPEN_SUCCESS) { conn->privateData = NULL; VIR_FREE(priv); } else { - priv->magic = MAGIC; conn->privateData = priv; } return ret; @@ -1268,9 +1228,6 @@ doRemoteClose (virConnectPtr conn, struc /* See comment for remoteType. */ free (priv->type); - /* Free private data. */ - priv->magic = DEAD; - /* Free callback list */ virDomainEventCallbackListFree(priv->callbackList); @@ -1284,10 +1241,10 @@ remoteClose (virConnectPtr conn) remoteClose (virConnectPtr conn) { int ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; ret = doRemoteClose(conn, priv); - free (priv); + VIR_FREE (priv); conn->privateData = NULL; return ret; @@ -1298,7 +1255,7 @@ remoteSupportsFeature (virConnectPtr con { remote_supports_feature_args args; remote_supports_feature_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; /* VIR_DRV_FEATURE_REMOTE* features are handled directly. */ if (feature == VIR_DRV_FEATURE_REMOTE) return 1; @@ -1326,7 +1283,7 @@ remoteType (virConnectPtr conn) remoteType (virConnectPtr conn) { remote_get_type_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; /* Cached? */ if (priv->type) return priv->type; @@ -1345,7 +1302,7 @@ remoteGetVersion (virConnectPtr conn, un remoteGetVersion (virConnectPtr conn, unsigned long *hvVer) { remote_get_version_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_VERSION, @@ -1361,7 +1318,7 @@ remoteGetHostname (virConnectPtr conn) remoteGetHostname (virConnectPtr conn) { remote_get_hostname_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_HOSTNAME, @@ -1378,7 +1335,7 @@ remoteGetMaxVcpus (virConnectPtr conn, c { remote_get_max_vcpus_args args; remote_get_max_vcpus_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); args.type = type == NULL ? NULL : (char **) &type; @@ -1394,7 +1351,7 @@ remoteNodeGetInfo (virConnectPtr conn, v remoteNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info) { remote_node_get_info_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_INFO, @@ -1418,7 +1375,7 @@ remoteGetCapabilities (virConnectPtr con remoteGetCapabilities (virConnectPtr conn) { remote_get_capabilities_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_CAPABILITIES, @@ -1439,7 +1396,7 @@ remoteNodeGetCellsFreeMemory(virConnectP remote_node_get_cells_free_memory_args args; remote_node_get_cells_free_memory_ret ret; int i; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; if (maxCells > REMOTE_NODE_MAX_CELLS) { errorf (conn, VIR_ERR_RPC, @@ -1470,7 +1427,7 @@ remoteNodeGetFreeMemory (virConnectPtr c remoteNodeGetFreeMemory (virConnectPtr conn) { remote_node_get_free_memory_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_FREE_MEMORY, @@ -1488,7 +1445,7 @@ remoteListDomains (virConnectPtr conn, i int i; remote_list_domains_args args; remote_list_domains_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; if (maxids > REMOTE_DOMAIN_ID_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -1524,7 +1481,7 @@ remoteNumOfDomains (virConnectPtr conn) remoteNumOfDomains (virConnectPtr conn) { remote_num_of_domains_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DOMAINS, @@ -1543,7 +1500,7 @@ remoteDomainCreateXML (virConnectPtr con virDomainPtr dom; remote_domain_create_xml_args args; remote_domain_create_xml_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; args.xml_desc = (char *) xmlDesc; args.flags = flags; @@ -1566,7 +1523,7 @@ remoteDomainLookupByID (virConnectPtr co virDomainPtr dom; remote_domain_lookup_by_id_args args; remote_domain_lookup_by_id_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; args.id = id; @@ -1588,7 +1545,7 @@ remoteDomainLookupByUUID (virConnectPtr virDomainPtr dom; remote_domain_lookup_by_uuid_args args; remote_domain_lookup_by_uuid_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); @@ -1609,7 +1566,7 @@ remoteDomainLookupByName (virConnectPtr virDomainPtr dom; remote_domain_lookup_by_name_args args; remote_domain_lookup_by_name_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; args.name = (char *) name; @@ -1629,7 +1586,7 @@ remoteDomainSuspend (virDomainPtr domain remoteDomainSuspend (virDomainPtr domain) { remote_domain_suspend_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1645,7 +1602,7 @@ remoteDomainResume (virDomainPtr domain) remoteDomainResume (virDomainPtr domain) { remote_domain_resume_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1661,7 +1618,7 @@ remoteDomainShutdown (virDomainPtr domai remoteDomainShutdown (virDomainPtr domain) { remote_domain_shutdown_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1677,7 +1634,7 @@ remoteDomainReboot (virDomainPtr domain, remoteDomainReboot (virDomainPtr domain, unsigned int flags) { remote_domain_reboot_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.flags = flags; @@ -1694,7 +1651,7 @@ remoteDomainDestroy (virDomainPtr domain remoteDomainDestroy (virDomainPtr domain) { remote_domain_destroy_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1711,7 +1668,7 @@ remoteDomainGetOSType (virDomainPtr doma { remote_domain_get_os_type_args args; remote_domain_get_os_type_ret ret; - GET_PRIVATE (domain->conn, NULL); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1730,7 +1687,7 @@ remoteDomainGetMaxMemory (virDomainPtr d { remote_domain_get_max_memory_args args; remote_domain_get_max_memory_ret ret; - GET_PRIVATE (domain->conn, 0); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1747,7 +1704,7 @@ remoteDomainSetMaxMemory (virDomainPtr d remoteDomainSetMaxMemory (virDomainPtr domain, unsigned long memory) { remote_domain_set_max_memory_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.memory = memory; @@ -1764,7 +1721,7 @@ remoteDomainSetMemory (virDomainPtr doma remoteDomainSetMemory (virDomainPtr domain, unsigned long memory) { remote_domain_set_memory_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.memory = memory; @@ -1782,7 +1739,7 @@ remoteDomainGetInfo (virDomainPtr domain { remote_domain_get_info_args args; remote_domain_get_info_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1805,7 +1762,7 @@ remoteDomainSave (virDomainPtr domain, c remoteDomainSave (virDomainPtr domain, const char *to) { remote_domain_save_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.to = (char *) to; @@ -1822,7 +1779,7 @@ remoteDomainRestore (virConnectPtr conn, remoteDomainRestore (virConnectPtr conn, const char *from) { remote_domain_restore_args args; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; args.from = (char *) from; @@ -1838,7 +1795,7 @@ remoteDomainCoreDump (virDomainPtr domai remoteDomainCoreDump (virDomainPtr domain, const char *to, int flags) { remote_domain_core_dump_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.to = (char *) to; @@ -1856,7 +1813,7 @@ remoteDomainSetVcpus (virDomainPtr domai remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus) { remote_domain_set_vcpus_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.nvcpus = nvcpus; @@ -1876,7 +1833,7 @@ remoteDomainPinVcpu (virDomainPtr domain int maplen) { remote_domain_pin_vcpu_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; if (maplen > REMOTE_CPUMAP_MAX) { errorf (domain->conn, VIR_ERR_RPC, @@ -1908,7 +1865,7 @@ remoteDomainGetVcpus (virDomainPtr domai int i; remote_domain_get_vcpus_args args; remote_domain_get_vcpus_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; if (maxinfo > REMOTE_VCPUINFO_MAX) { errorf (domain->conn, VIR_ERR_RPC, @@ -1970,7 +1927,7 @@ remoteDomainGetMaxVcpus (virDomainPtr do { remote_domain_get_max_vcpus_args args; remote_domain_get_max_vcpus_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -1988,7 +1945,7 @@ remoteDomainDumpXML (virDomainPtr domain { remote_domain_dump_xml_args args; remote_domain_dump_xml_ret ret; - GET_PRIVATE (domain->conn, NULL); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.flags = flags; @@ -2012,7 +1969,7 @@ remoteDomainMigratePrepare (virConnectPt { remote_domain_migrate_prepare_args args; remote_domain_migrate_prepare_ret ret; - GET_PRIVATE (dconn, -1); + struct private_data *priv = dconn->privateData; args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in; args.flags = flags; @@ -2045,7 +2002,7 @@ remoteDomainMigratePerform (virDomainPtr unsigned long resource) { remote_domain_migrate_perform_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.cookie.cookie_len = cookielen; @@ -2074,7 +2031,7 @@ remoteDomainMigrateFinish (virConnectPtr virDomainPtr ddom; remote_domain_migrate_finish_args args; remote_domain_migrate_finish_ret ret; - GET_PRIVATE (dconn, NULL); + struct private_data *priv = dconn->privateData; args.dname = (char *) dname; args.cookie.cookie_len = cookielen; @@ -2104,7 +2061,7 @@ remoteDomainMigratePrepare2 (virConnectP { remote_domain_migrate_prepare2_args args; remote_domain_migrate_prepare2_ret ret; - GET_PRIVATE (dconn, -1); + struct private_data *priv = dconn->privateData; args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in; args.flags = flags; @@ -2140,7 +2097,7 @@ remoteDomainMigrateFinish2 (virConnectPt virDomainPtr ddom; remote_domain_migrate_finish2_args args; remote_domain_migrate_finish2_ret ret; - GET_PRIVATE (dconn, NULL); + struct private_data *priv = dconn->privateData; args.dname = (char *) dname; args.cookie.cookie_len = cookielen; @@ -2167,7 +2124,7 @@ remoteListDefinedDomains (virConnectPtr int i; remote_list_defined_domains_args args; remote_list_defined_domains_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; if (maxnames > REMOTE_DOMAIN_NAME_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -2208,7 +2165,7 @@ remoteNumOfDefinedDomains (virConnectPtr remoteNumOfDefinedDomains (virConnectPtr conn) { remote_num_of_defined_domains_ret ret; - GET_PRIVATE (conn, -1); + struct private_data *priv = conn->privateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_DOMAINS, @@ -2223,7 +2180,7 @@ remoteDomainCreate (virDomainPtr domain) remoteDomainCreate (virDomainPtr domain) { remote_domain_create_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2241,7 +2198,7 @@ remoteDomainDefineXML (virConnectPtr con virDomainPtr dom; remote_domain_define_xml_args args; remote_domain_define_xml_ret ret; - GET_PRIVATE (conn, NULL); + struct private_data *priv = conn->privateData; args.xml = (char *) xml; @@ -2261,7 +2218,7 @@ remoteDomainUndefine (virDomainPtr domai remoteDomainUndefine (virDomainPtr domain) { remote_domain_undefine_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2277,7 +2234,7 @@ remoteDomainAttachDevice (virDomainPtr d remoteDomainAttachDevice (virDomainPtr domain, const char *xml) { remote_domain_attach_device_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.xml = (char *) xml; @@ -2294,7 +2251,7 @@ remoteDomainDetachDevice (virDomainPtr d remoteDomainDetachDevice (virDomainPtr domain, const char *xml) { remote_domain_detach_device_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.xml = (char *) xml; @@ -2312,7 +2269,7 @@ remoteDomainGetAutostart (virDomainPtr d { remote_domain_get_autostart_args args; remote_domain_get_autostart_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2330,7 +2287,7 @@ remoteDomainSetAutostart (virDomainPtr d remoteDomainSetAutostart (virDomainPtr domain, int autostart) { remote_domain_set_autostart_args args; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.autostart = autostart; @@ -2348,7 +2305,7 @@ remoteDomainGetSchedulerType (virDomainP { remote_domain_get_scheduler_type_args args; remote_domain_get_scheduler_type_ret ret; - GET_PRIVATE (domain->conn, NULL); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2371,7 +2328,7 @@ remoteDomainGetSchedulerParameters (virD remote_domain_get_scheduler_parameters_args args; remote_domain_get_scheduler_parameters_ret ret; int i; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.nparams = *nparams; @@ -2431,7 +2388,7 @@ remoteDomainSetSchedulerParameters (virD { remote_domain_set_scheduler_parameters_args args; int i, do_error; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2489,7 +2446,7 @@ remoteDomainBlockStats (virDomainPtr dom { remote_domain_block_stats_args args; remote_domain_block_stats_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.path = (char *) path; @@ -2516,7 +2473,7 @@ remoteDomainInterfaceStats (virDomainPtr { remote_domain_interface_stats_args args; remote_domain_interface_stats_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); args.path = (char *) path; @@ -2551,7 +2508,7 @@ remoteDomainBlockPeek (virDomainPtr doma { remote_domain_block_peek_args args; remote_domain_block_peek_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) { errorf (domain->conn, VIR_ERR_RPC, @@ -2596,7 +2553,7 @@ remoteDomainMemoryPeek (virDomainPtr dom { remote_domain_memory_peek_args args; remote_domain_memory_peek_ret ret; - GET_PRIVATE (domain->conn, -1); + struct private_data *priv = domain->conn->privateData; if (size > REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX) { errorf (domain->conn, VIR_ERR_RPC, @@ -2666,14 +2623,12 @@ remoteNetworkOpen (virConnectPtr conn, rflags |= VIR_DRV_OPEN_REMOTE_RO; rflags |= VIR_DRV_OPEN_REMOTE_UNIX; - priv->magic = DEAD; priv->sock = -1; ret = doRemoteOpen(conn, priv, auth, rflags); if (ret != VIR_DRV_OPEN_SUCCESS) { conn->networkPrivateData = NULL; VIR_FREE(priv); } else { - priv->magic = MAGIC; priv->localUses = 1; conn->networkPrivateData = priv; } @@ -2685,7 +2640,8 @@ remoteNetworkClose (virConnectPtr conn) remoteNetworkClose (virConnectPtr conn) { int ret = 0; - GET_NETWORK_PRIVATE (conn, -1); + struct private_data *priv = conn->networkPrivateData; + if (priv->localUses) { priv->localUses--; if (!priv->localUses) { @@ -2701,7 +2657,7 @@ remoteNumOfNetworks (virConnectPtr conn) remoteNumOfNetworks (virConnectPtr conn) { remote_num_of_networks_ret ret; - GET_NETWORK_PRIVATE (conn, -1); + struct private_data *priv = conn->networkPrivateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_NETWORKS, @@ -2718,7 +2674,7 @@ remoteListNetworks (virConnectPtr conn, int i; remote_list_networks_args args; remote_list_networks_ret ret; - GET_NETWORK_PRIVATE (conn, -1); + struct private_data *priv = conn->networkPrivateData; if (maxnames > REMOTE_NETWORK_NAME_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -2759,7 +2715,7 @@ remoteNumOfDefinedNetworks (virConnectPt remoteNumOfDefinedNetworks (virConnectPtr conn) { remote_num_of_defined_networks_ret ret; - GET_NETWORK_PRIVATE (conn, -1); + struct private_data *priv = conn->networkPrivateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_NETWORKS, @@ -2777,7 +2733,7 @@ remoteListDefinedNetworks (virConnectPtr int i; remote_list_defined_networks_args args; remote_list_defined_networks_ret ret; - GET_NETWORK_PRIVATE (conn, -1); + struct private_data *priv = conn->networkPrivateData; if (maxnames > REMOTE_NETWORK_NAME_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -2821,7 +2777,7 @@ remoteNetworkLookupByUUID (virConnectPtr virNetworkPtr net; remote_network_lookup_by_uuid_args args; remote_network_lookup_by_uuid_ret ret; - GET_NETWORK_PRIVATE (conn, NULL); + struct private_data *priv = conn->networkPrivateData; memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); @@ -2844,7 +2800,7 @@ remoteNetworkLookupByName (virConnectPtr virNetworkPtr net; remote_network_lookup_by_name_args args; remote_network_lookup_by_name_ret ret; - GET_NETWORK_PRIVATE (conn, NULL); + struct private_data *priv = conn->networkPrivateData; args.name = (char *) name; @@ -2866,7 +2822,7 @@ remoteNetworkCreateXML (virConnectPtr co virNetworkPtr net; remote_network_create_xml_args args; remote_network_create_xml_ret ret; - GET_NETWORK_PRIVATE (conn, NULL); + struct private_data *priv = conn->networkPrivateData; args.xml = (char *) xmlDesc; @@ -2888,7 +2844,7 @@ remoteNetworkDefineXML (virConnectPtr co virNetworkPtr net; remote_network_define_xml_args args; remote_network_define_xml_ret ret; - GET_NETWORK_PRIVATE (conn, NULL); + struct private_data *priv = conn->networkPrivateData; args.xml = (char *) xml; @@ -2908,7 +2864,7 @@ remoteNetworkUndefine (virNetworkPtr net remoteNetworkUndefine (virNetworkPtr network) { remote_network_undefine_args args; - GET_NETWORK_PRIVATE (network->conn, -1); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); @@ -2924,7 +2880,7 @@ remoteNetworkCreate (virNetworkPtr netwo remoteNetworkCreate (virNetworkPtr network) { remote_network_create_args args; - GET_NETWORK_PRIVATE (network->conn, -1); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); @@ -2940,7 +2896,7 @@ remoteNetworkDestroy (virNetworkPtr netw remoteNetworkDestroy (virNetworkPtr network) { remote_network_destroy_args args; - GET_NETWORK_PRIVATE (network->conn, -1); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); @@ -2957,7 +2913,7 @@ remoteNetworkDumpXML (virNetworkPtr netw { remote_network_dump_xml_args args; remote_network_dump_xml_ret ret; - GET_NETWORK_PRIVATE (network->conn, NULL); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); args.flags = flags; @@ -2977,7 +2933,7 @@ remoteNetworkGetBridgeName (virNetworkPt { remote_network_get_bridge_name_args args; remote_network_get_bridge_name_ret ret; - GET_NETWORK_PRIVATE (network->conn, NULL); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); @@ -2996,7 +2952,7 @@ remoteNetworkGetAutostart (virNetworkPtr { remote_network_get_autostart_args args; remote_network_get_autostart_ret ret; - GET_NETWORK_PRIVATE (network->conn, -1); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); @@ -3015,7 +2971,7 @@ remoteNetworkSetAutostart (virNetworkPtr remoteNetworkSetAutostart (virNetworkPtr network, int autostart) { remote_network_set_autostart_args args; - GET_NETWORK_PRIVATE (network->conn, -1); + struct private_data *priv = network->conn->networkPrivateData; make_nonnull_network (&args.net, network); args.autostart = autostart; @@ -3071,14 +3027,12 @@ remoteStorageOpen (virConnectPtr conn, rflags |= VIR_DRV_OPEN_REMOTE_RO; rflags |= VIR_DRV_OPEN_REMOTE_UNIX; - priv->magic = DEAD; priv->sock = -1; ret = doRemoteOpen(conn, priv, auth, rflags); if (ret != VIR_DRV_OPEN_SUCCESS) { conn->storagePrivateData = NULL; VIR_FREE(priv); } else { - priv->magic = MAGIC; priv->localUses = 1; conn->storagePrivateData = priv; } @@ -3090,7 +3044,8 @@ remoteStorageClose (virConnectPtr conn) remoteStorageClose (virConnectPtr conn) { int ret = 0; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->storagePrivateData; + if (priv->localUses) { priv->localUses--; if (!priv->localUses) { @@ -3106,7 +3061,7 @@ remoteNumOfStoragePools (virConnectPtr c remoteNumOfStoragePools (virConnectPtr conn) { remote_num_of_storage_pools_ret ret; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->storagePrivateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_STORAGE_POOLS, @@ -3123,7 +3078,7 @@ remoteListStoragePools (virConnectPtr co int i; remote_list_storage_pools_args args; remote_list_storage_pools_ret ret; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->storagePrivateData; if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); @@ -3160,7 +3115,7 @@ remoteNumOfDefinedStoragePools (virConne remoteNumOfDefinedStoragePools (virConnectPtr conn) { remote_num_of_defined_storage_pools_ret ret; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->storagePrivateData; memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS, @@ -3178,7 +3133,7 @@ remoteListDefinedStoragePools (virConnec int i; remote_list_defined_storage_pools_args args; remote_list_defined_storage_pools_ret ret; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->storagePrivateData; if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); @@ -3219,7 +3174,7 @@ remoteFindStoragePoolSources (virConnect { remote_find_storage_pool_sources_args args; remote_find_storage_pool_sources_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; const char *emptyString = ""; char *retval; @@ -3259,7 +3214,7 @@ remoteStoragePoolLookupByUUID (virConnec virStoragePoolPtr pool; remote_storage_pool_lookup_by_uuid_args args; remote_storage_pool_lookup_by_uuid_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); @@ -3282,7 +3237,7 @@ remoteStoragePoolLookupByName (virConnec virStoragePoolPtr pool; remote_storage_pool_lookup_by_name_args args; remote_storage_pool_lookup_by_name_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; args.name = (char *) name; @@ -3304,7 +3259,7 @@ remoteStoragePoolLookupByVolume (virStor virStoragePoolPtr pool; remote_storage_pool_lookup_by_volume_args args; remote_storage_pool_lookup_by_volume_ret ret; - GET_STORAGE_PRIVATE (vol->conn, NULL); + struct private_data *priv = vol->conn->storagePrivateData; make_nonnull_storage_vol (&args.vol, vol); @@ -3327,7 +3282,7 @@ remoteStoragePoolCreateXML (virConnectPt virStoragePoolPtr pool; remote_storage_pool_create_xml_args args; remote_storage_pool_create_xml_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; args.xml = (char *) xmlDesc; args.flags = flags; @@ -3350,7 +3305,7 @@ remoteStoragePoolDefineXML (virConnectPt virStoragePoolPtr pool; remote_storage_pool_define_xml_args args; remote_storage_pool_define_xml_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; args.xml = (char *) xml; args.flags = flags; @@ -3371,7 +3326,7 @@ remoteStoragePoolUndefine (virStoragePoo remoteStoragePoolUndefine (virStoragePoolPtr pool) { remote_storage_pool_undefine_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); @@ -3387,7 +3342,7 @@ remoteStoragePoolCreate (virStoragePoolP remoteStoragePoolCreate (virStoragePoolPtr pool, unsigned int flags) { remote_storage_pool_create_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3405,7 +3360,7 @@ remoteStoragePoolBuild (virStoragePoolPt unsigned int flags) { remote_storage_pool_build_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3422,7 +3377,7 @@ remoteStoragePoolDestroy (virStoragePool remoteStoragePoolDestroy (virStoragePoolPtr pool) { remote_storage_pool_destroy_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); @@ -3439,7 +3394,7 @@ remoteStoragePoolDelete (virStoragePoolP unsigned int flags) { remote_storage_pool_delete_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3457,7 +3412,7 @@ remoteStoragePoolRefresh (virStoragePool unsigned int flags) { remote_storage_pool_refresh_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3475,7 +3430,7 @@ remoteStoragePoolGetInfo (virStoragePool { remote_storage_pool_get_info_args args; remote_storage_pool_get_info_ret ret; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); @@ -3499,7 +3454,7 @@ remoteStoragePoolDumpXML (virStoragePool { remote_storage_pool_dump_xml_args args; remote_storage_pool_dump_xml_ret ret; - GET_STORAGE_PRIVATE (pool->conn, NULL); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3519,7 +3474,7 @@ remoteStoragePoolGetAutostart (virStorag { remote_storage_pool_get_autostart_args args; remote_storage_pool_get_autostart_ret ret; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); @@ -3538,7 +3493,7 @@ remoteStoragePoolSetAutostart (virStorag remoteStoragePoolSetAutostart (virStoragePoolPtr pool, int autostart) { remote_storage_pool_set_autostart_args args; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.autostart = autostart; @@ -3557,7 +3512,7 @@ remoteStoragePoolNumOfVolumes (virStorag { remote_storage_pool_num_of_volumes_args args; remote_storage_pool_num_of_volumes_ret ret; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool(&args.pool, pool); @@ -3576,7 +3531,7 @@ remoteStoragePoolListVolumes (virStorage int i; remote_storage_pool_list_volumes_args args; remote_storage_pool_list_volumes_ret ret; - GET_STORAGE_PRIVATE (pool->conn, -1); + struct private_data *priv = pool->conn->storagePrivateData; if (maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { error (pool->conn, VIR_ERR_RPC, _("too many storage volumes requested")); @@ -3619,7 +3574,7 @@ remoteStorageVolLookupByName (virStorage virStorageVolPtr vol; remote_storage_vol_lookup_by_name_args args; remote_storage_vol_lookup_by_name_ret ret; - GET_STORAGE_PRIVATE (pool->conn, NULL); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool(&args.pool, pool); args.name = (char *) name; @@ -3643,7 +3598,7 @@ remoteStorageVolLookupByKey (virConnectP virStorageVolPtr vol; remote_storage_vol_lookup_by_key_args args; remote_storage_vol_lookup_by_key_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; args.key = (char *) key; @@ -3666,7 +3621,7 @@ remoteStorageVolLookupByPath (virConnect virStorageVolPtr vol; remote_storage_vol_lookup_by_path_args args; remote_storage_vol_lookup_by_path_ret ret; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->storagePrivateData; args.path = (char *) path; @@ -3689,7 +3644,7 @@ remoteStorageVolCreateXML (virStoragePoo virStorageVolPtr vol; remote_storage_vol_create_xml_args args; remote_storage_vol_create_xml_ret ret; - GET_STORAGE_PRIVATE (pool->conn, NULL); + struct private_data *priv = pool->conn->storagePrivateData; make_nonnull_storage_pool (&args.pool, pool); args.xml = (char *) xmlDesc; @@ -3712,7 +3667,7 @@ remoteStorageVolDelete (virStorageVolPtr unsigned int flags) { remote_storage_vol_delete_args args; - GET_STORAGE_PRIVATE (vol->conn, -1); + struct private_data *priv = vol->conn->storagePrivateData; make_nonnull_storage_vol (&args.vol, vol); args.flags = flags; @@ -3730,7 +3685,7 @@ remoteStorageVolGetInfo (virStorageVolPt { remote_storage_vol_get_info_args args; remote_storage_vol_get_info_ret ret; - GET_STORAGE_PRIVATE (vol->conn, -1); + struct private_data *priv = vol->conn->storagePrivateData; make_nonnull_storage_vol (&args.vol, vol); @@ -3753,7 +3708,7 @@ remoteStorageVolDumpXML (virStorageVolPt { remote_storage_vol_dump_xml_args args; remote_storage_vol_dump_xml_ret ret; - GET_STORAGE_PRIVATE (vol->conn, NULL); + struct private_data *priv = vol->conn->storagePrivateData; make_nonnull_storage_vol (&args.vol, vol); args.flags = flags; @@ -3773,7 +3728,7 @@ remoteStorageVolGetPath (virStorageVolPt { remote_storage_vol_get_path_args args; remote_storage_vol_get_path_ret ret; - GET_NETWORK_PRIVATE (vol->conn, NULL); + struct private_data *priv = vol->conn->storagePrivateData; make_nonnull_storage_vol (&args.vol, vol); @@ -3813,7 +3768,8 @@ static int remoteDevMonClose(virConnectP static int remoteDevMonClose(virConnectPtr conn) { int ret = 0; - GET_DEVMON_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData; + if (priv->localUses) { priv->localUses--; if (!priv->localUses) { @@ -3831,7 +3787,7 @@ static int remoteNodeNumOfDevices(virCon { remote_node_num_of_devices_args args; remote_node_num_of_devices_ret ret; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData; args.cap = cap ? (char **)&cap : NULL; args.flags = flags; @@ -3855,7 +3811,7 @@ static int remoteNodeListDevices(virConn int i; remote_node_list_devices_args args; remote_node_list_devices_ret ret; - GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData; if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many device names requested")); @@ -3897,7 +3853,7 @@ static virNodeDevicePtr remoteNodeDevice remote_node_device_lookup_by_name_args args; remote_node_device_lookup_by_name_ret ret; virNodeDevicePtr dev; - GET_STORAGE_PRIVATE (conn, NULL); + struct private_data *priv = conn->devMonPrivateData; args.name = (char *)name; @@ -3919,7 +3875,7 @@ static char *remoteNodeDeviceDumpXML(vir { remote_node_device_dump_xml_args args; remote_node_device_dump_xml_ret ret; - GET_STORAGE_PRIVATE (dev->conn, NULL); + struct private_data *priv = dev->conn->devMonPrivateData; args.name = dev->name; args.flags = flags; @@ -3938,7 +3894,7 @@ static char *remoteNodeDeviceGetParent(v { remote_node_device_get_parent_args args; remote_node_device_get_parent_ret ret; - GET_STORAGE_PRIVATE (dev->conn, NULL); + struct private_data *priv = dev->conn->devMonPrivateData; args.name = dev->name; @@ -3956,7 +3912,7 @@ static int remoteNodeDeviceNumOfCaps(vir { remote_node_device_num_of_caps_args args; remote_node_device_num_of_caps_ret ret; - GET_STORAGE_PRIVATE (dev->conn, -1); + struct private_data *priv = dev->conn->devMonPrivateData; args.name = dev->name; @@ -3976,7 +3932,7 @@ static int remoteNodeDeviceListCaps(virN int i; remote_node_device_list_caps_args args; remote_node_device_list_caps_ret ret; - GET_STORAGE_PRIVATE (dev->conn, -1); + struct private_data *priv = dev->conn->devMonPrivateData; if (maxnames > REMOTE_NODE_DEVICE_CAPS_LIST_MAX) { error (dev->conn, VIR_ERR_RPC, _("too many capability names requested")); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
THis patch removes use of macros for accessing the privateDtaa on a connection because they obscure data access making it harder to visually validate correct thread locking
It's a trade-off, weighing loss of the magic-number check against improved readability wrt threads and locking. Sounds like it's the right way to go, ... However, some of the transformations seem not to have worked out properly:
-#define GET_NETWORK_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->networkPrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - } - -#define GET_STORAGE_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->storagePrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - "tried to use a closed or uninitialised handle"); \ - return (retcode); \ - } - -#define GET_DEVMON_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->devMonPrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - }
Here are a few of them:
- GET_NETWORK_PRIVATE (vol->conn, NULL); + struct private_data *priv = vol->conn->storagePrivateData; ^^^^^^^ should be ->networkPrivateData
- GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData; ^^^^^^ storagePrivateData
- GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData;
- GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData;
and a few more.

On Wed, Jan 14, 2009 at 01:23:28PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
THis patch removes use of macros for accessing the privateDtaa on a connection because they obscure data access making it harder to visually validate correct thread locking
However, some of the transformations seem not to have worked out properly:
No, they are all correct AFAIK. The *existing* code was buggy using the wrong macros in many places.
Here are a few of them:
- GET_NETWORK_PRIVATE (vol->conn, NULL); + struct private_data *priv = vol->conn->storagePrivateData; ^^^^^^^ should be ->networkPrivateData
It should be storagePrivateData, since this is from the method remoteStorageVolGetPath
- GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData; ^^^^^^ storagePrivateData
- GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData;
- GET_STORAGE_PRIVATE (conn, -1); + struct private_data *priv = conn->devMonPrivateData;
and a few more.
You need to compare with the function context shown in the patch, rather than assume the original code was correct :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
No, they are all correct AFAIK. The *existing* code was buggy using the wrong macros in many places. ... You need to compare with the function context shown in the patch, rather than assume the original code was correct :-)
Yeah, "assuming" can cause trouble ;-) It would help others down the road if there were a note in the ChangeLog that this change set also fixes several bugs. Some might even prefer to put the minimal bug-fix-only change into its own change set. From an N-year maintenance perspective, that's preferable: less risk of it interfering with other changes. Otherwise, the fixes are buried under all the similar-looking-but-syntax-only changes. If you don't mind splitting this patch, I'll be happy to supply the equivalent pair of replacement change sets.

On Wed, Jan 14, 2009 at 02:29:14PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
No, they are all correct AFAIK. The *existing* code was buggy using the wrong macros in many places. ... You need to compare with the function context shown in the patch, rather than assume the original code was correct :-)
Yeah, "assuming" can cause trouble ;-)
It would help others down the road if there were a note in the ChangeLog that this change set also fixes several bugs.
I comitted this in two parts, the first doing the bug fix. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Wed, Jan 14, 2009 at 02:29:14PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
No, they are all correct AFAIK. The *existing* code was buggy using the wrong macros in many places. ... You need to compare with the function context shown in the patch, rather than assume the original code was correct :-)
Yeah, "assuming" can cause trouble ;-)
It would help others down the road if there were a note in the ChangeLog that this change set also fixes several bugs.
I comitted this in two parts, the first doing the bug fix.
Thanks! FYI, I've rebased the git tree.

"Daniel P. Berrange" <berrange@redhat.com> wrote:
THis patch removes use of macros for accessing the privateDtaa on a connection because they obscure data access making it harder to visually validate correct thread locking
remote_internal.c | 272 ++++++++++++++++++++++-------------------------------- 1 file changed, 114 insertions(+), 158 deletions(-)
Daniel
diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -86,14 +86,9 @@ #include "util.h" #include "event.h"
-/* Per-connection private data. */ -#define MAGIC 999 /* private_data->magic if OK */ -#define DEAD 998 /* private_data->magic if dead/closed */ - static int inside_daemon = 0;
struct private_data { - int magic; /* Should be MAGIC or DEAD. */ int sock; /* Socket. */ int watch; /* File handle watch */ pid_t pid; /* PID of tunnel process */ @@ -118,39 +113,6 @@ struct private_data { /* Timer for flushing domainEvents queue */ int eventFlushTimer; }; - -#define GET_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->privateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - } - -#define GET_NETWORK_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->networkPrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - } - -#define GET_STORAGE_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->storagePrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - "tried to use a closed or uninitialised handle"); \ - return (retcode); \ - } - -#define GET_DEVMON_PRIVATE(conn,retcode) \ - struct private_data *priv = (struct private_data *) (conn)->devMonPrivateData; \ - if (!priv || priv->magic != MAGIC) { \ - error (conn, VIR_ERR_INVALID_ARG, \ - _("tried to use a closed or uninitialised handle")); \ - return (retcode); \ - }
I offer to redo this change set, separating it into two parts: 1) remove .magic and code from macros that manipulates it 2) perform the substitutions: either automate that, or ensure that the generated code does not change. Then there's almost no need for review.

On Tue, Jan 13, 2009 at 05:39:16PM +0000, Daniel P. Berrange wrote:
THis patch removes use of macros for accessing the privateDtaa on a connection because they obscure data access making it harder to visually validate correct thread locking
remote_internal.c | 272 ++++++++++++++++++++++--------------------------------
Ouch, fixes some horrible bugs too. +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler. remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 819 insertions(+), 437 deletions(-) Daniel diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -1253,12 +1253,16 @@ static int static int remoteSupportsFeature (virConnectPtr conn, int feature) { + int rv = -1; remote_supports_feature_args args; remote_supports_feature_ret ret; struct private_data *priv = conn->privateData; /* VIR_DRV_FEATURE_REMOTE* features are handled directly. */ - if (feature == VIR_DRV_FEATURE_REMOTE) return 1; + if (feature == VIR_DRV_FEATURE_REMOTE) { + rv = 1; + goto done; + } args.feature = feature; @@ -1266,9 +1270,12 @@ remoteSupportsFeature (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_SUPPORTS_FEATURE, (xdrproc_t) xdr_remote_supports_feature_args, (char *) &args, (xdrproc_t) xdr_remote_supports_feature_ret, (char *) &ret) == -1) - return -1; - - return ret.supported; + goto done; + + rv = ret.supported; + +done: + return rv; } /* Unfortunately this function is defined to return a static string. @@ -1282,25 +1289,33 @@ static const char * static const char * remoteType (virConnectPtr conn) { + char *rv = NULL; remote_get_type_ret ret; struct private_data *priv = conn->privateData; /* Cached? */ - if (priv->type) return priv->type; + if (priv->type) { + rv = priv->type; + goto done; + } memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_TYPE, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_type_ret, (char *) &ret) == -1) - return NULL; + goto done; /* Stash. */ - return priv->type = ret.type; + rv = priv->type = ret.type; + +done: + return rv; } static int remoteGetVersion (virConnectPtr conn, unsigned long *hvVer) { + int rv = -1; remote_get_version_ret ret; struct private_data *priv = conn->privateData; @@ -1308,15 +1323,19 @@ remoteGetVersion (virConnectPtr conn, un if (call (conn, priv, 0, REMOTE_PROC_GET_VERSION, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_version_ret, (char *) &ret) == -1) - return -1; + goto done; if (hvVer) *hvVer = ret.hv_ver; - return 0; + rv = 0; + +done: + return rv; } static char * remoteGetHostname (virConnectPtr conn) { + char *rv = NULL; remote_get_hostname_ret ret; struct private_data *priv = conn->privateData; @@ -1324,15 +1343,19 @@ remoteGetHostname (virConnectPtr conn) if (call (conn, priv, 0, REMOTE_PROC_GET_HOSTNAME, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_hostname_ret, (char *) &ret) == -1) - return NULL; + goto done; /* Caller frees this. */ - return ret.hostname; + rv = ret.hostname; + +done: + return rv; } static int remoteGetMaxVcpus (virConnectPtr conn, const char *type) { + int rv = -1; remote_get_max_vcpus_args args; remote_get_max_vcpus_ret ret; struct private_data *priv = conn->privateData; @@ -1342,14 +1365,18 @@ remoteGetMaxVcpus (virConnectPtr conn, c if (call (conn, priv, 0, REMOTE_PROC_GET_MAX_VCPUS, (xdrproc_t) xdr_remote_get_max_vcpus_args, (char *) &args, (xdrproc_t) xdr_remote_get_max_vcpus_ret, (char *) &ret) == -1) - return -1; - - return ret.max_vcpus; + goto done; + + rv = ret.max_vcpus; + +done: + return rv; } static int remoteNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info) { + int rv = -1; remote_node_get_info_ret ret; struct private_data *priv = conn->privateData; @@ -1357,7 +1384,7 @@ remoteNodeGetInfo (virConnectPtr conn, v if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_INFO, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_node_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; strncpy (info->model, ret.model, 32); info->model[31] = '\0'; @@ -1368,12 +1395,16 @@ remoteNodeGetInfo (virConnectPtr conn, v info->sockets = ret.sockets; info->cores = ret.cores; info->threads = ret.threads; - return 0; + rv = 0; + +done: + return rv; } static char * remoteGetCapabilities (virConnectPtr conn) { + char *rv = NULL; remote_get_capabilities_ret ret; struct private_data *priv = conn->privateData; @@ -1381,10 +1412,13 @@ remoteGetCapabilities (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_GET_CAPABILITIES, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_capabilities_ret, (char *)&ret) == -1) - return NULL; + goto done; /* Caller frees this. */ - return ret.capabilities; + rv = ret.capabilities; + +done: + return rv; } static int @@ -1393,6 +1427,7 @@ remoteNodeGetCellsFreeMemory(virConnectP int startCell, int maxCells) { + int rv = -1; remote_node_get_cells_free_memory_args args; remote_node_get_cells_free_memory_ret ret; int i; @@ -1403,7 +1438,7 @@ remoteNodeGetCellsFreeMemory(virConnectP _("too many NUMA cells: %d > %d"), maxCells, REMOTE_NODE_MAX_CELLS); - return -1; + goto done; } args.startCell = startCell; @@ -1413,19 +1448,23 @@ remoteNodeGetCellsFreeMemory(virConnectP if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_CELLS_FREE_MEMORY, (xdrproc_t) xdr_remote_node_get_cells_free_memory_args, (char *)&args, (xdrproc_t) xdr_remote_node_get_cells_free_memory_ret, (char *)&ret) == -1) - return -1; + goto done; for (i = 0 ; i < ret.freeMems.freeMems_len ; i++) freeMems[i] = ret.freeMems.freeMems_val[i]; xdr_free((xdrproc_t) xdr_remote_node_get_cells_free_memory_ret, (char *) &ret); - return ret.freeMems.freeMems_len; + rv = ret.freeMems.freeMems_len; + +done: + return rv; } static unsigned long long remoteNodeGetFreeMemory (virConnectPtr conn) { + unsigned long long rv = 0; /* 0 is error value this special function*/ remote_node_get_free_memory_ret ret; struct private_data *priv = conn->privateData; @@ -1433,15 +1472,19 @@ remoteNodeGetFreeMemory (virConnectPtr c if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_FREE_MEMORY, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_remote_node_get_free_memory_ret, (char *)&ret) == -1) - return 0; - - return ret.freeMem; + goto done; + + rv = ret.freeMem; + +done: + return rv; } static int remoteListDomains (virConnectPtr conn, int *ids, int maxids) { + int rv = -1; int i; remote_list_domains_args args; remote_list_domains_ret ret; @@ -1451,7 +1494,7 @@ remoteListDomains (virConnectPtr conn, i errorf (conn, VIR_ERR_RPC, _("too many remote domain IDs: %d > %d"), maxids, REMOTE_DOMAIN_ID_LIST_MAX); - return -1; + goto done; } args.maxids = maxids; @@ -1459,27 +1502,31 @@ remoteListDomains (virConnectPtr conn, i if (call (conn, priv, 0, REMOTE_PROC_LIST_DOMAINS, (xdrproc_t) xdr_remote_list_domains_args, (char *) &args, (xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.ids.ids_len > maxids) { errorf (conn, VIR_ERR_RPC, _("too many remote domain IDs: %d > %d"), ret.ids.ids_len, maxids); - xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret); - return -1; + goto cleanup; } for (i = 0; i < ret.ids.ids_len; ++i) ids[i] = ret.ids.ids_val[i]; + rv = ret.ids.ids_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret); - return ret.ids.ids_len; +done: + return rv; } static int remoteNumOfDomains (virConnectPtr conn) { + int rv = -1; remote_num_of_domains_ret ret; struct private_data *priv = conn->privateData; @@ -1487,9 +1534,12 @@ remoteNumOfDomains (virConnectPtr conn) if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DOMAINS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_domains_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static virDomainPtr @@ -1497,7 +1547,7 @@ remoteDomainCreateXML (virConnectPtr con const char *xmlDesc, unsigned int flags) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_create_xml_args args; remote_domain_create_xml_ret ret; struct private_data *priv = conn->privateData; @@ -1509,18 +1559,19 @@ remoteDomainCreateXML (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE_XML, (xdrproc_t) xdr_remote_domain_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_domain_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_create_xml_ret, (char *) &ret); +done: return dom; } static virDomainPtr remoteDomainLookupByID (virConnectPtr conn, int id) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_lookup_by_id_args args; remote_domain_lookup_by_id_ret ret; struct private_data *priv = conn->privateData; @@ -1531,18 +1582,19 @@ remoteDomainLookupByID (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_ID, (xdrproc_t) xdr_remote_domain_lookup_by_id_args, (char *) &args, (xdrproc_t) xdr_remote_domain_lookup_by_id_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_id_ret, (char *) &ret); +done: return dom; } static virDomainPtr remoteDomainLookupByUUID (virConnectPtr conn, const unsigned char *uuid) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_lookup_by_uuid_args args; remote_domain_lookup_by_uuid_ret ret; struct private_data *priv = conn->privateData; @@ -1553,17 +1605,19 @@ remoteDomainLookupByUUID (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_UUID, (xdrproc_t) xdr_remote_domain_lookup_by_uuid_args, (char *) &args, (xdrproc_t) xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret); + +done: return dom; } static virDomainPtr remoteDomainLookupByName (virConnectPtr conn, const char *name) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_lookup_by_name_args args; remote_domain_lookup_by_name_ret ret; struct private_data *priv = conn->privateData; @@ -1574,17 +1628,19 @@ remoteDomainLookupByName (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_domain_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_domain_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_name_ret, (char *) &ret); +done: return dom; } static int remoteDomainSuspend (virDomainPtr domain) { + int rv = -1; remote_domain_suspend_args args; struct private_data *priv = domain->conn->privateData; @@ -1593,14 +1649,18 @@ remoteDomainSuspend (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SUSPEND, (xdrproc_t) xdr_remote_domain_suspend_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainResume (virDomainPtr domain) { + int rv = -1; remote_domain_resume_args args; struct private_data *priv = domain->conn->privateData; @@ -1609,14 +1669,18 @@ remoteDomainResume (virDomainPtr domain) if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_RESUME, (xdrproc_t) xdr_remote_domain_resume_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainShutdown (virDomainPtr domain) { + int rv = -1; remote_domain_shutdown_args args; struct private_data *priv = domain->conn->privateData; @@ -1625,14 +1689,18 @@ remoteDomainShutdown (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SHUTDOWN, (xdrproc_t) xdr_remote_domain_shutdown_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainReboot (virDomainPtr domain, unsigned int flags) { + int rv = -1; remote_domain_reboot_args args; struct private_data *priv = domain->conn->privateData; @@ -1642,14 +1710,18 @@ remoteDomainReboot (virDomainPtr domain, if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_REBOOT, (xdrproc_t) xdr_remote_domain_reboot_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainDestroy (virDomainPtr domain) { + int rv = -1; remote_domain_destroy_args args; struct private_data *priv = domain->conn->privateData; @@ -1658,14 +1730,18 @@ remoteDomainDestroy (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DESTROY, (xdrproc_t) xdr_remote_domain_destroy_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static char * remoteDomainGetOSType (virDomainPtr domain) { + char *rv = NULL; remote_domain_get_os_type_args args; remote_domain_get_os_type_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1676,15 +1752,19 @@ remoteDomainGetOSType (virDomainPtr doma if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_OS_TYPE, (xdrproc_t) xdr_remote_domain_get_os_type_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_os_type_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.type; + goto done; + + /* Caller frees. */ + rv = ret.type; + +done: + return rv; } static unsigned long remoteDomainGetMaxMemory (virDomainPtr domain) { + unsigned long rv = 0; remote_domain_get_max_memory_args args; remote_domain_get_max_memory_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1695,14 +1775,18 @@ remoteDomainGetMaxMemory (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_MAX_MEMORY, (xdrproc_t) xdr_remote_domain_get_max_memory_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_max_memory_ret, (char *) &ret) == -1) - return 0; - - return ret.memory; + goto done; + + rv = ret.memory; + +done: + return rv; } static int remoteDomainSetMaxMemory (virDomainPtr domain, unsigned long memory) { + int rv = -1; remote_domain_set_max_memory_args args; struct private_data *priv = domain->conn->privateData; @@ -1712,14 +1796,18 @@ remoteDomainSetMaxMemory (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_MAX_MEMORY, (xdrproc_t) xdr_remote_domain_set_max_memory_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainSetMemory (virDomainPtr domain, unsigned long memory) { + int rv = -1; remote_domain_set_memory_args args; struct private_data *priv = domain->conn->privateData; @@ -1729,14 +1817,18 @@ remoteDomainSetMemory (virDomainPtr doma if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_MEMORY, (xdrproc_t) xdr_remote_domain_set_memory_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainGetInfo (virDomainPtr domain, virDomainInfoPtr info) { + int rv = -1; remote_domain_get_info_args args; remote_domain_get_info_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1747,7 +1839,7 @@ remoteDomainGetInfo (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_INFO, (xdrproc_t) xdr_remote_domain_get_info_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; info->state = ret.state; info->maxMem = ret.max_mem; @@ -1755,12 +1847,16 @@ remoteDomainGetInfo (virDomainPtr domain info->nrVirtCpu = ret.nr_virt_cpu; info->cpuTime = ret.cpu_time; - return 0; + rv = 0; + +done: + return rv; } static int remoteDomainSave (virDomainPtr domain, const char *to) { + int rv = -1; remote_domain_save_args args; struct private_data *priv = domain->conn->privateData; @@ -1770,14 +1866,18 @@ remoteDomainSave (virDomainPtr domain, c if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SAVE, (xdrproc_t) xdr_remote_domain_save_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainRestore (virConnectPtr conn, const char *from) { + int rv = -1; remote_domain_restore_args args; struct private_data *priv = conn->privateData; @@ -1786,14 +1886,18 @@ remoteDomainRestore (virConnectPtr conn, if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_RESTORE, (xdrproc_t) xdr_remote_domain_restore_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainCoreDump (virDomainPtr domain, const char *to, int flags) { + int rv = -1; remote_domain_core_dump_args args; struct private_data *priv = domain->conn->privateData; @@ -1804,14 +1908,18 @@ remoteDomainCoreDump (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CORE_DUMP, (xdrproc_t) xdr_remote_domain_core_dump_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus) { + int rv = -1; remote_domain_set_vcpus_args args; struct private_data *priv = domain->conn->privateData; @@ -1821,9 +1929,12 @@ remoteDomainSetVcpus (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_VCPUS, (xdrproc_t) xdr_remote_domain_set_vcpus_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int @@ -1832,6 +1943,7 @@ remoteDomainPinVcpu (virDomainPtr domain unsigned char *cpumap, int maplen) { + int rv = -1; remote_domain_pin_vcpu_args args; struct private_data *priv = domain->conn->privateData; @@ -1839,7 +1951,7 @@ remoteDomainPinVcpu (virDomainPtr domain errorf (domain->conn, VIR_ERR_RPC, _("map length greater than maximum: %d > %d"), maplen, REMOTE_CPUMAP_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -1850,9 +1962,12 @@ remoteDomainPinVcpu (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_PIN_VCPU, (xdrproc_t) xdr_remote_domain_pin_vcpu_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int @@ -1862,6 +1977,7 @@ remoteDomainGetVcpus (virDomainPtr domai unsigned char *cpumaps, int maplen) { + int rv = -1; int i; remote_domain_get_vcpus_args args; remote_domain_get_vcpus_ret ret; @@ -1871,13 +1987,13 @@ remoteDomainGetVcpus (virDomainPtr domai errorf (domain->conn, VIR_ERR_RPC, _("vCPU count exceeds maximum: %d > %d"), maxinfo, REMOTE_VCPUINFO_MAX); - return -1; + goto done; } if (maxinfo * maplen > REMOTE_CPUMAPS_MAX) { errorf (domain->conn, VIR_ERR_RPC, _("vCPU map buffer length exceeds maximum: %d > %d"), maxinfo * maplen, REMOTE_CPUMAPS_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -1888,21 +2004,19 @@ remoteDomainGetVcpus (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_VCPUS, (xdrproc_t) xdr_remote_domain_get_vcpus_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.info.info_len > maxinfo) { errorf (domain->conn, VIR_ERR_RPC, _("host reports too many vCPUs: %d > %d"), ret.info.info_len, maxinfo); - xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); - return -1; + goto cleanup; } if (ret.cpumaps.cpumaps_len > maxinfo * maplen) { errorf (domain->conn, VIR_ERR_RPC, _("host reports map buffer length exceeds maximum: %d > %d"), ret.cpumaps.cpumaps_len, maxinfo * maplen); - xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); - return -1; + goto cleanup; } memset (info, 0, sizeof (virVcpuInfo) * maxinfo); @@ -1918,13 +2032,19 @@ remoteDomainGetVcpus (virDomainPtr domai for (i = 0; i < ret.cpumaps.cpumaps_len; ++i) cpumaps[i] = ret.cpumaps.cpumaps_val[i]; + rv = ret.info.info_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); - return ret.info.info_len; + +done: + return rv; } static int remoteDomainGetMaxVcpus (virDomainPtr domain) { + int rv = -1; remote_domain_get_max_vcpus_args args; remote_domain_get_max_vcpus_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1935,14 +2055,18 @@ remoteDomainGetMaxVcpus (virDomainPtr do if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_MAX_VCPUS, (xdrproc_t) xdr_remote_domain_get_max_vcpus_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_max_vcpus_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static char * remoteDomainDumpXML (virDomainPtr domain, int flags) { + char *rv = NULL; remote_domain_dump_xml_args args; remote_domain_dump_xml_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1954,10 +2078,13 @@ remoteDomainDumpXML (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DUMP_XML, (xdrproc_t) xdr_remote_domain_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_domain_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static int @@ -1967,6 +2094,7 @@ remoteDomainMigratePrepare (virConnectPt unsigned long flags, const char *dname, unsigned long resource) { + int rv = -1; remote_domain_migrate_prepare_args args; remote_domain_migrate_prepare_ret ret; struct private_data *priv = dconn->privateData; @@ -1980,7 +2108,7 @@ remoteDomainMigratePrepare (virConnectPt if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE, (xdrproc_t) xdr_remote_domain_migrate_prepare_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_prepare_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.cookie.cookie_len > 0) { *cookie = ret.cookie.cookie_val; /* Caller frees. */ @@ -1989,7 +2117,10 @@ remoteDomainMigratePrepare (virConnectPt if (ret.uri_out) *uri_out = *ret.uri_out; /* Caller frees. */ - return 0; + rv = 0; + +done: + return rv; } static int @@ -2001,6 +2132,7 @@ remoteDomainMigratePerform (virDomainPtr const char *dname, unsigned long resource) { + int rv = -1; remote_domain_migrate_perform_args args; struct private_data *priv = domain->conn->privateData; @@ -2015,9 +2147,12 @@ remoteDomainMigratePerform (virDomainPtr if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PERFORM, (xdrproc_t) xdr_remote_domain_migrate_perform_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static virDomainPtr @@ -2028,7 +2163,7 @@ remoteDomainMigrateFinish (virConnectPtr const char *uri, unsigned long flags) { - virDomainPtr ddom; + virDomainPtr ddom = NULL; remote_domain_migrate_finish_args args; remote_domain_migrate_finish_ret ret; struct private_data *priv = dconn->privateData; @@ -2043,11 +2178,12 @@ remoteDomainMigrateFinish (virConnectPtr if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH, (xdrproc_t) xdr_remote_domain_migrate_finish_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_finish_ret, (char *) &ret) == -1) - return NULL; + goto done; ddom = get_nonnull_domain (dconn, ret.ddom); xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish_ret, (char *) &ret); +done: return ddom; } @@ -2059,6 +2195,7 @@ remoteDomainMigratePrepare2 (virConnectP unsigned long resource, const char *dom_xml) { + int rv = -1; remote_domain_migrate_prepare2_args args; remote_domain_migrate_prepare2_ret ret; struct private_data *priv = dconn->privateData; @@ -2073,7 +2210,7 @@ remoteDomainMigratePrepare2 (virConnectP if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2, (xdrproc_t) xdr_remote_domain_migrate_prepare2_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_prepare2_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.cookie.cookie_len > 0) { *cookie = ret.cookie.cookie_val; /* Caller frees. */ @@ -2082,7 +2219,10 @@ remoteDomainMigratePrepare2 (virConnectP if (ret.uri_out) *uri_out = *ret.uri_out; /* Caller frees. */ - return 0; + rv = 0; + +done: + return rv; } static virDomainPtr @@ -2094,7 +2234,7 @@ remoteDomainMigrateFinish2 (virConnectPt unsigned long flags, int retcode) { - virDomainPtr ddom; + virDomainPtr ddom = NULL; remote_domain_migrate_finish2_args args; remote_domain_migrate_finish2_ret ret; struct private_data *priv = dconn->privateData; @@ -2110,17 +2250,19 @@ remoteDomainMigrateFinish2 (virConnectPt if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2, (xdrproc_t) xdr_remote_domain_migrate_finish2_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_finish2_ret, (char *) &ret) == -1) - return NULL; + goto done; ddom = get_nonnull_domain (dconn, ret.ddom); xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish2_ret, (char *) &ret); +done: return ddom; } static int remoteListDefinedDomains (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_defined_domains_args args; remote_list_defined_domains_ret ret; @@ -2130,7 +2272,7 @@ remoteListDefinedDomains (virConnectPtr errorf (conn, VIR_ERR_RPC, _("too many remote domain names: %d > %d"), maxnames, REMOTE_DOMAIN_NAME_LIST_MAX); - return -1; + goto done; } args.maxnames = maxnames; @@ -2138,32 +2280,36 @@ remoteListDefinedDomains (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_DOMAINS, (xdrproc_t) xdr_remote_list_defined_domains_args, (char *) &args, (xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { errorf (conn, VIR_ERR_RPC, _("too many remote domain names: %d > %d"), ret.names.names_len, maxnames); - xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static int remoteNumOfDefinedDomains (virConnectPtr conn) { + int rv = -1; remote_num_of_defined_domains_ret ret; struct private_data *priv = conn->privateData; @@ -2171,14 +2317,18 @@ remoteNumOfDefinedDomains (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_DOMAINS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_defined_domains_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteDomainCreate (virDomainPtr domain) { + int rv = -1; remote_domain_create_args args; struct private_data *priv = domain->conn->privateData; @@ -2187,15 +2337,18 @@ remoteDomainCreate (virDomainPtr domain) if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE, (xdrproc_t) xdr_remote_domain_create_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static virDomainPtr remoteDomainDefineXML (virConnectPtr conn, const char *xml) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_define_xml_args args; remote_domain_define_xml_ret ret; struct private_data *priv = conn->privateData; @@ -2206,17 +2359,19 @@ remoteDomainDefineXML (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_DEFINE_XML, (xdrproc_t) xdr_remote_domain_define_xml_args, (char *) &args, (xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret); +done: return dom; } static int remoteDomainUndefine (virDomainPtr domain) { + int rv = -1; remote_domain_undefine_args args; struct private_data *priv = domain->conn->privateData; @@ -2225,14 +2380,18 @@ remoteDomainUndefine (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_UNDEFINE, (xdrproc_t) xdr_remote_domain_undefine_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainAttachDevice (virDomainPtr domain, const char *xml) { + int rv = -1; remote_domain_attach_device_args args; struct private_data *priv = domain->conn->privateData; @@ -2242,14 +2401,18 @@ remoteDomainAttachDevice (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_ATTACH_DEVICE, (xdrproc_t) xdr_remote_domain_attach_device_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainDetachDevice (virDomainPtr domain, const char *xml) { + int rv = -1; remote_domain_detach_device_args args; struct private_data *priv = domain->conn->privateData; @@ -2259,14 +2422,18 @@ remoteDomainDetachDevice (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DETACH_DEVICE, (xdrproc_t) xdr_remote_domain_detach_device_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainGetAutostart (virDomainPtr domain, int *autostart) { + int rv = -1; remote_domain_get_autostart_args args; remote_domain_get_autostart_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2277,15 +2444,19 @@ remoteDomainGetAutostart (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_AUTOSTART, (xdrproc_t) xdr_remote_domain_get_autostart_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_autostart_ret, (char *) &ret) == -1) - return -1; + goto done; if (autostart) *autostart = ret.autostart; - return 0; + rv = 0; + +done: + return rv; } static int remoteDomainSetAutostart (virDomainPtr domain, int autostart) { + int rv = -1; remote_domain_set_autostart_args args; struct private_data *priv = domain->conn->privateData; @@ -2295,14 +2466,18 @@ remoteDomainSetAutostart (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_AUTOSTART, (xdrproc_t) xdr_remote_domain_set_autostart_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static char * remoteDomainGetSchedulerType (virDomainPtr domain, int *nparams) { + char *rv = NULL; remote_domain_get_scheduler_type_args args; remote_domain_get_scheduler_type_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2313,21 +2488,25 @@ remoteDomainGetSchedulerType (virDomainP if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SCHEDULER_TYPE, (xdrproc_t) xdr_remote_domain_get_scheduler_type_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_scheduler_type_ret, (char *) &ret) == -1) - return NULL; + goto done; if (nparams) *nparams = ret.nparams; /* Caller frees this. */ - return ret.type; + rv = ret.type; + +done: + return rv; } static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) { + int rv = -1; remote_domain_get_scheduler_parameters_args args; remote_domain_get_scheduler_parameters_ret ret; - int i; + int i = -1; struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2337,16 +2516,15 @@ remoteDomainGetSchedulerParameters (virD if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS, (xdrproc_t) xdr_remote_domain_get_scheduler_parameters_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret) == -1) - return -1; + goto done; /* Check the length of the returned list carefully. */ if (ret.params.params_len > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX || ret.params.params_len > *nparams) { - xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); error (domain->conn, VIR_ERR_RPC, _("remoteDomainGetSchedulerParameters: " "returned number of parameters exceeds limit")); - return -1; + goto cleanup; } *nparams = ret.params.params_len; @@ -2370,22 +2548,31 @@ remoteDomainGetSchedulerParameters (virD case VIR_DOMAIN_SCHED_FIELD_BOOLEAN: params[i].value.b = ret.params.params_val[i].value.remote_sched_param_value_u.b; break; default: - xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); error (domain->conn, VIR_ERR_RPC, _("remoteDomainGetSchedulerParameters: " "unknown parameter type")); - return -1; - } - } - + goto cleanup; + } + } + + rv = 0; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; + if (rv != 0) { + for ( ; i >= 0 ; i--) + VIR_FREE(params[i].field); + } + +done: + return rv; } static int remoteDomainSetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int nparams) { + int rv = -1; remote_domain_set_scheduler_parameters_args args; int i, do_error; struct private_data *priv = domain->conn->privateData; @@ -2396,7 +2583,7 @@ remoteDomainSetSchedulerParameters (virD args.params.params_len = nparams; if (VIR_ALLOC_N(args.params.params_val, nparams) < 0) { error (domain->conn, VIR_ERR_RPC, _("out of memory allocating array")); - return -1; + goto done; } do_error = 0; @@ -2429,21 +2616,25 @@ remoteDomainSetSchedulerParameters (virD if (do_error) { xdr_free ((xdrproc_t) xdr_remote_domain_set_scheduler_parameters_args, (char *) &args); - return -1; + goto done; } if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS, (xdrproc_t) xdr_remote_domain_set_scheduler_parameters_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainBlockStats (virDomainPtr domain, const char *path, struct _virDomainBlockStats *stats) { + int rv = -1; remote_domain_block_stats_args args; remote_domain_block_stats_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2456,7 +2647,7 @@ remoteDomainBlockStats (virDomainPtr dom (xdrproc_t) xdr_remote_domain_block_stats_args, (char *) &args, (xdrproc_t) xdr_remote_domain_block_stats_ret, (char *) &ret) == -1) - return -1; + goto done; stats->rd_req = ret.rd_req; stats->rd_bytes = ret.rd_bytes; @@ -2464,13 +2655,17 @@ remoteDomainBlockStats (virDomainPtr dom stats->wr_bytes = ret.wr_bytes; stats->errs = ret.errs; - return 0; + rv = 0; + +done: + return rv; } static int remoteDomainInterfaceStats (virDomainPtr domain, const char *path, struct _virDomainInterfaceStats *stats) { + int rv = -1; remote_domain_interface_stats_args args; remote_domain_interface_stats_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2484,7 +2679,7 @@ remoteDomainInterfaceStats (virDomainPtr (char *) &args, (xdrproc_t) xdr_remote_domain_interface_stats_ret, (char *) &ret) == -1) - return -1; + goto done; stats->rx_bytes = ret.rx_bytes; stats->rx_packets = ret.rx_packets; @@ -2495,7 +2690,10 @@ remoteDomainInterfaceStats (virDomainPtr stats->tx_errs = ret.tx_errs; stats->tx_drop = ret.tx_drop; - return 0; + rv = 0; + +done: + return rv; } static int @@ -2506,6 +2704,7 @@ remoteDomainBlockPeek (virDomainPtr doma void *buffer, unsigned int flags) { + int rv = -1; remote_domain_block_peek_args args; remote_domain_block_peek_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2514,7 +2713,7 @@ remoteDomainBlockPeek (virDomainPtr doma errorf (domain->conn, VIR_ERR_RPC, _("block peek request too large for remote protocol, %zi > %d"), size, REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -2529,19 +2728,22 @@ remoteDomainBlockPeek (virDomainPtr doma (char *) &args, (xdrproc_t) xdr_remote_domain_block_peek_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.buffer.buffer_len != size) { - errorf (domain->conn, VIR_ERR_RPC, - "%s", _("returned buffer is not same size as requested")); - free (ret.buffer.buffer_val); - return -1; + errorf (domain->conn, VIR_ERR_RPC, + "%s", _("returned buffer is not same size as requested")); + goto cleanup; } memcpy (buffer, ret.buffer.buffer_val, size); + rv = 0; + +cleanup: free (ret.buffer.buffer_val); - return 0; +done: + return rv; } static int @@ -2551,6 +2753,7 @@ remoteDomainMemoryPeek (virDomainPtr dom void *buffer, unsigned int flags) { + int rv = -1; remote_domain_memory_peek_args args; remote_domain_memory_peek_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2559,7 +2762,7 @@ remoteDomainMemoryPeek (virDomainPtr dom errorf (domain->conn, VIR_ERR_RPC, _("memory peek request too large for remote protocol, %zi > %d"), size, REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -2573,19 +2776,22 @@ remoteDomainMemoryPeek (virDomainPtr dom (char *) &args, (xdrproc_t) xdr_remote_domain_memory_peek_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.buffer.buffer_len != size) { - errorf (domain->conn, VIR_ERR_RPC, - "%s", _("returned buffer is not same size as requested")); - free (ret.buffer.buffer_val); - return -1; + errorf (domain->conn, VIR_ERR_RPC, + "%s", _("returned buffer is not same size as requested")); + goto cleanup; } memcpy (buffer, ret.buffer.buffer_val, size); + rv = 0; + +cleanup: free (ret.buffer.buffer_val); - return 0; +done: + return rv; } /*----------------------------------------------------------------------*/ @@ -2639,23 +2845,24 @@ static int static int remoteNetworkClose (virConnectPtr conn) { - int ret = 0; + int rv = 0; struct private_data *priv = conn->networkPrivateData; if (priv->localUses) { priv->localUses--; if (!priv->localUses) { - ret = doRemoteClose(conn, priv); + rv = doRemoteClose(conn, priv); VIR_FREE(priv); conn->networkPrivateData = NULL; } } - return ret; + return rv; } static int remoteNumOfNetworks (virConnectPtr conn) { + int rv = -1; remote_num_of_networks_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2663,14 +2870,18 @@ remoteNumOfNetworks (virConnectPtr conn) if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_NETWORKS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_networks_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListNetworks (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_networks_args args; remote_list_networks_ret ret; @@ -2680,7 +2891,7 @@ remoteListNetworks (virConnectPtr conn, errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), maxnames, REMOTE_NETWORK_NAME_LIST_MAX); - return -1; + goto done; } args.maxnames = maxnames; @@ -2688,32 +2899,36 @@ remoteListNetworks (virConnectPtr conn, if (call (conn, priv, 0, REMOTE_PROC_LIST_NETWORKS, (xdrproc_t) xdr_remote_list_networks_args, (char *) &args, (xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), ret.names.names_len, maxnames); - xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static int remoteNumOfDefinedNetworks (virConnectPtr conn) { + int rv = -1; remote_num_of_defined_networks_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2721,15 +2936,19 @@ remoteNumOfDefinedNetworks (virConnectPt if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_NETWORKS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_defined_networks_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListDefinedNetworks (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_defined_networks_args args; remote_list_defined_networks_ret ret; @@ -2739,7 +2958,7 @@ remoteListDefinedNetworks (virConnectPtr errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), maxnames, REMOTE_NETWORK_NAME_LIST_MAX); - return -1; + goto done; } args.maxnames = maxnames; @@ -2747,34 +2966,37 @@ remoteListDefinedNetworks (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_NETWORKS, (xdrproc_t) xdr_remote_list_defined_networks_args, (char *) &args, (xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), ret.names.names_len, maxnames); - xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static virNetworkPtr remoteNetworkLookupByUUID (virConnectPtr conn, const unsigned char *uuid) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_lookup_by_uuid_args args; remote_network_lookup_by_uuid_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2785,11 +3007,12 @@ remoteNetworkLookupByUUID (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_NETWORK_LOOKUP_BY_UUID, (xdrproc_t) xdr_remote_network_lookup_by_uuid_args, (char *) &args, (xdrproc_t) xdr_remote_network_lookup_by_uuid_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_uuid_ret, (char *) &ret); +done: return net; } @@ -2797,7 +3020,7 @@ remoteNetworkLookupByName (virConnectPtr remoteNetworkLookupByName (virConnectPtr conn, const char *name) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_lookup_by_name_args args; remote_network_lookup_by_name_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2808,18 +3031,19 @@ remoteNetworkLookupByName (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_NETWORK_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_network_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_network_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_name_ret, (char *) &ret); +done: return net; } static virNetworkPtr remoteNetworkCreateXML (virConnectPtr conn, const char *xmlDesc) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_create_xml_args args; remote_network_create_xml_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2830,18 +3054,19 @@ remoteNetworkCreateXML (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_NETWORK_CREATE_XML, (xdrproc_t) xdr_remote_network_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_network_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_create_xml_ret, (char *) &ret); +done: return net; } static virNetworkPtr remoteNetworkDefineXML (virConnectPtr conn, const char *xml) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_define_xml_args args; remote_network_define_xml_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2852,17 +3077,19 @@ remoteNetworkDefineXML (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_NETWORK_DEFINE_XML, (xdrproc_t) xdr_remote_network_define_xml_args, (char *) &args, (xdrproc_t) xdr_remote_network_define_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_define_xml_ret, (char *) &ret); +done: return net; } static int remoteNetworkUndefine (virNetworkPtr network) { + int rv = -1; remote_network_undefine_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2871,14 +3098,18 @@ remoteNetworkUndefine (virNetworkPtr net if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_UNDEFINE, (xdrproc_t) xdr_remote_network_undefine_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteNetworkCreate (virNetworkPtr network) { + int rv = -1; remote_network_create_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2887,14 +3118,18 @@ remoteNetworkCreate (virNetworkPtr netwo if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_CREATE, (xdrproc_t) xdr_remote_network_create_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteNetworkDestroy (virNetworkPtr network) { + int rv = -1; remote_network_destroy_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2903,14 +3138,18 @@ remoteNetworkDestroy (virNetworkPtr netw if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DESTROY, (xdrproc_t) xdr_remote_network_destroy_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static char * remoteNetworkDumpXML (virNetworkPtr network, int flags) { + char *rv = NULL; remote_network_dump_xml_args args; remote_network_dump_xml_ret ret; struct private_data *priv = network->conn->networkPrivateData; @@ -2922,15 +3161,19 @@ remoteNetworkDumpXML (virNetworkPtr netw if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DUMP_XML, (xdrproc_t) xdr_remote_network_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_network_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static char * remoteNetworkGetBridgeName (virNetworkPtr network) { + char *rv = NULL; remote_network_get_bridge_name_args args; remote_network_get_bridge_name_ret ret; struct private_data *priv = network->conn->networkPrivateData; @@ -2941,15 +3184,19 @@ remoteNetworkGetBridgeName (virNetworkPt if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_GET_BRIDGE_NAME, (xdrproc_t) xdr_remote_network_get_bridge_name_args, (char *) &args, (xdrproc_t) xdr_remote_network_get_bridge_name_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.name; + goto done; + + /* Caller frees. */ + rv = ret.name; + +done: + return rv; } static int remoteNetworkGetAutostart (virNetworkPtr network, int *autostart) { + int rv = -1; remote_network_get_autostart_args args; remote_network_get_autostart_ret ret; struct private_data *priv = network->conn->networkPrivateData; @@ -2960,16 +3207,20 @@ remoteNetworkGetAutostart (virNetworkPtr if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_GET_AUTOSTART, (xdrproc_t) xdr_remote_network_get_autostart_args, (char *) &args, (xdrproc_t) xdr_remote_network_get_autostart_ret, (char *) &ret) == -1) - return -1; + goto done; if (autostart) *autostart = ret.autostart; - return 0; + rv = 0; + +done: + return rv; } static int remoteNetworkSetAutostart (virNetworkPtr network, int autostart) { + int rv = -1; remote_network_set_autostart_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2979,9 +3230,12 @@ remoteNetworkSetAutostart (virNetworkPtr if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_SET_AUTOSTART, (xdrproc_t) xdr_remote_network_set_autostart_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } @@ -3054,12 +3308,14 @@ remoteStorageClose (virConnectPtr conn) conn->storagePrivateData = NULL; } } + return ret; } static int remoteNumOfStoragePools (virConnectPtr conn) { + int rv = -1; remote_num_of_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3067,14 +3323,18 @@ remoteNumOfStoragePools (virConnectPtr c if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_STORAGE_POOLS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_storage_pools_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListStoragePools (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_storage_pools_args args; remote_list_storage_pools_ret ret; @@ -3082,7 +3342,7 @@ remoteListStoragePools (virConnectPtr co if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); - return -1; + goto done; } args.maxnames = maxnames; @@ -3090,30 +3350,34 @@ remoteListStoragePools (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_LIST_STORAGE_POOLS, (xdrproc_t) xdr_remote_list_storage_pools_args, (char *) &args, (xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (conn, VIR_ERR_RPC, _("too many storage pools received")); - xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static int remoteNumOfDefinedStoragePools (virConnectPtr conn) { + int rv = -1; remote_num_of_defined_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3121,15 +3385,19 @@ remoteNumOfDefinedStoragePools (virConne if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_defined_storage_pools_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListDefinedStoragePools (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_defined_storage_pools_args args; remote_list_defined_storage_pools_ret ret; @@ -3137,7 +3405,7 @@ remoteListDefinedStoragePools (virConnec if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); - return -1; + goto done; } args.maxnames = maxnames; @@ -3145,25 +3413,28 @@ remoteListDefinedStoragePools (virConnec if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS, (xdrproc_t) xdr_remote_list_defined_storage_pools_args, (char *) &args, (xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (conn, VIR_ERR_RPC, _("too many storage pools received")); - xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static char * @@ -3172,11 +3443,11 @@ remoteFindStoragePoolSources (virConnect const char *srcSpec, unsigned int flags) { + char *rv = NULL; remote_find_storage_pool_sources_args args; remote_find_storage_pool_sources_ret ret; struct private_data *priv = conn->storagePrivateData; const char *emptyString = ""; - char *retval; args.type = (char*)type; /* @@ -3197,21 +3468,22 @@ remoteFindStoragePoolSources (virConnect if (call (conn, priv, 0, REMOTE_PROC_FIND_STORAGE_POOL_SOURCES, (xdrproc_t) xdr_remote_find_storage_pool_sources_args, (char *) &args, (xdrproc_t) xdr_remote_find_storage_pool_sources_ret, (char *) &ret) == -1) - return NULL; - - retval = ret.xml; + goto done; + + rv = ret.xml; ret.xml = NULL; /* To stop xdr_free free'ing it */ xdr_free ((xdrproc_t) xdr_remote_find_storage_pool_sources_ret, (char *) &ret); - return retval; +done: + return rv; } static virStoragePoolPtr remoteStoragePoolLookupByUUID (virConnectPtr conn, const unsigned char *uuid) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_lookup_by_uuid_args args; remote_storage_pool_lookup_by_uuid_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3222,11 +3494,12 @@ remoteStoragePoolLookupByUUID (virConnec if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID, (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret); +done: return pool; } @@ -3234,7 +3507,7 @@ remoteStoragePoolLookupByName (virConnec remoteStoragePoolLookupByName (virConnectPtr conn, const char *name) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_lookup_by_name_args args; remote_storage_pool_lookup_by_name_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3245,18 +3518,19 @@ remoteStoragePoolLookupByName (virConnec if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret); +done: return pool; } static virStoragePoolPtr remoteStoragePoolLookupByVolume (virStorageVolPtr vol) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_lookup_by_volume_args args; remote_storage_pool_lookup_by_volume_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3267,11 +3541,12 @@ remoteStoragePoolLookupByVolume (virStor if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME, (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (vol->conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret); +done: return pool; } @@ -3279,7 +3554,7 @@ static virStoragePoolPtr static virStoragePoolPtr remoteStoragePoolCreateXML (virConnectPtr conn, const char *xmlDesc, unsigned int flags) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_create_xml_args args; remote_storage_pool_create_xml_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3291,18 +3566,19 @@ remoteStoragePoolCreateXML (virConnectPt if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE_XML, (xdrproc_t) xdr_remote_storage_pool_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_create_xml_ret, (char *) &ret); +done: return pool; } static virStoragePoolPtr remoteStoragePoolDefineXML (virConnectPtr conn, const char *xml, unsigned int flags) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_define_xml_args args; remote_storage_pool_define_xml_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3314,17 +3590,19 @@ remoteStoragePoolDefineXML (virConnectPt if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DEFINE_XML, (xdrproc_t) xdr_remote_storage_pool_define_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_define_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_define_xml_ret, (char *) &ret); +done: return pool; } static int remoteStoragePoolUndefine (virStoragePoolPtr pool) { + int rv = -1; remote_storage_pool_undefine_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3333,14 +3611,18 @@ remoteStoragePoolUndefine (virStoragePoo if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_UNDEFINE, (xdrproc_t) xdr_remote_storage_pool_undefine_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolCreate (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_create_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3350,15 +3632,19 @@ remoteStoragePoolCreate (virStoragePoolP if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE, (xdrproc_t) xdr_remote_storage_pool_create_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolBuild (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_build_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3368,14 +3654,18 @@ remoteStoragePoolBuild (virStoragePoolPt if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_BUILD, (xdrproc_t) xdr_remote_storage_pool_build_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolDestroy (virStoragePoolPtr pool) { + int rv = -1; remote_storage_pool_destroy_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3384,15 +3674,19 @@ remoteStoragePoolDestroy (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DESTROY, (xdrproc_t) xdr_remote_storage_pool_destroy_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolDelete (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_delete_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3402,15 +3696,19 @@ remoteStoragePoolDelete (virStoragePoolP if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DELETE, (xdrproc_t) xdr_remote_storage_pool_delete_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolRefresh (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_refresh_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3420,14 +3718,18 @@ remoteStoragePoolRefresh (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_REFRESH, (xdrproc_t) xdr_remote_storage_pool_refresh_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolGetInfo (virStoragePoolPtr pool, virStoragePoolInfoPtr info) { + int rv = -1; remote_storage_pool_get_info_args args; remote_storage_pool_get_info_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3438,20 +3740,24 @@ remoteStoragePoolGetInfo (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_INFO, (xdrproc_t) xdr_remote_storage_pool_get_info_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; info->state = ret.state; info->capacity = ret.capacity; info->allocation = ret.allocation; info->available = ret.available; - return 0; + rv = 0; + +done: + return rv; } static char * remoteStoragePoolDumpXML (virStoragePoolPtr pool, unsigned int flags) { + char *rv = NULL; remote_storage_pool_dump_xml_args args; remote_storage_pool_dump_xml_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3463,15 +3769,19 @@ remoteStoragePoolDumpXML (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DUMP_XML, (xdrproc_t) xdr_remote_storage_pool_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static int remoteStoragePoolGetAutostart (virStoragePoolPtr pool, int *autostart) { + int rv = -1; remote_storage_pool_get_autostart_args args; remote_storage_pool_get_autostart_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3482,16 +3792,20 @@ remoteStoragePoolGetAutostart (virStorag if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART, (xdrproc_t) xdr_remote_storage_pool_get_autostart_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_get_autostart_ret, (char *) &ret) == -1) - return -1; + goto done; if (autostart) *autostart = ret.autostart; - return 0; + rv = 0; + +done: + return rv; } static int remoteStoragePoolSetAutostart (virStoragePoolPtr pool, int autostart) { + int rv = -1; remote_storage_pool_set_autostart_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3501,15 +3815,19 @@ remoteStoragePoolSetAutostart (virStorag if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART, (xdrproc_t) xdr_remote_storage_pool_set_autostart_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolNumOfVolumes (virStoragePoolPtr pool) { + int rv = -1; remote_storage_pool_num_of_volumes_args args; remote_storage_pool_num_of_volumes_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3520,14 +3838,18 @@ remoteStoragePoolNumOfVolumes (virStorag if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES, (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteStoragePoolListVolumes (virStoragePoolPtr pool, char **const names, int maxnames) { + int rv = -1; int i; remote_storage_pool_list_volumes_args args; remote_storage_pool_list_volumes_ret ret; @@ -3535,7 +3857,7 @@ remoteStoragePoolListVolumes (virStorage if (maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { error (pool->conn, VIR_ERR_RPC, _("too many storage volumes requested")); - return -1; + goto done; } args.maxnames = maxnames; make_nonnull_storage_pool(&args.pool, pool); @@ -3544,25 +3866,28 @@ remoteStoragePoolListVolumes (virStorage if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES, (xdrproc_t) xdr_remote_storage_pool_list_volumes_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (pool->conn, VIR_ERR_RPC, _("too many storage volumes received")); - xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } @@ -3571,7 +3896,7 @@ remoteStorageVolLookupByName (virStorage remoteStorageVolLookupByName (virStoragePoolPtr pool, const char *name) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_lookup_by_name_args args; remote_storage_vol_lookup_by_name_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3583,11 +3908,12 @@ remoteStorageVolLookupByName (virStorage if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (pool->conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret); +done: return vol; } @@ -3595,7 +3921,7 @@ remoteStorageVolLookupByKey (virConnectP remoteStorageVolLookupByKey (virConnectPtr conn, const char *key) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_lookup_by_key_args args; remote_storage_vol_lookup_by_key_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3606,11 +3932,12 @@ remoteStorageVolLookupByKey (virConnectP if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY, (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret); +done: return vol; } @@ -3618,7 +3945,7 @@ remoteStorageVolLookupByPath (virConnect remoteStorageVolLookupByPath (virConnectPtr conn, const char *path) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_lookup_by_path_args args; remote_storage_vol_lookup_by_path_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3629,11 +3956,12 @@ remoteStorageVolLookupByPath (virConnect if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH, (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret); +done: return vol; } @@ -3641,7 +3969,7 @@ remoteStorageVolCreateXML (virStoragePoo remoteStorageVolCreateXML (virStoragePoolPtr pool, const char *xmlDesc, unsigned int flags) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_create_xml_args args; remote_storage_vol_create_xml_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3654,11 +3982,12 @@ remoteStorageVolCreateXML (virStoragePoo if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CREATE_XML, (xdrproc_t) xdr_remote_storage_vol_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (pool->conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_ret, (char *) &ret); +done: return vol; } @@ -3666,6 +3995,7 @@ remoteStorageVolDelete (virStorageVolPtr remoteStorageVolDelete (virStorageVolPtr vol, unsigned int flags) { + int rv = -1; remote_storage_vol_delete_args args; struct private_data *priv = vol->conn->storagePrivateData; @@ -3675,14 +4005,18 @@ remoteStorageVolDelete (virStorageVolPtr if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DELETE, (xdrproc_t) xdr_remote_storage_vol_delete_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStorageVolGetInfo (virStorageVolPtr vol, virStorageVolInfoPtr info) { + int rv = -1; remote_storage_vol_get_info_args args; remote_storage_vol_get_info_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3693,19 +4027,23 @@ remoteStorageVolGetInfo (virStorageVolPt if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_INFO, (xdrproc_t) xdr_remote_storage_vol_get_info_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; info->type = ret.type; info->capacity = ret.capacity; info->allocation = ret.allocation; - return 0; + rv = 0; + +done: + return rv; } static char * remoteStorageVolDumpXML (virStorageVolPtr vol, unsigned int flags) { + char *rv = NULL; remote_storage_vol_dump_xml_args args; remote_storage_vol_dump_xml_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3717,15 +4055,19 @@ remoteStorageVolDumpXML (virStorageVolPt if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DUMP_XML, (xdrproc_t) xdr_remote_storage_vol_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static char * remoteStorageVolGetPath (virStorageVolPtr vol) { + char *rv = NULL; remote_storage_vol_get_path_args args; remote_storage_vol_get_path_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3736,10 +4078,13 @@ remoteStorageVolGetPath (virStorageVolPt if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_PATH, (xdrproc_t) xdr_remote_storage_vol_get_path_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_get_path_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.name; + goto done; + + /* Caller frees. */ + rv = ret.name; + +done: + return rv; } @@ -3785,6 +4130,7 @@ static int remoteNodeNumOfDevices(virCon const char *cap, unsigned int flags) { + int rv = -1; remote_node_num_of_devices_args args; remote_node_num_of_devices_ret ret; struct private_data *priv = conn->devMonPrivateData; @@ -3796,9 +4142,12 @@ static int remoteNodeNumOfDevices(virCon if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES, (xdrproc_t) xdr_remote_node_num_of_devices_args, (char *) &args, (xdrproc_t) xdr_remote_node_num_of_devices_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } @@ -3808,6 +4157,7 @@ static int remoteNodeListDevices(virConn int maxnames, unsigned int flags) { + int rv = -1; int i; remote_node_list_devices_args args; remote_node_list_devices_ret ret; @@ -3815,7 +4165,7 @@ static int remoteNodeListDevices(virConn if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many device names requested")); - return -1; + goto done; } args.cap = cap ? (char **)&cap : NULL; args.maxnames = maxnames; @@ -3825,25 +4175,28 @@ static int remoteNodeListDevices(virConn if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES, (xdrproc_t) xdr_remote_node_list_devices_args, (char *) &args, (xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (conn, VIR_ERR_RPC, _("too many device names received")); - xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } @@ -3852,7 +4205,7 @@ static virNodeDevicePtr remoteNodeDevice { remote_node_device_lookup_by_name_args args; remote_node_device_lookup_by_name_ret ret; - virNodeDevicePtr dev; + virNodeDevicePtr dev = NULL; struct private_data *priv = conn->devMonPrivateData; args.name = (char *)name; @@ -3861,18 +4214,20 @@ static virNodeDevicePtr remoteNodeDevice if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_node_device_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; dev = get_nonnull_node_device(conn, ret.dev); xdr_free ((xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret); +done: return dev; } static char *remoteNodeDeviceDumpXML(virNodeDevicePtr dev, unsigned int flags) { + char *rv = NULL; remote_node_device_dump_xml_args args; remote_node_device_dump_xml_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; @@ -3884,14 +4239,18 @@ static char *remoteNodeDeviceDumpXML(vir if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DUMP_XML, (xdrproc_t) xdr_remote_node_device_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static char *remoteNodeDeviceGetParent(virNodeDevicePtr dev) { + char *rv = NULL; remote_node_device_get_parent_args args; remote_node_device_get_parent_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; @@ -3902,14 +4261,18 @@ static char *remoteNodeDeviceGetParent(v if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_GET_PARENT, (xdrproc_t) xdr_remote_node_device_get_parent_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_get_parent_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.parent ? *ret.parent : NULL; + goto done; + + /* Caller frees. */ + rv = ret.parent ? *ret.parent : NULL; + +done: + return rv; } static int remoteNodeDeviceNumOfCaps(virNodeDevicePtr dev) { + int rv = -1; remote_node_device_num_of_caps_args args; remote_node_device_num_of_caps_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; @@ -3920,15 +4283,19 @@ static int remoteNodeDeviceNumOfCaps(vir if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS, (xdrproc_t) xdr_remote_node_device_num_of_caps_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_num_of_caps_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteNodeDeviceListCaps(virNodeDevicePtr dev, char **const names, int maxnames) { + int rv = -1; int i; remote_node_device_list_caps_args args; remote_node_device_list_caps_ret ret; @@ -3936,7 +4303,7 @@ static int remoteNodeDeviceListCaps(virN if (maxnames > REMOTE_NODE_DEVICE_CAPS_LIST_MAX) { error (dev->conn, VIR_ERR_RPC, _("too many capability names requested")); - return -1; + goto done; } args.maxnames = maxnames; args.name = dev->name; @@ -3945,25 +4312,28 @@ static int remoteNodeDeviceListCaps(virN if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LIST_CAPS, (xdrproc_t) xdr_remote_node_device_list_caps_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (dev->conn, VIR_ERR_RPC, _("too many capability names received")); - xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } @@ -4620,6 +4990,7 @@ remoteAuthSASL (virConnectPtr conn, stru remoteAuthFreeCredentials(cred, ncred); if (ret != 0 && saslconn) sasl_dispose(&saslconn); + return ret; } #endif /* HAVE_SASL */ @@ -4684,16 +5055,17 @@ static int remoteDomainEventRegister (vi void *opaque, virFreeCallback freecb) { + int rv = -1; struct private_data *priv = conn->privateData; if (priv->eventFlushTimer < 0) { error (conn, VIR_ERR_NO_SUPPORT, _("no event support")); - return -1; + goto done; } if (virDomainEventCallbackListAdd(conn, priv->callbackList, callback, opaque, freecb) < 0) { error (conn, VIR_ERR_RPC, _("adding cb to list")); - return -1; + goto done; } if ( priv->callbackList->count == 1 ) { @@ -4701,21 +5073,25 @@ static int remoteDomainEventRegister (vi if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_REGISTER, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - } - - return 0; + goto done; + } + + rv = 0; + +done: + return rv; } static int remoteDomainEventDeregister (virConnectPtr conn, virConnectDomainEventCallback callback) { struct private_data *priv = conn->privateData; + int rv = -1; if (virDomainEventCallbackListRemove(conn, priv->callbackList, - callback) < 0) { + callback) < 0) { error (conn, VIR_ERR_RPC, _("removing cb fron list")); - return -1; + goto done; } if ( priv->callbackList->count == 0 ) { @@ -4723,10 +5099,13 @@ static int remoteDomainEventDeregister ( if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - } - - return 0; + goto done; + } + + rv = 0; + +done: + return rv; } /*----------------------------------------------------------------------*/ @@ -5522,22 +5901,22 @@ remoteDomainEventFired(int watch, DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); virEventRemoveHandle(watch); - return; + goto done; } if (fd != priv->sock) { virEventRemoveHandle(watch); - return; + goto done; } /* Read and deserialise length word. */ if (really_read (conn, priv, 0, buffer2, sizeof buffer2) == -1) - return; + goto done; xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); if (!xdr_int (&xdr, &len)) { error (conn, VIR_ERR_RPC, _("xdr_int (length word, reply)")); - return; + goto done; } xdr_destroy (&xdr); @@ -5546,20 +5925,20 @@ remoteDomainEventFired(int watch, if (len < 0 || len > REMOTE_MESSAGE_MAX) { error (conn, VIR_ERR_RPC, _("packet received from server too large")); - return; + goto done; } /* Read reply header and what follows (either a ret or an error). */ if (really_read (conn, priv, 0, buffer, len) == -1) { error (conn, VIR_ERR_RPC, _("error reading buffer from memory")); - return; + goto done; } /* Deserialise reply header. */ xdrmem_create (&xdr, buffer, len, XDR_DECODE); if (!xdr_remote_message_header (&xdr, &hdr)) { error (conn, VIR_ERR_RPC, _("invalid header in event firing")); - return; + goto done; } if (hdr.proc == REMOTE_PROC_DOMAIN_EVENT && @@ -5570,6 +5949,9 @@ remoteDomainEventFired(int watch, DEBUG0("invalid proc in event firing"); error (conn, VIR_ERR_RPC, _("invalid proc in event firing")); } + +done: + return; } void -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++-------------------
Hi Dan, I got about halfway through it and spotted something odd:
diff --git a/src/remote_internal.c b/src/remote_internal.c ... static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) ... +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; + if (rv != 0) { + for ( ; i >= 0 ; i--) + VIR_FREE(params[i].field); + } + +done: + return rv; }
Freeing the .field member looks bogus. That member is declared like this: struct _virSchedParameter { char field[VIR_DOMAIN_SCHED_FIELD_LENGTH]; /* parameter name */ merge problem, or vestige of older design, I guess.

Jim Meyering <jim@meyering.net> wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++-------------------
Hi Dan,
I got about halfway through it and spotted something odd:
Other than that, the rest of that patch looks fine. But oh-so-boring to read (and write, I'm sure). At about 25% of the way through I even considered writing something to parse the 2 or 3 main patterns and automate the process. But there would have been too many little irregularities...

On Wed, Jan 14, 2009 at 08:39:02PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++-------------------
Hi Dan,
I got about halfway through it and spotted something odd:
diff --git a/src/remote_internal.c b/src/remote_internal.c ... static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) ... +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; + if (rv != 0) { + for ( ; i >= 0 ; i--) + VIR_FREE(params[i].field); + } + +done: + return rv; }
Freeing the .field member looks bogus.
Hmmm, not sure why I added that - I must have been thinking it was a malloc'd char * at the time. Clearly bogus Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Wed, Jan 14, 2009 at 10:41:50PM +0000, Daniel P. Berrange wrote:
On Wed, Jan 14, 2009 at 08:39:02PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++-------------------
Hi Dan,
I got about halfway through it and spotted something odd:
diff --git a/src/remote_internal.c b/src/remote_internal.c ... static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) ... +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; + if (rv != 0) { + for ( ; i >= 0 ; i--) + VIR_FREE(params[i].field); + } + +done: + return rv; }
Freeing the .field member looks bogus.
Hmmm, not sure why I added that - I must have been thinking it was a malloc'd char * at the time. Clearly bogus
Updated to remove that bogus chunk. Daniel diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -1253,12 +1253,16 @@ remoteClose (virConnectPtr conn) static int remoteSupportsFeature (virConnectPtr conn, int feature) { + int rv = -1; remote_supports_feature_args args; remote_supports_feature_ret ret; struct private_data *priv = conn->privateData; /* VIR_DRV_FEATURE_REMOTE* features are handled directly. */ - if (feature == VIR_DRV_FEATURE_REMOTE) return 1; + if (feature == VIR_DRV_FEATURE_REMOTE) { + rv = 1; + goto done; + } args.feature = feature; @@ -1266,9 +1270,12 @@ remoteSupportsFeature (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_SUPPORTS_FEATURE, (xdrproc_t) xdr_remote_supports_feature_args, (char *) &args, (xdrproc_t) xdr_remote_supports_feature_ret, (char *) &ret) == -1) - return -1; - - return ret.supported; + goto done; + + rv = ret.supported; + +done: + return rv; } /* Unfortunately this function is defined to return a static string. @@ -1282,25 +1289,33 @@ remoteSupportsFeature (virConnectPtr con static const char * remoteType (virConnectPtr conn) { + char *rv = NULL; remote_get_type_ret ret; struct private_data *priv = conn->privateData; /* Cached? */ - if (priv->type) return priv->type; + if (priv->type) { + rv = priv->type; + goto done; + } memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_TYPE, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_type_ret, (char *) &ret) == -1) - return NULL; + goto done; /* Stash. */ - return priv->type = ret.type; + rv = priv->type = ret.type; + +done: + return rv; } static int remoteGetVersion (virConnectPtr conn, unsigned long *hvVer) { + int rv = -1; remote_get_version_ret ret; struct private_data *priv = conn->privateData; @@ -1308,15 +1323,19 @@ remoteGetVersion (virConnectPtr conn, un if (call (conn, priv, 0, REMOTE_PROC_GET_VERSION, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_version_ret, (char *) &ret) == -1) - return -1; + goto done; if (hvVer) *hvVer = ret.hv_ver; - return 0; + rv = 0; + +done: + return rv; } static char * remoteGetHostname (virConnectPtr conn) { + char *rv = NULL; remote_get_hostname_ret ret; struct private_data *priv = conn->privateData; @@ -1324,15 +1343,19 @@ remoteGetHostname (virConnectPtr conn) if (call (conn, priv, 0, REMOTE_PROC_GET_HOSTNAME, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_hostname_ret, (char *) &ret) == -1) - return NULL; + goto done; /* Caller frees this. */ - return ret.hostname; + rv = ret.hostname; + +done: + return rv; } static int remoteGetMaxVcpus (virConnectPtr conn, const char *type) { + int rv = -1; remote_get_max_vcpus_args args; remote_get_max_vcpus_ret ret; struct private_data *priv = conn->privateData; @@ -1342,14 +1365,18 @@ remoteGetMaxVcpus (virConnectPtr conn, c if (call (conn, priv, 0, REMOTE_PROC_GET_MAX_VCPUS, (xdrproc_t) xdr_remote_get_max_vcpus_args, (char *) &args, (xdrproc_t) xdr_remote_get_max_vcpus_ret, (char *) &ret) == -1) - return -1; - - return ret.max_vcpus; + goto done; + + rv = ret.max_vcpus; + +done: + return rv; } static int remoteNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info) { + int rv = -1; remote_node_get_info_ret ret; struct private_data *priv = conn->privateData; @@ -1357,7 +1384,7 @@ remoteNodeGetInfo (virConnectPtr conn, v if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_INFO, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_node_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; strncpy (info->model, ret.model, 32); info->model[31] = '\0'; @@ -1368,12 +1395,16 @@ remoteNodeGetInfo (virConnectPtr conn, v info->sockets = ret.sockets; info->cores = ret.cores; info->threads = ret.threads; - return 0; + rv = 0; + +done: + return rv; } static char * remoteGetCapabilities (virConnectPtr conn) { + char *rv = NULL; remote_get_capabilities_ret ret; struct private_data *priv = conn->privateData; @@ -1381,10 +1412,13 @@ remoteGetCapabilities (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_GET_CAPABILITIES, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_get_capabilities_ret, (char *)&ret) == -1) - return NULL; + goto done; /* Caller frees this. */ - return ret.capabilities; + rv = ret.capabilities; + +done: + return rv; } static int @@ -1393,6 +1427,7 @@ remoteNodeGetCellsFreeMemory(virConnectP int startCell, int maxCells) { + int rv = -1; remote_node_get_cells_free_memory_args args; remote_node_get_cells_free_memory_ret ret; int i; @@ -1403,7 +1438,7 @@ remoteNodeGetCellsFreeMemory(virConnectP _("too many NUMA cells: %d > %d"), maxCells, REMOTE_NODE_MAX_CELLS); - return -1; + goto done; } args.startCell = startCell; @@ -1413,19 +1448,23 @@ remoteNodeGetCellsFreeMemory(virConnectP if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_CELLS_FREE_MEMORY, (xdrproc_t) xdr_remote_node_get_cells_free_memory_args, (char *)&args, (xdrproc_t) xdr_remote_node_get_cells_free_memory_ret, (char *)&ret) == -1) - return -1; + goto done; for (i = 0 ; i < ret.freeMems.freeMems_len ; i++) freeMems[i] = ret.freeMems.freeMems_val[i]; xdr_free((xdrproc_t) xdr_remote_node_get_cells_free_memory_ret, (char *) &ret); - return ret.freeMems.freeMems_len; + rv = ret.freeMems.freeMems_len; + +done: + return rv; } static unsigned long long remoteNodeGetFreeMemory (virConnectPtr conn) { + unsigned long long rv = 0; /* 0 is error value this special function*/ remote_node_get_free_memory_ret ret; struct private_data *priv = conn->privateData; @@ -1433,15 +1472,19 @@ remoteNodeGetFreeMemory (virConnectPtr c if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_FREE_MEMORY, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_remote_node_get_free_memory_ret, (char *)&ret) == -1) - return 0; - - return ret.freeMem; + goto done; + + rv = ret.freeMem; + +done: + return rv; } static int remoteListDomains (virConnectPtr conn, int *ids, int maxids) { + int rv = -1; int i; remote_list_domains_args args; remote_list_domains_ret ret; @@ -1451,7 +1494,7 @@ remoteListDomains (virConnectPtr conn, i errorf (conn, VIR_ERR_RPC, _("too many remote domain IDs: %d > %d"), maxids, REMOTE_DOMAIN_ID_LIST_MAX); - return -1; + goto done; } args.maxids = maxids; @@ -1459,27 +1502,31 @@ remoteListDomains (virConnectPtr conn, i if (call (conn, priv, 0, REMOTE_PROC_LIST_DOMAINS, (xdrproc_t) xdr_remote_list_domains_args, (char *) &args, (xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.ids.ids_len > maxids) { errorf (conn, VIR_ERR_RPC, _("too many remote domain IDs: %d > %d"), ret.ids.ids_len, maxids); - xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret); - return -1; + goto cleanup; } for (i = 0; i < ret.ids.ids_len; ++i) ids[i] = ret.ids.ids_val[i]; + rv = ret.ids.ids_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret); - return ret.ids.ids_len; +done: + return rv; } static int remoteNumOfDomains (virConnectPtr conn) { + int rv = -1; remote_num_of_domains_ret ret; struct private_data *priv = conn->privateData; @@ -1487,9 +1534,12 @@ remoteNumOfDomains (virConnectPtr conn) if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DOMAINS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_domains_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static virDomainPtr @@ -1497,7 +1547,7 @@ remoteDomainCreateXML (virConnectPtr con const char *xmlDesc, unsigned int flags) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_create_xml_args args; remote_domain_create_xml_ret ret; struct private_data *priv = conn->privateData; @@ -1509,18 +1559,19 @@ remoteDomainCreateXML (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE_XML, (xdrproc_t) xdr_remote_domain_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_domain_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_create_xml_ret, (char *) &ret); +done: return dom; } static virDomainPtr remoteDomainLookupByID (virConnectPtr conn, int id) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_lookup_by_id_args args; remote_domain_lookup_by_id_ret ret; struct private_data *priv = conn->privateData; @@ -1531,18 +1582,19 @@ remoteDomainLookupByID (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_ID, (xdrproc_t) xdr_remote_domain_lookup_by_id_args, (char *) &args, (xdrproc_t) xdr_remote_domain_lookup_by_id_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_id_ret, (char *) &ret); +done: return dom; } static virDomainPtr remoteDomainLookupByUUID (virConnectPtr conn, const unsigned char *uuid) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_lookup_by_uuid_args args; remote_domain_lookup_by_uuid_ret ret; struct private_data *priv = conn->privateData; @@ -1553,17 +1605,19 @@ remoteDomainLookupByUUID (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_UUID, (xdrproc_t) xdr_remote_domain_lookup_by_uuid_args, (char *) &args, (xdrproc_t) xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret); + +done: return dom; } static virDomainPtr remoteDomainLookupByName (virConnectPtr conn, const char *name) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_lookup_by_name_args args; remote_domain_lookup_by_name_ret ret; struct private_data *priv = conn->privateData; @@ -1574,17 +1628,19 @@ remoteDomainLookupByName (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_domain_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_domain_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_name_ret, (char *) &ret); +done: return dom; } static int remoteDomainSuspend (virDomainPtr domain) { + int rv = -1; remote_domain_suspend_args args; struct private_data *priv = domain->conn->privateData; @@ -1593,14 +1649,18 @@ remoteDomainSuspend (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SUSPEND, (xdrproc_t) xdr_remote_domain_suspend_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainResume (virDomainPtr domain) { + int rv = -1; remote_domain_resume_args args; struct private_data *priv = domain->conn->privateData; @@ -1609,14 +1669,18 @@ remoteDomainResume (virDomainPtr domain) if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_RESUME, (xdrproc_t) xdr_remote_domain_resume_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainShutdown (virDomainPtr domain) { + int rv = -1; remote_domain_shutdown_args args; struct private_data *priv = domain->conn->privateData; @@ -1625,14 +1689,18 @@ remoteDomainShutdown (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SHUTDOWN, (xdrproc_t) xdr_remote_domain_shutdown_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainReboot (virDomainPtr domain, unsigned int flags) { + int rv = -1; remote_domain_reboot_args args; struct private_data *priv = domain->conn->privateData; @@ -1642,14 +1710,18 @@ remoteDomainReboot (virDomainPtr domain, if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_REBOOT, (xdrproc_t) xdr_remote_domain_reboot_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainDestroy (virDomainPtr domain) { + int rv = -1; remote_domain_destroy_args args; struct private_data *priv = domain->conn->privateData; @@ -1658,14 +1730,18 @@ remoteDomainDestroy (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DESTROY, (xdrproc_t) xdr_remote_domain_destroy_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static char * remoteDomainGetOSType (virDomainPtr domain) { + char *rv = NULL; remote_domain_get_os_type_args args; remote_domain_get_os_type_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1676,15 +1752,19 @@ remoteDomainGetOSType (virDomainPtr doma if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_OS_TYPE, (xdrproc_t) xdr_remote_domain_get_os_type_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_os_type_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.type; + goto done; + + /* Caller frees. */ + rv = ret.type; + +done: + return rv; } static unsigned long remoteDomainGetMaxMemory (virDomainPtr domain) { + unsigned long rv = 0; remote_domain_get_max_memory_args args; remote_domain_get_max_memory_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1695,14 +1775,18 @@ remoteDomainGetMaxMemory (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_MAX_MEMORY, (xdrproc_t) xdr_remote_domain_get_max_memory_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_max_memory_ret, (char *) &ret) == -1) - return 0; - - return ret.memory; + goto done; + + rv = ret.memory; + +done: + return rv; } static int remoteDomainSetMaxMemory (virDomainPtr domain, unsigned long memory) { + int rv = -1; remote_domain_set_max_memory_args args; struct private_data *priv = domain->conn->privateData; @@ -1712,14 +1796,18 @@ remoteDomainSetMaxMemory (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_MAX_MEMORY, (xdrproc_t) xdr_remote_domain_set_max_memory_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainSetMemory (virDomainPtr domain, unsigned long memory) { + int rv = -1; remote_domain_set_memory_args args; struct private_data *priv = domain->conn->privateData; @@ -1729,14 +1817,18 @@ remoteDomainSetMemory (virDomainPtr doma if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_MEMORY, (xdrproc_t) xdr_remote_domain_set_memory_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainGetInfo (virDomainPtr domain, virDomainInfoPtr info) { + int rv = -1; remote_domain_get_info_args args; remote_domain_get_info_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1747,7 +1839,7 @@ remoteDomainGetInfo (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_INFO, (xdrproc_t) xdr_remote_domain_get_info_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; info->state = ret.state; info->maxMem = ret.max_mem; @@ -1755,12 +1847,16 @@ remoteDomainGetInfo (virDomainPtr domain info->nrVirtCpu = ret.nr_virt_cpu; info->cpuTime = ret.cpu_time; - return 0; + rv = 0; + +done: + return rv; } static int remoteDomainSave (virDomainPtr domain, const char *to) { + int rv = -1; remote_domain_save_args args; struct private_data *priv = domain->conn->privateData; @@ -1770,14 +1866,18 @@ remoteDomainSave (virDomainPtr domain, c if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SAVE, (xdrproc_t) xdr_remote_domain_save_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainRestore (virConnectPtr conn, const char *from) { + int rv = -1; remote_domain_restore_args args; struct private_data *priv = conn->privateData; @@ -1786,14 +1886,18 @@ remoteDomainRestore (virConnectPtr conn, if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_RESTORE, (xdrproc_t) xdr_remote_domain_restore_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainCoreDump (virDomainPtr domain, const char *to, int flags) { + int rv = -1; remote_domain_core_dump_args args; struct private_data *priv = domain->conn->privateData; @@ -1804,14 +1908,18 @@ remoteDomainCoreDump (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CORE_DUMP, (xdrproc_t) xdr_remote_domain_core_dump_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus) { + int rv = -1; remote_domain_set_vcpus_args args; struct private_data *priv = domain->conn->privateData; @@ -1821,9 +1929,12 @@ remoteDomainSetVcpus (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_VCPUS, (xdrproc_t) xdr_remote_domain_set_vcpus_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int @@ -1832,6 +1943,7 @@ remoteDomainPinVcpu (virDomainPtr domain unsigned char *cpumap, int maplen) { + int rv = -1; remote_domain_pin_vcpu_args args; struct private_data *priv = domain->conn->privateData; @@ -1839,7 +1951,7 @@ remoteDomainPinVcpu (virDomainPtr domain errorf (domain->conn, VIR_ERR_RPC, _("map length greater than maximum: %d > %d"), maplen, REMOTE_CPUMAP_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -1850,9 +1962,12 @@ remoteDomainPinVcpu (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_PIN_VCPU, (xdrproc_t) xdr_remote_domain_pin_vcpu_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int @@ -1862,6 +1977,7 @@ remoteDomainGetVcpus (virDomainPtr domai unsigned char *cpumaps, int maplen) { + int rv = -1; int i; remote_domain_get_vcpus_args args; remote_domain_get_vcpus_ret ret; @@ -1871,13 +1987,13 @@ remoteDomainGetVcpus (virDomainPtr domai errorf (domain->conn, VIR_ERR_RPC, _("vCPU count exceeds maximum: %d > %d"), maxinfo, REMOTE_VCPUINFO_MAX); - return -1; + goto done; } if (maxinfo * maplen > REMOTE_CPUMAPS_MAX) { errorf (domain->conn, VIR_ERR_RPC, _("vCPU map buffer length exceeds maximum: %d > %d"), maxinfo * maplen, REMOTE_CPUMAPS_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -1888,21 +2004,19 @@ remoteDomainGetVcpus (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_VCPUS, (xdrproc_t) xdr_remote_domain_get_vcpus_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.info.info_len > maxinfo) { errorf (domain->conn, VIR_ERR_RPC, _("host reports too many vCPUs: %d > %d"), ret.info.info_len, maxinfo); - xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); - return -1; + goto cleanup; } if (ret.cpumaps.cpumaps_len > maxinfo * maplen) { errorf (domain->conn, VIR_ERR_RPC, _("host reports map buffer length exceeds maximum: %d > %d"), ret.cpumaps.cpumaps_len, maxinfo * maplen); - xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); - return -1; + goto cleanup; } memset (info, 0, sizeof (virVcpuInfo) * maxinfo); @@ -1918,13 +2032,19 @@ remoteDomainGetVcpus (virDomainPtr domai for (i = 0; i < ret.cpumaps.cpumaps_len; ++i) cpumaps[i] = ret.cpumaps.cpumaps_val[i]; + rv = ret.info.info_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); - return ret.info.info_len; + +done: + return rv; } static int remoteDomainGetMaxVcpus (virDomainPtr domain) { + int rv = -1; remote_domain_get_max_vcpus_args args; remote_domain_get_max_vcpus_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1935,14 +2055,18 @@ remoteDomainGetMaxVcpus (virDomainPtr do if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_MAX_VCPUS, (xdrproc_t) xdr_remote_domain_get_max_vcpus_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_max_vcpus_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static char * remoteDomainDumpXML (virDomainPtr domain, int flags) { + char *rv = NULL; remote_domain_dump_xml_args args; remote_domain_dump_xml_ret ret; struct private_data *priv = domain->conn->privateData; @@ -1954,10 +2078,13 @@ remoteDomainDumpXML (virDomainPtr domain if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DUMP_XML, (xdrproc_t) xdr_remote_domain_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_domain_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static int @@ -1967,6 +2094,7 @@ remoteDomainMigratePrepare (virConnectPt unsigned long flags, const char *dname, unsigned long resource) { + int rv = -1; remote_domain_migrate_prepare_args args; remote_domain_migrate_prepare_ret ret; struct private_data *priv = dconn->privateData; @@ -1980,7 +2108,7 @@ remoteDomainMigratePrepare (virConnectPt if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE, (xdrproc_t) xdr_remote_domain_migrate_prepare_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_prepare_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.cookie.cookie_len > 0) { *cookie = ret.cookie.cookie_val; /* Caller frees. */ @@ -1989,7 +2117,10 @@ remoteDomainMigratePrepare (virConnectPt if (ret.uri_out) *uri_out = *ret.uri_out; /* Caller frees. */ - return 0; + rv = 0; + +done: + return rv; } static int @@ -2001,6 +2132,7 @@ remoteDomainMigratePerform (virDomainPtr const char *dname, unsigned long resource) { + int rv = -1; remote_domain_migrate_perform_args args; struct private_data *priv = domain->conn->privateData; @@ -2015,9 +2147,12 @@ remoteDomainMigratePerform (virDomainPtr if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PERFORM, (xdrproc_t) xdr_remote_domain_migrate_perform_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static virDomainPtr @@ -2028,7 +2163,7 @@ remoteDomainMigrateFinish (virConnectPtr const char *uri, unsigned long flags) { - virDomainPtr ddom; + virDomainPtr ddom = NULL; remote_domain_migrate_finish_args args; remote_domain_migrate_finish_ret ret; struct private_data *priv = dconn->privateData; @@ -2043,11 +2178,12 @@ remoteDomainMigrateFinish (virConnectPtr if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH, (xdrproc_t) xdr_remote_domain_migrate_finish_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_finish_ret, (char *) &ret) == -1) - return NULL; + goto done; ddom = get_nonnull_domain (dconn, ret.ddom); xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish_ret, (char *) &ret); +done: return ddom; } @@ -2059,6 +2195,7 @@ remoteDomainMigratePrepare2 (virConnectP unsigned long resource, const char *dom_xml) { + int rv = -1; remote_domain_migrate_prepare2_args args; remote_domain_migrate_prepare2_ret ret; struct private_data *priv = dconn->privateData; @@ -2073,7 +2210,7 @@ remoteDomainMigratePrepare2 (virConnectP if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2, (xdrproc_t) xdr_remote_domain_migrate_prepare2_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_prepare2_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.cookie.cookie_len > 0) { *cookie = ret.cookie.cookie_val; /* Caller frees. */ @@ -2082,7 +2219,10 @@ remoteDomainMigratePrepare2 (virConnectP if (ret.uri_out) *uri_out = *ret.uri_out; /* Caller frees. */ - return 0; + rv = 0; + +done: + return rv; } static virDomainPtr @@ -2094,7 +2234,7 @@ remoteDomainMigrateFinish2 (virConnectPt unsigned long flags, int retcode) { - virDomainPtr ddom; + virDomainPtr ddom = NULL; remote_domain_migrate_finish2_args args; remote_domain_migrate_finish2_ret ret; struct private_data *priv = dconn->privateData; @@ -2110,17 +2250,19 @@ remoteDomainMigrateFinish2 (virConnectPt if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2, (xdrproc_t) xdr_remote_domain_migrate_finish2_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_finish2_ret, (char *) &ret) == -1) - return NULL; + goto done; ddom = get_nonnull_domain (dconn, ret.ddom); xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish2_ret, (char *) &ret); +done: return ddom; } static int remoteListDefinedDomains (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_defined_domains_args args; remote_list_defined_domains_ret ret; @@ -2130,7 +2272,7 @@ remoteListDefinedDomains (virConnectPtr errorf (conn, VIR_ERR_RPC, _("too many remote domain names: %d > %d"), maxnames, REMOTE_DOMAIN_NAME_LIST_MAX); - return -1; + goto done; } args.maxnames = maxnames; @@ -2138,32 +2280,36 @@ remoteListDefinedDomains (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_DOMAINS, (xdrproc_t) xdr_remote_list_defined_domains_args, (char *) &args, (xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { errorf (conn, VIR_ERR_RPC, _("too many remote domain names: %d > %d"), ret.names.names_len, maxnames); - xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static int remoteNumOfDefinedDomains (virConnectPtr conn) { + int rv = -1; remote_num_of_defined_domains_ret ret; struct private_data *priv = conn->privateData; @@ -2171,14 +2317,18 @@ remoteNumOfDefinedDomains (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_DOMAINS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_defined_domains_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteDomainCreate (virDomainPtr domain) { + int rv = -1; remote_domain_create_args args; struct private_data *priv = domain->conn->privateData; @@ -2187,15 +2337,18 @@ remoteDomainCreate (virDomainPtr domain) if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE, (xdrproc_t) xdr_remote_domain_create_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static virDomainPtr remoteDomainDefineXML (virConnectPtr conn, const char *xml) { - virDomainPtr dom; + virDomainPtr dom = NULL; remote_domain_define_xml_args args; remote_domain_define_xml_ret ret; struct private_data *priv = conn->privateData; @@ -2206,17 +2359,19 @@ remoteDomainDefineXML (virConnectPtr con if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_DEFINE_XML, (xdrproc_t) xdr_remote_domain_define_xml_args, (char *) &args, (xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; dom = get_nonnull_domain (conn, ret.dom); xdr_free ((xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret); +done: return dom; } static int remoteDomainUndefine (virDomainPtr domain) { + int rv = -1; remote_domain_undefine_args args; struct private_data *priv = domain->conn->privateData; @@ -2225,14 +2380,18 @@ remoteDomainUndefine (virDomainPtr domai if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_UNDEFINE, (xdrproc_t) xdr_remote_domain_undefine_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainAttachDevice (virDomainPtr domain, const char *xml) { + int rv = -1; remote_domain_attach_device_args args; struct private_data *priv = domain->conn->privateData; @@ -2242,14 +2401,18 @@ remoteDomainAttachDevice (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_ATTACH_DEVICE, (xdrproc_t) xdr_remote_domain_attach_device_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainDetachDevice (virDomainPtr domain, const char *xml) { + int rv = -1; remote_domain_detach_device_args args; struct private_data *priv = domain->conn->privateData; @@ -2259,14 +2422,18 @@ remoteDomainDetachDevice (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DETACH_DEVICE, (xdrproc_t) xdr_remote_domain_detach_device_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainGetAutostart (virDomainPtr domain, int *autostart) { + int rv = -1; remote_domain_get_autostart_args args; remote_domain_get_autostart_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2277,15 +2444,19 @@ remoteDomainGetAutostart (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_AUTOSTART, (xdrproc_t) xdr_remote_domain_get_autostart_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_autostart_ret, (char *) &ret) == -1) - return -1; + goto done; if (autostart) *autostart = ret.autostart; - return 0; + rv = 0; + +done: + return rv; } static int remoteDomainSetAutostart (virDomainPtr domain, int autostart) { + int rv = -1; remote_domain_set_autostart_args args; struct private_data *priv = domain->conn->privateData; @@ -2295,14 +2466,18 @@ remoteDomainSetAutostart (virDomainPtr d if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_AUTOSTART, (xdrproc_t) xdr_remote_domain_set_autostart_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static char * remoteDomainGetSchedulerType (virDomainPtr domain, int *nparams) { + char *rv = NULL; remote_domain_get_scheduler_type_args args; remote_domain_get_scheduler_type_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2313,21 +2488,25 @@ remoteDomainGetSchedulerType (virDomainP if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SCHEDULER_TYPE, (xdrproc_t) xdr_remote_domain_get_scheduler_type_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_scheduler_type_ret, (char *) &ret) == -1) - return NULL; + goto done; if (nparams) *nparams = ret.nparams; /* Caller frees this. */ - return ret.type; + rv = ret.type; + +done: + return rv; } static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) { + int rv = -1; remote_domain_get_scheduler_parameters_args args; remote_domain_get_scheduler_parameters_ret ret; - int i; + int i = -1; struct private_data *priv = domain->conn->privateData; make_nonnull_domain (&args.dom, domain); @@ -2337,16 +2516,15 @@ remoteDomainGetSchedulerParameters (virD if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS, (xdrproc_t) xdr_remote_domain_get_scheduler_parameters_args, (char *) &args, (xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret) == -1) - return -1; + goto done; /* Check the length of the returned list carefully. */ if (ret.params.params_len > REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX || ret.params.params_len > *nparams) { - xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); error (domain->conn, VIR_ERR_RPC, _("remoteDomainGetSchedulerParameters: " "returned number of parameters exceeds limit")); - return -1; + goto cleanup; } *nparams = ret.params.params_len; @@ -2370,22 +2548,26 @@ remoteDomainGetSchedulerParameters (virD case VIR_DOMAIN_SCHED_FIELD_BOOLEAN: params[i].value.b = ret.params.params_val[i].value.remote_sched_param_value_u.b; break; default: - xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); error (domain->conn, VIR_ERR_RPC, _("remoteDomainGetSchedulerParameters: " "unknown parameter type")); - return -1; - } - } - + goto cleanup; + } + } + + rv = 0; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; +done: + return rv; } static int remoteDomainSetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int nparams) { + int rv = -1; remote_domain_set_scheduler_parameters_args args; int i, do_error; struct private_data *priv = domain->conn->privateData; @@ -2396,7 +2578,7 @@ remoteDomainSetSchedulerParameters (virD args.params.params_len = nparams; if (VIR_ALLOC_N(args.params.params_val, nparams) < 0) { error (domain->conn, VIR_ERR_RPC, _("out of memory allocating array")); - return -1; + goto done; } do_error = 0; @@ -2429,21 +2611,25 @@ remoteDomainSetSchedulerParameters (virD if (do_error) { xdr_free ((xdrproc_t) xdr_remote_domain_set_scheduler_parameters_args, (char *) &args); - return -1; + goto done; } if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS, (xdrproc_t) xdr_remote_domain_set_scheduler_parameters_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteDomainBlockStats (virDomainPtr domain, const char *path, struct _virDomainBlockStats *stats) { + int rv = -1; remote_domain_block_stats_args args; remote_domain_block_stats_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2456,7 +2642,7 @@ remoteDomainBlockStats (virDomainPtr dom (xdrproc_t) xdr_remote_domain_block_stats_args, (char *) &args, (xdrproc_t) xdr_remote_domain_block_stats_ret, (char *) &ret) == -1) - return -1; + goto done; stats->rd_req = ret.rd_req; stats->rd_bytes = ret.rd_bytes; @@ -2464,13 +2650,17 @@ remoteDomainBlockStats (virDomainPtr dom stats->wr_bytes = ret.wr_bytes; stats->errs = ret.errs; - return 0; + rv = 0; + +done: + return rv; } static int remoteDomainInterfaceStats (virDomainPtr domain, const char *path, struct _virDomainInterfaceStats *stats) { + int rv = -1; remote_domain_interface_stats_args args; remote_domain_interface_stats_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2484,7 +2674,7 @@ remoteDomainInterfaceStats (virDomainPtr (char *) &args, (xdrproc_t) xdr_remote_domain_interface_stats_ret, (char *) &ret) == -1) - return -1; + goto done; stats->rx_bytes = ret.rx_bytes; stats->rx_packets = ret.rx_packets; @@ -2495,7 +2685,10 @@ remoteDomainInterfaceStats (virDomainPtr stats->tx_errs = ret.tx_errs; stats->tx_drop = ret.tx_drop; - return 0; + rv = 0; + +done: + return rv; } static int @@ -2506,6 +2699,7 @@ remoteDomainBlockPeek (virDomainPtr doma void *buffer, unsigned int flags) { + int rv = -1; remote_domain_block_peek_args args; remote_domain_block_peek_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2514,7 +2708,7 @@ remoteDomainBlockPeek (virDomainPtr doma errorf (domain->conn, VIR_ERR_RPC, _("block peek request too large for remote protocol, %zi > %d"), size, REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -2529,19 +2723,22 @@ remoteDomainBlockPeek (virDomainPtr doma (char *) &args, (xdrproc_t) xdr_remote_domain_block_peek_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.buffer.buffer_len != size) { - errorf (domain->conn, VIR_ERR_RPC, - "%s", _("returned buffer is not same size as requested")); - free (ret.buffer.buffer_val); - return -1; + errorf (domain->conn, VIR_ERR_RPC, + "%s", _("returned buffer is not same size as requested")); + goto cleanup; } memcpy (buffer, ret.buffer.buffer_val, size); + rv = 0; + +cleanup: free (ret.buffer.buffer_val); - return 0; +done: + return rv; } static int @@ -2551,6 +2748,7 @@ remoteDomainMemoryPeek (virDomainPtr dom void *buffer, unsigned int flags) { + int rv = -1; remote_domain_memory_peek_args args; remote_domain_memory_peek_ret ret; struct private_data *priv = domain->conn->privateData; @@ -2559,7 +2757,7 @@ remoteDomainMemoryPeek (virDomainPtr dom errorf (domain->conn, VIR_ERR_RPC, _("memory peek request too large for remote protocol, %zi > %d"), size, REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX); - return -1; + goto done; } make_nonnull_domain (&args.dom, domain); @@ -2573,19 +2771,22 @@ remoteDomainMemoryPeek (virDomainPtr dom (char *) &args, (xdrproc_t) xdr_remote_domain_memory_peek_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.buffer.buffer_len != size) { - errorf (domain->conn, VIR_ERR_RPC, - "%s", _("returned buffer is not same size as requested")); - free (ret.buffer.buffer_val); - return -1; + errorf (domain->conn, VIR_ERR_RPC, + "%s", _("returned buffer is not same size as requested")); + goto cleanup; } memcpy (buffer, ret.buffer.buffer_val, size); + rv = 0; + +cleanup: free (ret.buffer.buffer_val); - return 0; +done: + return rv; } /*----------------------------------------------------------------------*/ @@ -2639,23 +2840,24 @@ remoteNetworkOpen (virConnectPtr conn, static int remoteNetworkClose (virConnectPtr conn) { - int ret = 0; + int rv = 0; struct private_data *priv = conn->networkPrivateData; if (priv->localUses) { priv->localUses--; if (!priv->localUses) { - ret = doRemoteClose(conn, priv); + rv = doRemoteClose(conn, priv); VIR_FREE(priv); conn->networkPrivateData = NULL; } } - return ret; + return rv; } static int remoteNumOfNetworks (virConnectPtr conn) { + int rv = -1; remote_num_of_networks_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2663,14 +2865,18 @@ remoteNumOfNetworks (virConnectPtr conn) if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_NETWORKS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_networks_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListNetworks (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_networks_args args; remote_list_networks_ret ret; @@ -2680,7 +2886,7 @@ remoteListNetworks (virConnectPtr conn, errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), maxnames, REMOTE_NETWORK_NAME_LIST_MAX); - return -1; + goto done; } args.maxnames = maxnames; @@ -2688,32 +2894,36 @@ remoteListNetworks (virConnectPtr conn, if (call (conn, priv, 0, REMOTE_PROC_LIST_NETWORKS, (xdrproc_t) xdr_remote_list_networks_args, (char *) &args, (xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), ret.names.names_len, maxnames); - xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static int remoteNumOfDefinedNetworks (virConnectPtr conn) { + int rv = -1; remote_num_of_defined_networks_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2721,15 +2931,19 @@ remoteNumOfDefinedNetworks (virConnectPt if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_NETWORKS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_defined_networks_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListDefinedNetworks (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_defined_networks_args args; remote_list_defined_networks_ret ret; @@ -2739,7 +2953,7 @@ remoteListDefinedNetworks (virConnectPtr errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), maxnames, REMOTE_NETWORK_NAME_LIST_MAX); - return -1; + goto done; } args.maxnames = maxnames; @@ -2747,34 +2961,37 @@ remoteListDefinedNetworks (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_NETWORKS, (xdrproc_t) xdr_remote_list_defined_networks_args, (char *) &args, (xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { errorf (conn, VIR_ERR_RPC, _("too many remote networks: %d > %d"), ret.names.names_len, maxnames); - xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static virNetworkPtr remoteNetworkLookupByUUID (virConnectPtr conn, const unsigned char *uuid) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_lookup_by_uuid_args args; remote_network_lookup_by_uuid_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2785,11 +3002,12 @@ remoteNetworkLookupByUUID (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_NETWORK_LOOKUP_BY_UUID, (xdrproc_t) xdr_remote_network_lookup_by_uuid_args, (char *) &args, (xdrproc_t) xdr_remote_network_lookup_by_uuid_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_uuid_ret, (char *) &ret); +done: return net; } @@ -2797,7 +3015,7 @@ static virNetworkPtr remoteNetworkLookupByName (virConnectPtr conn, const char *name) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_lookup_by_name_args args; remote_network_lookup_by_name_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2808,18 +3026,19 @@ remoteNetworkLookupByName (virConnectPtr if (call (conn, priv, 0, REMOTE_PROC_NETWORK_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_network_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_network_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_name_ret, (char *) &ret); +done: return net; } static virNetworkPtr remoteNetworkCreateXML (virConnectPtr conn, const char *xmlDesc) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_create_xml_args args; remote_network_create_xml_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2830,18 +3049,19 @@ remoteNetworkCreateXML (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_NETWORK_CREATE_XML, (xdrproc_t) xdr_remote_network_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_network_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_create_xml_ret, (char *) &ret); +done: return net; } static virNetworkPtr remoteNetworkDefineXML (virConnectPtr conn, const char *xml) { - virNetworkPtr net; + virNetworkPtr net = NULL; remote_network_define_xml_args args; remote_network_define_xml_ret ret; struct private_data *priv = conn->networkPrivateData; @@ -2852,17 +3072,19 @@ remoteNetworkDefineXML (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_NETWORK_DEFINE_XML, (xdrproc_t) xdr_remote_network_define_xml_args, (char *) &args, (xdrproc_t) xdr_remote_network_define_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; net = get_nonnull_network (conn, ret.net); xdr_free ((xdrproc_t) &xdr_remote_network_define_xml_ret, (char *) &ret); +done: return net; } static int remoteNetworkUndefine (virNetworkPtr network) { + int rv = -1; remote_network_undefine_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2871,14 +3093,18 @@ remoteNetworkUndefine (virNetworkPtr net if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_UNDEFINE, (xdrproc_t) xdr_remote_network_undefine_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteNetworkCreate (virNetworkPtr network) { + int rv = -1; remote_network_create_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2887,14 +3113,18 @@ remoteNetworkCreate (virNetworkPtr netwo if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_CREATE, (xdrproc_t) xdr_remote_network_create_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteNetworkDestroy (virNetworkPtr network) { + int rv = -1; remote_network_destroy_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2903,14 +3133,18 @@ remoteNetworkDestroy (virNetworkPtr netw if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DESTROY, (xdrproc_t) xdr_remote_network_destroy_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static char * remoteNetworkDumpXML (virNetworkPtr network, int flags) { + char *rv = NULL; remote_network_dump_xml_args args; remote_network_dump_xml_ret ret; struct private_data *priv = network->conn->networkPrivateData; @@ -2922,15 +3156,19 @@ remoteNetworkDumpXML (virNetworkPtr netw if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DUMP_XML, (xdrproc_t) xdr_remote_network_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_network_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static char * remoteNetworkGetBridgeName (virNetworkPtr network) { + char *rv = NULL; remote_network_get_bridge_name_args args; remote_network_get_bridge_name_ret ret; struct private_data *priv = network->conn->networkPrivateData; @@ -2941,15 +3179,19 @@ remoteNetworkGetBridgeName (virNetworkPt if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_GET_BRIDGE_NAME, (xdrproc_t) xdr_remote_network_get_bridge_name_args, (char *) &args, (xdrproc_t) xdr_remote_network_get_bridge_name_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.name; + goto done; + + /* Caller frees. */ + rv = ret.name; + +done: + return rv; } static int remoteNetworkGetAutostart (virNetworkPtr network, int *autostart) { + int rv = -1; remote_network_get_autostart_args args; remote_network_get_autostart_ret ret; struct private_data *priv = network->conn->networkPrivateData; @@ -2960,16 +3202,20 @@ remoteNetworkGetAutostart (virNetworkPtr if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_GET_AUTOSTART, (xdrproc_t) xdr_remote_network_get_autostart_args, (char *) &args, (xdrproc_t) xdr_remote_network_get_autostart_ret, (char *) &ret) == -1) - return -1; + goto done; if (autostart) *autostart = ret.autostart; - return 0; + rv = 0; + +done: + return rv; } static int remoteNetworkSetAutostart (virNetworkPtr network, int autostart) { + int rv = -1; remote_network_set_autostart_args args; struct private_data *priv = network->conn->networkPrivateData; @@ -2979,9 +3225,12 @@ remoteNetworkSetAutostart (virNetworkPtr if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_SET_AUTOSTART, (xdrproc_t) xdr_remote_network_set_autostart_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } @@ -3054,12 +3303,14 @@ remoteStorageClose (virConnectPtr conn) conn->storagePrivateData = NULL; } } + return ret; } static int remoteNumOfStoragePools (virConnectPtr conn) { + int rv = -1; remote_num_of_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3067,14 +3318,18 @@ remoteNumOfStoragePools (virConnectPtr c if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_STORAGE_POOLS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_storage_pools_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListStoragePools (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_storage_pools_args args; remote_list_storage_pools_ret ret; @@ -3082,7 +3337,7 @@ remoteListStoragePools (virConnectPtr co if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); - return -1; + goto done; } args.maxnames = maxnames; @@ -3090,30 +3345,34 @@ remoteListStoragePools (virConnectPtr co if (call (conn, priv, 0, REMOTE_PROC_LIST_STORAGE_POOLS, (xdrproc_t) xdr_remote_list_storage_pools_args, (char *) &args, (xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (conn, VIR_ERR_RPC, _("too many storage pools received")); - xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static int remoteNumOfDefinedStoragePools (virConnectPtr conn) { + int rv = -1; remote_num_of_defined_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3121,15 +3380,19 @@ remoteNumOfDefinedStoragePools (virConne if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_num_of_defined_storage_pools_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteListDefinedStoragePools (virConnectPtr conn, char **const names, int maxnames) { + int rv = -1; int i; remote_list_defined_storage_pools_args args; remote_list_defined_storage_pools_ret ret; @@ -3137,7 +3400,7 @@ remoteListDefinedStoragePools (virConnec if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); - return -1; + goto done; } args.maxnames = maxnames; @@ -3145,25 +3408,28 @@ remoteListDefinedStoragePools (virConnec if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_STORAGE_POOLS, (xdrproc_t) xdr_remote_list_defined_storage_pools_args, (char *) &args, (xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (conn, VIR_ERR_RPC, _("too many storage pools received")); - xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } static char * @@ -3172,11 +3438,11 @@ remoteFindStoragePoolSources (virConnect const char *srcSpec, unsigned int flags) { + char *rv = NULL; remote_find_storage_pool_sources_args args; remote_find_storage_pool_sources_ret ret; struct private_data *priv = conn->storagePrivateData; const char *emptyString = ""; - char *retval; args.type = (char*)type; /* @@ -3197,21 +3463,22 @@ remoteFindStoragePoolSources (virConnect if (call (conn, priv, 0, REMOTE_PROC_FIND_STORAGE_POOL_SOURCES, (xdrproc_t) xdr_remote_find_storage_pool_sources_args, (char *) &args, (xdrproc_t) xdr_remote_find_storage_pool_sources_ret, (char *) &ret) == -1) - return NULL; - - retval = ret.xml; + goto done; + + rv = ret.xml; ret.xml = NULL; /* To stop xdr_free free'ing it */ xdr_free ((xdrproc_t) xdr_remote_find_storage_pool_sources_ret, (char *) &ret); - return retval; +done: + return rv; } static virStoragePoolPtr remoteStoragePoolLookupByUUID (virConnectPtr conn, const unsigned char *uuid) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_lookup_by_uuid_args args; remote_storage_pool_lookup_by_uuid_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3222,11 +3489,12 @@ remoteStoragePoolLookupByUUID (virConnec if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_UUID, (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret); +done: return pool; } @@ -3234,7 +3502,7 @@ static virStoragePoolPtr remoteStoragePoolLookupByName (virConnectPtr conn, const char *name) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_lookup_by_name_args args; remote_storage_pool_lookup_by_name_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3245,18 +3513,19 @@ remoteStoragePoolLookupByName (virConnec if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret); +done: return pool; } static virStoragePoolPtr remoteStoragePoolLookupByVolume (virStorageVolPtr vol) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_lookup_by_volume_args args; remote_storage_pool_lookup_by_volume_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3267,11 +3536,12 @@ remoteStoragePoolLookupByVolume (virStor if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_VOLUME, (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (vol->conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret); +done: return pool; } @@ -3279,7 +3549,7 @@ remoteStoragePoolLookupByVolume (virStor static virStoragePoolPtr remoteStoragePoolCreateXML (virConnectPtr conn, const char *xmlDesc, unsigned int flags) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_create_xml_args args; remote_storage_pool_create_xml_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3291,18 +3561,19 @@ remoteStoragePoolCreateXML (virConnectPt if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE_XML, (xdrproc_t) xdr_remote_storage_pool_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_create_xml_ret, (char *) &ret); +done: return pool; } static virStoragePoolPtr remoteStoragePoolDefineXML (virConnectPtr conn, const char *xml, unsigned int flags) { - virStoragePoolPtr pool; + virStoragePoolPtr pool = NULL; remote_storage_pool_define_xml_args args; remote_storage_pool_define_xml_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3314,17 +3585,19 @@ remoteStoragePoolDefineXML (virConnectPt if (call (conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DEFINE_XML, (xdrproc_t) xdr_remote_storage_pool_define_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_define_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; pool = get_nonnull_storage_pool (conn, ret.pool); xdr_free ((xdrproc_t) &xdr_remote_storage_pool_define_xml_ret, (char *) &ret); +done: return pool; } static int remoteStoragePoolUndefine (virStoragePoolPtr pool) { + int rv = -1; remote_storage_pool_undefine_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3333,14 +3606,18 @@ remoteStoragePoolUndefine (virStoragePoo if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_UNDEFINE, (xdrproc_t) xdr_remote_storage_pool_undefine_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolCreate (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_create_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3350,15 +3627,19 @@ remoteStoragePoolCreate (virStoragePoolP if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_CREATE, (xdrproc_t) xdr_remote_storage_pool_create_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolBuild (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_build_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3368,14 +3649,18 @@ remoteStoragePoolBuild (virStoragePoolPt if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_BUILD, (xdrproc_t) xdr_remote_storage_pool_build_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolDestroy (virStoragePoolPtr pool) { + int rv = -1; remote_storage_pool_destroy_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3384,15 +3669,19 @@ remoteStoragePoolDestroy (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DESTROY, (xdrproc_t) xdr_remote_storage_pool_destroy_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolDelete (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_delete_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3402,15 +3691,19 @@ remoteStoragePoolDelete (virStoragePoolP if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DELETE, (xdrproc_t) xdr_remote_storage_pool_delete_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolRefresh (virStoragePoolPtr pool, unsigned int flags) { + int rv = -1; remote_storage_pool_refresh_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3420,14 +3713,18 @@ remoteStoragePoolRefresh (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_REFRESH, (xdrproc_t) xdr_remote_storage_pool_refresh_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolGetInfo (virStoragePoolPtr pool, virStoragePoolInfoPtr info) { + int rv = -1; remote_storage_pool_get_info_args args; remote_storage_pool_get_info_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3438,20 +3735,24 @@ remoteStoragePoolGetInfo (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_INFO, (xdrproc_t) xdr_remote_storage_pool_get_info_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; info->state = ret.state; info->capacity = ret.capacity; info->allocation = ret.allocation; info->available = ret.available; - return 0; + rv = 0; + +done: + return rv; } static char * remoteStoragePoolDumpXML (virStoragePoolPtr pool, unsigned int flags) { + char *rv = NULL; remote_storage_pool_dump_xml_args args; remote_storage_pool_dump_xml_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3463,15 +3764,19 @@ remoteStoragePoolDumpXML (virStoragePool if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_DUMP_XML, (xdrproc_t) xdr_remote_storage_pool_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static int remoteStoragePoolGetAutostart (virStoragePoolPtr pool, int *autostart) { + int rv = -1; remote_storage_pool_get_autostart_args args; remote_storage_pool_get_autostart_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3482,16 +3787,20 @@ remoteStoragePoolGetAutostart (virStorag if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_GET_AUTOSTART, (xdrproc_t) xdr_remote_storage_pool_get_autostart_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_get_autostart_ret, (char *) &ret) == -1) - return -1; + goto done; if (autostart) *autostart = ret.autostart; - return 0; + rv = 0; + +done: + return rv; } static int remoteStoragePoolSetAutostart (virStoragePoolPtr pool, int autostart) { + int rv = -1; remote_storage_pool_set_autostart_args args; struct private_data *priv = pool->conn->storagePrivateData; @@ -3501,15 +3810,19 @@ remoteStoragePoolSetAutostart (virStorag if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_SET_AUTOSTART, (xdrproc_t) xdr_remote_storage_pool_set_autostart_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStoragePoolNumOfVolumes (virStoragePoolPtr pool) { + int rv = -1; remote_storage_pool_num_of_volumes_args args; remote_storage_pool_num_of_volumes_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3520,14 +3833,18 @@ remoteStoragePoolNumOfVolumes (virStorag if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_NUM_OF_VOLUMES, (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_num_of_volumes_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteStoragePoolListVolumes (virStoragePoolPtr pool, char **const names, int maxnames) { + int rv = -1; int i; remote_storage_pool_list_volumes_args args; remote_storage_pool_list_volumes_ret ret; @@ -3535,7 +3852,7 @@ remoteStoragePoolListVolumes (virStorage if (maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { error (pool->conn, VIR_ERR_RPC, _("too many storage volumes requested")); - return -1; + goto done; } args.maxnames = maxnames; make_nonnull_storage_pool(&args.pool, pool); @@ -3544,25 +3861,28 @@ remoteStoragePoolListVolumes (virStorage if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_POOL_LIST_VOLUMES, (xdrproc_t) xdr_remote_storage_pool_list_volumes_args, (char *) &args, (xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (pool->conn, VIR_ERR_RPC, _("too many storage volumes received")); - xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } @@ -3571,7 +3891,7 @@ static virStorageVolPtr remoteStorageVolLookupByName (virStoragePoolPtr pool, const char *name) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_lookup_by_name_args args; remote_storage_vol_lookup_by_name_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3583,11 +3903,12 @@ remoteStorageVolLookupByName (virStorage if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (pool->conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret); +done: return vol; } @@ -3595,7 +3916,7 @@ static virStorageVolPtr remoteStorageVolLookupByKey (virConnectPtr conn, const char *key) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_lookup_by_key_args args; remote_storage_vol_lookup_by_key_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3606,11 +3927,12 @@ remoteStorageVolLookupByKey (virConnectP if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_KEY, (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret); +done: return vol; } @@ -3618,7 +3940,7 @@ static virStorageVolPtr remoteStorageVolLookupByPath (virConnectPtr conn, const char *path) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_lookup_by_path_args args; remote_storage_vol_lookup_by_path_ret ret; struct private_data *priv = conn->storagePrivateData; @@ -3629,11 +3951,12 @@ remoteStorageVolLookupByPath (virConnect if (call (conn, priv, 0, REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH, (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret); +done: return vol; } @@ -3641,7 +3964,7 @@ static virStorageVolPtr remoteStorageVolCreateXML (virStoragePoolPtr pool, const char *xmlDesc, unsigned int flags) { - virStorageVolPtr vol; + virStorageVolPtr vol = NULL; remote_storage_vol_create_xml_args args; remote_storage_vol_create_xml_ret ret; struct private_data *priv = pool->conn->storagePrivateData; @@ -3654,11 +3977,12 @@ remoteStorageVolCreateXML (virStoragePoo if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CREATE_XML, (xdrproc_t) xdr_remote_storage_vol_create_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_create_xml_ret, (char *) &ret) == -1) - return NULL; + goto done; vol = get_nonnull_storage_vol (pool->conn, ret.vol); xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_ret, (char *) &ret); +done: return vol; } @@ -3666,6 +3990,7 @@ static int remoteStorageVolDelete (virStorageVolPtr vol, unsigned int flags) { + int rv = -1; remote_storage_vol_delete_args args; struct private_data *priv = vol->conn->storagePrivateData; @@ -3675,14 +4000,18 @@ remoteStorageVolDelete (virStorageVolPtr if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DELETE, (xdrproc_t) xdr_remote_storage_vol_delete_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - return 0; + goto done; + + rv = 0; + +done: + return rv; } static int remoteStorageVolGetInfo (virStorageVolPtr vol, virStorageVolInfoPtr info) { + int rv = -1; remote_storage_vol_get_info_args args; remote_storage_vol_get_info_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3693,19 +4022,23 @@ remoteStorageVolGetInfo (virStorageVolPt if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_INFO, (xdrproc_t) xdr_remote_storage_vol_get_info_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_get_info_ret, (char *) &ret) == -1) - return -1; + goto done; info->type = ret.type; info->capacity = ret.capacity; info->allocation = ret.allocation; - return 0; + rv = 0; + +done: + return rv; } static char * remoteStorageVolDumpXML (virStorageVolPtr vol, unsigned int flags) { + char *rv = NULL; remote_storage_vol_dump_xml_args args; remote_storage_vol_dump_xml_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3717,15 +4050,19 @@ remoteStorageVolDumpXML (virStorageVolPt if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_DUMP_XML, (xdrproc_t) xdr_remote_storage_vol_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static char * remoteStorageVolGetPath (virStorageVolPtr vol) { + char *rv = NULL; remote_storage_vol_get_path_args args; remote_storage_vol_get_path_ret ret; struct private_data *priv = vol->conn->storagePrivateData; @@ -3736,10 +4073,13 @@ remoteStorageVolGetPath (virStorageVolPt if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_GET_PATH, (xdrproc_t) xdr_remote_storage_vol_get_path_args, (char *) &args, (xdrproc_t) xdr_remote_storage_vol_get_path_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.name; + goto done; + + /* Caller frees. */ + rv = ret.name; + +done: + return rv; } @@ -3785,6 +4125,7 @@ static int remoteNodeNumOfDevices(virCon const char *cap, unsigned int flags) { + int rv = -1; remote_node_num_of_devices_args args; remote_node_num_of_devices_ret ret; struct private_data *priv = conn->devMonPrivateData; @@ -3796,9 +4137,12 @@ static int remoteNodeNumOfDevices(virCon if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES, (xdrproc_t) xdr_remote_node_num_of_devices_args, (char *) &args, (xdrproc_t) xdr_remote_node_num_of_devices_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } @@ -3808,6 +4152,7 @@ static int remoteNodeListDevices(virConn int maxnames, unsigned int flags) { + int rv = -1; int i; remote_node_list_devices_args args; remote_node_list_devices_ret ret; @@ -3815,7 +4160,7 @@ static int remoteNodeListDevices(virConn if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many device names requested")); - return -1; + goto done; } args.cap = cap ? (char **)&cap : NULL; args.maxnames = maxnames; @@ -3825,25 +4170,28 @@ static int remoteNodeListDevices(virConn if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES, (xdrproc_t) xdr_remote_node_list_devices_args, (char *) &args, (xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (conn, VIR_ERR_RPC, _("too many device names received")); - xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } @@ -3852,7 +4200,7 @@ static virNodeDevicePtr remoteNodeDevice { remote_node_device_lookup_by_name_args args; remote_node_device_lookup_by_name_ret ret; - virNodeDevicePtr dev; + virNodeDevicePtr dev = NULL; struct private_data *priv = conn->devMonPrivateData; args.name = (char *)name; @@ -3861,18 +4209,20 @@ static virNodeDevicePtr remoteNodeDevice if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME, (xdrproc_t) xdr_remote_node_device_lookup_by_name_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret) == -1) - return NULL; + goto done; dev = get_nonnull_node_device(conn, ret.dev); xdr_free ((xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret); +done: return dev; } static char *remoteNodeDeviceDumpXML(virNodeDevicePtr dev, unsigned int flags) { + char *rv = NULL; remote_node_device_dump_xml_args args; remote_node_device_dump_xml_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; @@ -3884,14 +4234,18 @@ static char *remoteNodeDeviceDumpXML(vir if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DUMP_XML, (xdrproc_t) xdr_remote_node_device_dump_xml_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_dump_xml_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.xml; + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + return rv; } static char *remoteNodeDeviceGetParent(virNodeDevicePtr dev) { + char *rv = NULL; remote_node_device_get_parent_args args; remote_node_device_get_parent_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; @@ -3902,14 +4256,18 @@ static char *remoteNodeDeviceGetParent(v if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_GET_PARENT, (xdrproc_t) xdr_remote_node_device_get_parent_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_get_parent_ret, (char *) &ret) == -1) - return NULL; - - /* Caller frees. */ - return ret.parent ? *ret.parent : NULL; + goto done; + + /* Caller frees. */ + rv = ret.parent ? *ret.parent : NULL; + +done: + return rv; } static int remoteNodeDeviceNumOfCaps(virNodeDevicePtr dev) { + int rv = -1; remote_node_device_num_of_caps_args args; remote_node_device_num_of_caps_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; @@ -3920,15 +4278,19 @@ static int remoteNodeDeviceNumOfCaps(vir if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS, (xdrproc_t) xdr_remote_node_device_num_of_caps_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_num_of_caps_ret, (char *) &ret) == -1) - return -1; - - return ret.num; + goto done; + + rv = ret.num; + +done: + return rv; } static int remoteNodeDeviceListCaps(virNodeDevicePtr dev, char **const names, int maxnames) { + int rv = -1; int i; remote_node_device_list_caps_args args; remote_node_device_list_caps_ret ret; @@ -3936,7 +4298,7 @@ static int remoteNodeDeviceListCaps(virN if (maxnames > REMOTE_NODE_DEVICE_CAPS_LIST_MAX) { error (dev->conn, VIR_ERR_RPC, _("too many capability names requested")); - return -1; + goto done; } args.maxnames = maxnames; args.name = dev->name; @@ -3945,25 +4307,28 @@ static int remoteNodeDeviceListCaps(virN if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LIST_CAPS, (xdrproc_t) xdr_remote_node_device_list_caps_args, (char *) &args, (xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret) == -1) - return -1; + goto done; if (ret.names.names_len > maxnames) { error (dev->conn, VIR_ERR_RPC, _("too many capability names received")); - xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret); - return -1; - } - - /* This call is caller-frees (although that isn't clear from - * the documentation). However xdr_free will free up both the - * names and the list of pointers, so we have to strdup the - * names here. - */ - for (i = 0; i < ret.names.names_len; ++i) - names[i] = strdup (ret.names.names_val[i]); - + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) + names[i] = strdup (ret.names.names_val[i]); + + rv = ret.names.names_len; + +cleanup: xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret); - return ret.names.names_len; +done: + return rv; } @@ -4620,6 +4985,7 @@ remoteAuthSASL (virConnectPtr conn, stru remoteAuthFreeCredentials(cred, ncred); if (ret != 0 && saslconn) sasl_dispose(&saslconn); + return ret; } #endif /* HAVE_SASL */ @@ -4684,16 +5050,17 @@ static int remoteDomainEventRegister (vi void *opaque, virFreeCallback freecb) { + int rv = -1; struct private_data *priv = conn->privateData; if (priv->eventFlushTimer < 0) { error (conn, VIR_ERR_NO_SUPPORT, _("no event support")); - return -1; + goto done; } if (virDomainEventCallbackListAdd(conn, priv->callbackList, callback, opaque, freecb) < 0) { error (conn, VIR_ERR_RPC, _("adding cb to list")); - return -1; + goto done; } if ( priv->callbackList->count == 1 ) { @@ -4701,21 +5068,25 @@ static int remoteDomainEventRegister (vi if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_REGISTER, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - } - - return 0; + goto done; + } + + rv = 0; + +done: + return rv; } static int remoteDomainEventDeregister (virConnectPtr conn, virConnectDomainEventCallback callback) { struct private_data *priv = conn->privateData; + int rv = -1; if (virDomainEventCallbackListRemove(conn, priv->callbackList, - callback) < 0) { + callback) < 0) { error (conn, VIR_ERR_RPC, _("removing cb fron list")); - return -1; + goto done; } if ( priv->callbackList->count == 0 ) { @@ -4723,10 +5094,13 @@ static int remoteDomainEventDeregister ( if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - } - - return 0; + goto done; + } + + rv = 0; + +done: + return rv; } /*----------------------------------------------------------------------*/ @@ -5522,22 +5896,22 @@ remoteDomainEventFired(int watch, DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); virEventRemoveHandle(watch); - return; + goto done; } if (fd != priv->sock) { virEventRemoveHandle(watch); - return; + goto done; } /* Read and deserialise length word. */ if (really_read (conn, priv, 0, buffer2, sizeof buffer2) == -1) - return; + goto done; xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); if (!xdr_int (&xdr, &len)) { error (conn, VIR_ERR_RPC, _("xdr_int (length word, reply)")); - return; + goto done; } xdr_destroy (&xdr); @@ -5546,20 +5920,20 @@ remoteDomainEventFired(int watch, if (len < 0 || len > REMOTE_MESSAGE_MAX) { error (conn, VIR_ERR_RPC, _("packet received from server too large")); - return; + goto done; } /* Read reply header and what follows (either a ret or an error). */ if (really_read (conn, priv, 0, buffer, len) == -1) { error (conn, VIR_ERR_RPC, _("error reading buffer from memory")); - return; + goto done; } /* Deserialise reply header. */ xdrmem_create (&xdr, buffer, len, XDR_DECODE); if (!xdr_remote_message_header (&xdr, &hdr)) { error (conn, VIR_ERR_RPC, _("invalid header in event firing")); - return; + goto done; } if (hdr.proc == REMOTE_PROC_DOMAIN_EVENT && @@ -5570,6 +5944,9 @@ remoteDomainEventFired(int watch, DEBUG0("invalid proc in event firing"); error (conn, VIR_ERR_RPC, _("invalid proc in event firing")); } + +done: + return; } void -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 15, 2009 at 11:31:53AM +0000, Daniel P. Berrange wrote:
On Wed, Jan 14, 2009 at 10:41:50PM +0000, Daniel P. Berrange wrote:
On Wed, Jan 14, 2009 at 08:39:02PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
good idea ...
Updated to remove that bogus chunk.
I could not spot anythings suspect, +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Wed, Jan 14, 2009 at 10:41:50PM +0000, Daniel P. Berrange wrote:
On Wed, Jan 14, 2009 at 08:39:02PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++-------------------
Hi Dan,
I got about halfway through it and spotted something odd:
diff --git a/src/remote_internal.c b/src/remote_internal.c ... static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) ... +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; + if (rv != 0) { + for ( ; i >= 0 ; i--) + VIR_FREE(params[i].field); + } + +done: + return rv; }
Freeing the .field member looks bogus.
Hmmm, not sure why I added that - I must have been thinking it was a malloc'd char * at the time. Clearly bogus
Updated to remove that bogus chunk.
ACK to that. I confirmed it removes just those 6 lines and updated the git patch to match.

On Thu, Jan 15, 2009 at 03:41:35PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Wed, Jan 14, 2009 at 10:41:50PM +0000, Daniel P. Berrange wrote:
On Wed, Jan 14, 2009 at 08:39:02PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch ensures all public API methods only have a single exit path, to make mutex unlocking simpler.
remote_internal.c | 1256 +++++++++++++++++++++++++++++++++++-------------------
Hi Dan,
I got about halfway through it and spotted something odd:
diff --git a/src/remote_internal.c b/src/remote_internal.c ... static int remoteDomainGetSchedulerParameters (virDomainPtr domain, virSchedParameterPtr params, int *nparams) ... +cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_scheduler_parameters_ret, (char *) &ret); - return 0; + if (rv != 0) { + for ( ; i >= 0 ; i--) + VIR_FREE(params[i].field); + } + +done: + return rv; }
Freeing the .field member looks bogus.
Hmmm, not sure why I added that - I must have been thinking it was a malloc'd char * at the time. Clearly bogus
Updated to remove that bogus chunk.
ACK to that. I confirmed it removes just those 6 lines and updated the git patch to match.
Comitted this patch now. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 15, 2009 at 11:31:53AM +0000, Daniel P. Berrange wrote:
Updated to remove that bogus chunk.
+1 based on overview of the patch and Jim's detailed reading. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

Adds locking in the remote driver. This obtains the lock on every single API method. This is quite poor, not giving any parallelism - that will be added in 2 patch's time, when we drop the lock while waiting on I/O remote_internal.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 481 insertions(+), 42 deletions(-) Daniel diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -89,6 +89,8 @@ static int inside_daemon = 0; static int inside_daemon = 0; struct private_data { + virMutex lock; + int sock; /* Socket. */ int watch; /* File handle watch */ pid_t pid; /* PID of tunnel process */ @@ -119,6 +121,16 @@ enum { REMOTE_CALL_QUIET_MISSING_RPC = 2, }; + +static void remoteDriverLock(struct private_data *driver) +{ + virMutexLock(&driver->lock); +} + +static void remoteDriverUnlock(struct private_data *driver) +{ + virMutexUnlock(&driver->lock); +} static int call (virConnectPtr conn, struct private_data *priv, int flags, int proc_nr, @@ -829,6 +841,15 @@ remoteOpen (virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } + if (virMutexInit(&priv->lock) < 0) { + error(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + VIR_FREE(priv); + return VIR_DRV_OPEN_ERROR; + } + remoteDriverLock(priv); + priv->localUses = 1; + if (flags & VIR_CONNECT_RO) rflags |= VIR_DRV_OPEN_REMOTE_RO; @@ -883,9 +904,11 @@ remoteOpen (virConnectPtr conn, ret = doRemoteOpen(conn, priv, auth, rflags); if (ret != VIR_DRV_OPEN_SUCCESS) { conn->privateData = NULL; + remoteDriverUnlock(priv); VIR_FREE(priv); } else { conn->privateData = priv; + remoteDriverUnlock(priv); } return ret; } @@ -1240,12 +1263,20 @@ static int static int remoteClose (virConnectPtr conn) { - int ret; - struct private_data *priv = conn->privateData; - - ret = doRemoteClose(conn, priv); - VIR_FREE (priv); - conn->privateData = NULL; + int ret = 0; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + conn->privateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE (priv); + } + if (priv) + remoteDriverUnlock(priv); return ret; } @@ -1257,6 +1288,8 @@ remoteSupportsFeature (virConnectPtr con remote_supports_feature_args args; remote_supports_feature_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); /* VIR_DRV_FEATURE_REMOTE* features are handled directly. */ if (feature == VIR_DRV_FEATURE_REMOTE) { @@ -1275,6 +1308,7 @@ remoteSupportsFeature (virConnectPtr con rv = ret.supported; done: + remoteDriverUnlock(priv); return rv; } @@ -1293,6 +1327,8 @@ remoteType (virConnectPtr conn) remote_get_type_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + /* Cached? */ if (priv->type) { rv = priv->type; @@ -1309,6 +1345,7 @@ remoteType (virConnectPtr conn) rv = priv->type = ret.type; done: + remoteDriverUnlock(priv); return rv; } @@ -1319,6 +1356,8 @@ remoteGetVersion (virConnectPtr conn, un remote_get_version_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_VERSION, (xdrproc_t) xdr_void, (char *) NULL, @@ -1329,6 +1368,7 @@ remoteGetVersion (virConnectPtr conn, un rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1338,6 +1378,8 @@ remoteGetHostname (virConnectPtr conn) char *rv = NULL; remote_get_hostname_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_HOSTNAME, @@ -1349,6 +1391,7 @@ remoteGetHostname (virConnectPtr conn) rv = ret.hostname; done: + remoteDriverUnlock(priv); return rv; } @@ -1359,6 +1402,8 @@ remoteGetMaxVcpus (virConnectPtr conn, c remote_get_max_vcpus_args args; remote_get_max_vcpus_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); memset (&ret, 0, sizeof ret); args.type = type == NULL ? NULL : (char **) &type; @@ -1370,6 +1415,7 @@ remoteGetMaxVcpus (virConnectPtr conn, c rv = ret.max_vcpus; done: + remoteDriverUnlock(priv); return rv; } @@ -1379,6 +1425,8 @@ remoteNodeGetInfo (virConnectPtr conn, v int rv = -1; remote_node_get_info_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_INFO, @@ -1398,6 +1446,7 @@ remoteNodeGetInfo (virConnectPtr conn, v rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1407,6 +1456,8 @@ remoteGetCapabilities (virConnectPtr con char *rv = NULL; remote_get_capabilities_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_GET_CAPABILITIES, @@ -1418,6 +1469,7 @@ remoteGetCapabilities (virConnectPtr con rv = ret.capabilities; done: + remoteDriverUnlock(priv); return rv; } @@ -1433,6 +1485,8 @@ remoteNodeGetCellsFreeMemory(virConnectP int i; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + if (maxCells > REMOTE_NODE_MAX_CELLS) { errorf (conn, VIR_ERR_RPC, _("too many NUMA cells: %d > %d"), @@ -1458,6 +1512,7 @@ remoteNodeGetCellsFreeMemory(virConnectP rv = ret.freeMems.freeMems_len; done: + remoteDriverUnlock(priv); return rv; } @@ -1467,6 +1522,8 @@ remoteNodeGetFreeMemory (virConnectPtr c unsigned long long rv = 0; /* 0 is error value this special function*/ remote_node_get_free_memory_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_FREE_MEMORY, @@ -1477,6 +1534,7 @@ remoteNodeGetFreeMemory (virConnectPtr c rv = ret.freeMem; done: + remoteDriverUnlock(priv); return rv; } @@ -1489,6 +1547,8 @@ remoteListDomains (virConnectPtr conn, i remote_list_domains_args args; remote_list_domains_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); if (maxids > REMOTE_DOMAIN_ID_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -1520,6 +1580,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -1530,6 +1591,8 @@ remoteNumOfDomains (virConnectPtr conn) remote_num_of_domains_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DOMAINS, (xdrproc_t) xdr_void, (char *) NULL, @@ -1539,6 +1602,7 @@ remoteNumOfDomains (virConnectPtr conn) rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -1552,6 +1616,8 @@ remoteDomainCreateXML (virConnectPtr con remote_domain_create_xml_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + args.xml_desc = (char *) xmlDesc; args.flags = flags; @@ -1565,6 +1631,7 @@ remoteDomainCreateXML (virConnectPtr con xdr_free ((xdrproc_t) &xdr_remote_domain_create_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return dom; } @@ -1576,6 +1643,8 @@ remoteDomainLookupByID (virConnectPtr co remote_domain_lookup_by_id_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + args.id = id; memset (&ret, 0, sizeof ret); @@ -1588,6 +1657,7 @@ remoteDomainLookupByID (virConnectPtr co xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_id_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return dom; } @@ -1599,6 +1669,8 @@ remoteDomainLookupByUUID (virConnectPtr remote_domain_lookup_by_uuid_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); memset (&ret, 0, sizeof ret); @@ -1611,6 +1683,7 @@ remoteDomainLookupByUUID (virConnectPtr xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return dom; } @@ -1622,6 +1695,8 @@ remoteDomainLookupByName (virConnectPtr remote_domain_lookup_by_name_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + args.name = (char *) name; memset (&ret, 0, sizeof ret); @@ -1634,6 +1709,7 @@ remoteDomainLookupByName (virConnectPtr xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_name_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return dom; } @@ -1643,6 +1719,8 @@ remoteDomainSuspend (virDomainPtr domain int rv = -1; remote_domain_suspend_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -1654,6 +1732,7 @@ remoteDomainSuspend (virDomainPtr domain rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1663,6 +1742,8 @@ remoteDomainResume (virDomainPtr domain) int rv = -1; remote_domain_resume_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -1674,6 +1755,7 @@ remoteDomainResume (virDomainPtr domain) rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1683,6 +1765,8 @@ remoteDomainShutdown (virDomainPtr domai int rv = -1; remote_domain_shutdown_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -1694,6 +1778,7 @@ remoteDomainShutdown (virDomainPtr domai rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1703,6 +1788,8 @@ remoteDomainReboot (virDomainPtr domain, int rv = -1; remote_domain_reboot_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.flags = flags; @@ -1715,6 +1802,7 @@ remoteDomainReboot (virDomainPtr domain, rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1724,6 +1812,8 @@ remoteDomainDestroy (virDomainPtr domain int rv = -1; remote_domain_destroy_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -1735,6 +1825,7 @@ remoteDomainDestroy (virDomainPtr domain rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1745,6 +1836,8 @@ remoteDomainGetOSType (virDomainPtr doma remote_domain_get_os_type_args args; remote_domain_get_os_type_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -1758,6 +1851,7 @@ remoteDomainGetOSType (virDomainPtr doma rv = ret.type; done: + remoteDriverUnlock(priv); return rv; } @@ -1769,6 +1863,8 @@ remoteDomainGetMaxMemory (virDomainPtr d remote_domain_get_max_memory_ret ret; struct private_data *priv = domain->conn->privateData; + remoteDriverLock(priv); + make_nonnull_domain (&args.dom, domain); memset (&ret, 0, sizeof ret); @@ -1780,6 +1876,7 @@ remoteDomainGetMaxMemory (virDomainPtr d rv = ret.memory; done: + remoteDriverUnlock(priv); return rv; } @@ -1789,6 +1886,8 @@ remoteDomainSetMaxMemory (virDomainPtr d int rv = -1; remote_domain_set_max_memory_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.memory = memory; @@ -1801,6 +1900,7 @@ remoteDomainSetMaxMemory (virDomainPtr d rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1810,6 +1910,8 @@ remoteDomainSetMemory (virDomainPtr doma int rv = -1; remote_domain_set_memory_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.memory = memory; @@ -1822,6 +1924,7 @@ remoteDomainSetMemory (virDomainPtr doma rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1832,6 +1935,8 @@ remoteDomainGetInfo (virDomainPtr domain remote_domain_get_info_args args; remote_domain_get_info_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -1850,6 +1955,7 @@ remoteDomainGetInfo (virDomainPtr domain rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1859,6 +1965,8 @@ remoteDomainSave (virDomainPtr domain, c int rv = -1; remote_domain_save_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.to = (char *) to; @@ -1871,6 +1979,7 @@ remoteDomainSave (virDomainPtr domain, c rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1880,6 +1989,8 @@ remoteDomainRestore (virConnectPtr conn, int rv = -1; remote_domain_restore_args args; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); args.from = (char *) from; @@ -1891,6 +2002,7 @@ remoteDomainRestore (virConnectPtr conn, rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1900,6 +2012,8 @@ remoteDomainCoreDump (virDomainPtr domai int rv = -1; remote_domain_core_dump_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.to = (char *) to; @@ -1913,6 +2027,7 @@ remoteDomainCoreDump (virDomainPtr domai rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1922,6 +2037,8 @@ remoteDomainSetVcpus (virDomainPtr domai int rv = -1; remote_domain_set_vcpus_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.nvcpus = nvcpus; @@ -1934,6 +2051,7 @@ remoteDomainSetVcpus (virDomainPtr domai rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1947,6 +2065,8 @@ remoteDomainPinVcpu (virDomainPtr domain remote_domain_pin_vcpu_args args; struct private_data *priv = domain->conn->privateData; + remoteDriverLock(priv); + if (maplen > REMOTE_CPUMAP_MAX) { errorf (domain->conn, VIR_ERR_RPC, _("map length greater than maximum: %d > %d"), @@ -1967,6 +2087,7 @@ remoteDomainPinVcpu (virDomainPtr domain rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -1983,6 +2104,8 @@ remoteDomainGetVcpus (virDomainPtr domai remote_domain_get_vcpus_ret ret; struct private_data *priv = domain->conn->privateData; + remoteDriverLock(priv); + if (maxinfo > REMOTE_VCPUINFO_MAX) { errorf (domain->conn, VIR_ERR_RPC, _("vCPU count exceeds maximum: %d > %d"), @@ -2038,6 +2161,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -2048,6 +2172,8 @@ remoteDomainGetMaxVcpus (virDomainPtr do remote_domain_get_max_vcpus_args args; remote_domain_get_max_vcpus_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -2060,6 +2186,7 @@ remoteDomainGetMaxVcpus (virDomainPtr do rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -2070,6 +2197,8 @@ remoteDomainDumpXML (virDomainPtr domain remote_domain_dump_xml_args args; remote_domain_dump_xml_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.flags = flags; @@ -2084,6 +2213,7 @@ remoteDomainDumpXML (virDomainPtr domain rv = ret.xml; done: + remoteDriverUnlock(priv); return rv; } @@ -2099,6 +2229,8 @@ remoteDomainMigratePrepare (virConnectPt remote_domain_migrate_prepare_ret ret; struct private_data *priv = dconn->privateData; + remoteDriverLock(priv); + args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in; args.flags = flags; args.dname = dname == NULL ? NULL : (char **) &dname; @@ -2120,6 +2252,7 @@ remoteDomainMigratePrepare (virConnectPt rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2136,6 +2269,8 @@ remoteDomainMigratePerform (virDomainPtr remote_domain_migrate_perform_args args; struct private_data *priv = domain->conn->privateData; + remoteDriverLock(priv); + make_nonnull_domain (&args.dom, domain); args.cookie.cookie_len = cookielen; args.cookie.cookie_val = (char *) cookie; @@ -2152,6 +2287,7 @@ remoteDomainMigratePerform (virDomainPtr rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2168,6 +2304,8 @@ remoteDomainMigrateFinish (virConnectPtr remote_domain_migrate_finish_ret ret; struct private_data *priv = dconn->privateData; + remoteDriverLock(priv); + args.dname = (char *) dname; args.cookie.cookie_len = cookielen; args.cookie.cookie_val = (char *) cookie; @@ -2184,6 +2322,7 @@ remoteDomainMigrateFinish (virConnectPtr xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return ddom; } @@ -2200,6 +2339,8 @@ remoteDomainMigratePrepare2 (virConnectP remote_domain_migrate_prepare2_ret ret; struct private_data *priv = dconn->privateData; + remoteDriverLock(priv); + args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in; args.flags = flags; args.dname = dname == NULL ? NULL : (char **) &dname; @@ -2222,6 +2363,7 @@ remoteDomainMigratePrepare2 (virConnectP rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2239,6 +2381,8 @@ remoteDomainMigrateFinish2 (virConnectPt remote_domain_migrate_finish2_ret ret; struct private_data *priv = dconn->privateData; + remoteDriverLock(priv); + args.dname = (char *) dname; args.cookie.cookie_len = cookielen; args.cookie.cookie_val = (char *) cookie; @@ -2256,6 +2400,7 @@ remoteDomainMigrateFinish2 (virConnectPt xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish2_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return ddom; } @@ -2267,6 +2412,8 @@ remoteListDefinedDomains (virConnectPtr remote_list_defined_domains_args args; remote_list_defined_domains_ret ret; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); if (maxnames > REMOTE_DOMAIN_NAME_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -2303,6 +2450,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -2313,6 +2461,8 @@ remoteNumOfDefinedDomains (virConnectPtr remote_num_of_defined_domains_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_DOMAINS, (xdrproc_t) xdr_void, (char *) NULL, @@ -2322,6 +2472,7 @@ remoteNumOfDefinedDomains (virConnectPtr rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -2331,6 +2482,8 @@ remoteDomainCreate (virDomainPtr domain) int rv = -1; remote_domain_create_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -2342,6 +2495,7 @@ remoteDomainCreate (virDomainPtr domain) rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2353,6 +2507,8 @@ remoteDomainDefineXML (virConnectPtr con remote_domain_define_xml_ret ret; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + args.xml = (char *) xml; memset (&ret, 0, sizeof ret); @@ -2365,6 +2521,7 @@ remoteDomainDefineXML (virConnectPtr con xdr_free ((xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return dom; } @@ -2374,6 +2531,8 @@ remoteDomainUndefine (virDomainPtr domai int rv = -1; remote_domain_undefine_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -2385,6 +2544,7 @@ remoteDomainUndefine (virDomainPtr domai rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2394,6 +2554,8 @@ remoteDomainAttachDevice (virDomainPtr d int rv = -1; remote_domain_attach_device_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.xml = (char *) xml; @@ -2406,6 +2568,7 @@ remoteDomainAttachDevice (virDomainPtr d rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2415,6 +2578,8 @@ remoteDomainDetachDevice (virDomainPtr d int rv = -1; remote_domain_detach_device_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.xml = (char *) xml; @@ -2427,6 +2592,7 @@ remoteDomainDetachDevice (virDomainPtr d rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2437,6 +2603,8 @@ remoteDomainGetAutostart (virDomainPtr d remote_domain_get_autostart_args args; remote_domain_get_autostart_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -2450,6 +2618,7 @@ remoteDomainGetAutostart (virDomainPtr d rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2459,6 +2628,8 @@ remoteDomainSetAutostart (virDomainPtr d int rv = -1; remote_domain_set_autostart_args args; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.autostart = autostart; @@ -2471,6 +2642,7 @@ remoteDomainSetAutostart (virDomainPtr d rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2481,6 +2653,8 @@ remoteDomainGetSchedulerType (virDomainP remote_domain_get_scheduler_type_args args; remote_domain_get_scheduler_type_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -2496,6 +2670,7 @@ remoteDomainGetSchedulerType (virDomainP rv = ret.type; done: + remoteDriverUnlock(priv); return rv; } @@ -2508,6 +2683,8 @@ remoteDomainGetSchedulerParameters (virD remote_domain_get_scheduler_parameters_ret ret; int i = -1; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.nparams = *nparams; @@ -2565,6 +2742,7 @@ cleanup: } done: + remoteDriverUnlock(priv); return rv; } @@ -2576,6 +2754,8 @@ remoteDomainSetSchedulerParameters (virD remote_domain_set_scheduler_parameters_args args; int i, do_error; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); @@ -2627,6 +2807,7 @@ remoteDomainSetSchedulerParameters (virD rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2638,6 +2819,8 @@ remoteDomainBlockStats (virDomainPtr dom remote_domain_block_stats_args args; remote_domain_block_stats_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.path = (char *) path; @@ -2658,6 +2841,7 @@ remoteDomainBlockStats (virDomainPtr dom rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2669,6 +2853,8 @@ remoteDomainInterfaceStats (virDomainPtr remote_domain_interface_stats_args args; remote_domain_interface_stats_ret ret; struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); make_nonnull_domain (&args.dom, domain); args.path = (char *) path; @@ -2693,6 +2879,7 @@ remoteDomainInterfaceStats (virDomainPtr rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -2709,6 +2896,8 @@ remoteDomainBlockPeek (virDomainPtr doma remote_domain_block_peek_ret ret; struct private_data *priv = domain->conn->privateData; + remoteDriverLock(priv); + if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) { errorf (domain->conn, VIR_ERR_RPC, _("block peek request too large for remote protocol, %zi > %d"), @@ -2743,6 +2932,7 @@ cleanup: free (ret.buffer.buffer_val); done: + remoteDriverUnlock(priv); return rv; } @@ -2758,6 +2948,8 @@ remoteDomainMemoryPeek (virDomainPtr dom remote_domain_memory_peek_ret ret; struct private_data *priv = domain->conn->privateData; + remoteDriverLock(priv); + if (size > REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX) { errorf (domain->conn, VIR_ERR_RPC, _("memory peek request too large for remote protocol, %zi > %d"), @@ -2791,6 +2983,7 @@ cleanup: free (ret.buffer.buffer_val); done: + remoteDriverUnlock(priv); return rv; } @@ -2807,11 +3000,17 @@ remoteNetworkOpen (virConnectPtr conn, if (conn && conn->driver && STREQ (conn->driver->name, "remote")) { - /* If we're here, the remote driver is already + struct private_data *priv; + + /* If we're here, the remote driver is already * in use due to a) a QEMU uri, or b) a remote * URI. So we can re-use existing connection */ - conn->networkPrivateData = conn->privateData; + priv = conn->privateData; + remoteDriverLock(priv); + priv->localUses++; + conn->networkPrivateData = priv; + remoteDriverUnlock(priv); return VIR_DRV_OPEN_SUCCESS; } else { /* Using a non-remote driver, so we need to open a @@ -2823,6 +3022,12 @@ remoteNetworkOpen (virConnectPtr conn, int ret, rflags = 0; if (VIR_ALLOC(priv) < 0) { error (conn, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (virMutexInit(&priv->lock) < 0) { + error(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + VIR_FREE(priv); return VIR_DRV_OPEN_ERROR; } if (flags & VIR_CONNECT_RO) @@ -2848,14 +3053,17 @@ remoteNetworkClose (virConnectPtr conn) int rv = 0; struct private_data *priv = conn->networkPrivateData; - if (priv->localUses) { - priv->localUses--; - if (!priv->localUses) { - rv = doRemoteClose(conn, priv); - VIR_FREE(priv); - conn->networkPrivateData = NULL; - } - } + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + rv = doRemoteClose(conn, priv); + conn->networkPrivateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + } + if (priv) + remoteDriverUnlock(priv); return rv; } @@ -2866,6 +3074,8 @@ remoteNumOfNetworks (virConnectPtr conn) remote_num_of_networks_ret ret; struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_NETWORKS, (xdrproc_t) xdr_void, (char *) NULL, @@ -2875,6 +3085,7 @@ remoteNumOfNetworks (virConnectPtr conn) rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -2886,6 +3097,8 @@ remoteListNetworks (virConnectPtr conn, remote_list_networks_args args; remote_list_networks_ret ret; struct private_data *priv = conn->networkPrivateData; + + remoteDriverLock(priv); if (maxnames > REMOTE_NETWORK_NAME_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -2922,6 +3135,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -2932,6 +3146,8 @@ remoteNumOfDefinedNetworks (virConnectPt remote_num_of_defined_networks_ret ret; struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_NETWORKS, (xdrproc_t) xdr_void, (char *) NULL, @@ -2941,6 +3157,7 @@ remoteNumOfDefinedNetworks (virConnectPt rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -2953,6 +3170,8 @@ remoteListDefinedNetworks (virConnectPtr remote_list_defined_networks_args args; remote_list_defined_networks_ret ret; struct private_data *priv = conn->networkPrivateData; + + remoteDriverLock(priv); if (maxnames > REMOTE_NETWORK_NAME_LIST_MAX) { errorf (conn, VIR_ERR_RPC, @@ -2989,6 +3208,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -3001,6 +3221,8 @@ remoteNetworkLookupByUUID (virConnectPtr remote_network_lookup_by_uuid_ret ret; struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); memset (&ret, 0, sizeof ret); @@ -3013,6 +3235,7 @@ remoteNetworkLookupByUUID (virConnectPtr xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_uuid_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return net; } @@ -3025,6 +3248,8 @@ remoteNetworkLookupByName (virConnectPtr remote_network_lookup_by_name_ret ret; struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + args.name = (char *) name; memset (&ret, 0, sizeof ret); @@ -3037,6 +3262,7 @@ remoteNetworkLookupByName (virConnectPtr xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_name_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return net; } @@ -3048,6 +3274,8 @@ remoteNetworkCreateXML (virConnectPtr co remote_network_create_xml_ret ret; struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + args.xml = (char *) xmlDesc; memset (&ret, 0, sizeof ret); @@ -3060,6 +3288,7 @@ remoteNetworkCreateXML (virConnectPtr co xdr_free ((xdrproc_t) &xdr_remote_network_create_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return net; } @@ -3071,6 +3300,8 @@ remoteNetworkDefineXML (virConnectPtr co remote_network_define_xml_ret ret; struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + args.xml = (char *) xml; memset (&ret, 0, sizeof ret); @@ -3083,6 +3314,7 @@ remoteNetworkDefineXML (virConnectPtr co xdr_free ((xdrproc_t) &xdr_remote_network_define_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return net; } @@ -3092,6 +3324,8 @@ remoteNetworkUndefine (virNetworkPtr net int rv = -1; remote_network_undefine_args args; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); @@ -3103,6 +3337,7 @@ remoteNetworkUndefine (virNetworkPtr net rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3112,6 +3347,8 @@ remoteNetworkCreate (virNetworkPtr netwo int rv = -1; remote_network_create_args args; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); @@ -3123,6 +3360,7 @@ remoteNetworkCreate (virNetworkPtr netwo rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3132,6 +3370,8 @@ remoteNetworkDestroy (virNetworkPtr netw int rv = -1; remote_network_destroy_args args; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); @@ -3143,6 +3383,7 @@ remoteNetworkDestroy (virNetworkPtr netw rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3153,6 +3394,8 @@ remoteNetworkDumpXML (virNetworkPtr netw remote_network_dump_xml_args args; remote_network_dump_xml_ret ret; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); args.flags = flags; @@ -3167,6 +3410,7 @@ remoteNetworkDumpXML (virNetworkPtr netw rv = ret.xml; done: + remoteDriverUnlock(priv); return rv; } @@ -3177,6 +3421,8 @@ remoteNetworkGetBridgeName (virNetworkPt remote_network_get_bridge_name_args args; remote_network_get_bridge_name_ret ret; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); @@ -3190,6 +3436,7 @@ remoteNetworkGetBridgeName (virNetworkPt rv = ret.name; done: + remoteDriverUnlock(priv); return rv; } @@ -3200,6 +3447,8 @@ remoteNetworkGetAutostart (virNetworkPtr remote_network_get_autostart_args args; remote_network_get_autostart_ret ret; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); @@ -3214,6 +3463,7 @@ remoteNetworkGetAutostart (virNetworkPtr rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3223,6 +3473,8 @@ remoteNetworkSetAutostart (virNetworkPtr int rv = -1; remote_network_set_autostart_args args; struct private_data *priv = network->conn->networkPrivateData; + + remoteDriverLock(priv); make_nonnull_network (&args.net, network); args.autostart = autostart; @@ -3235,6 +3487,7 @@ remoteNetworkSetAutostart (virNetworkPtr rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3254,16 +3507,23 @@ remoteStorageOpen (virConnectPtr conn, if (conn && conn->driver && STREQ (conn->driver->name, "remote")) { + struct private_data *priv = conn->privateData; /* If we're here, the remote driver is already * in use due to a) a QEMU uri, or b) a remote * URI. So we can re-use existing connection */ - conn->storagePrivateData = conn->privateData; + remoteDriverLock(priv); + priv->localUses++; + conn->storagePrivateData = priv; + remoteDriverUnlock(priv); return VIR_DRV_OPEN_SUCCESS; } else if (conn->networkDriver && STREQ (conn->networkDriver->name, "remote")) { - conn->storagePrivateData = conn->networkPrivateData; - ((struct private_data *)conn->storagePrivateData)->localUses++; + struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + conn->storagePrivateData = priv; + priv->localUses++; + remoteDriverUnlock(priv); return VIR_DRV_OPEN_SUCCESS; } else { /* Using a non-remote driver, so we need to open a @@ -3275,6 +3535,12 @@ remoteStorageOpen (virConnectPtr conn, int ret, rflags = 0; if (VIR_ALLOC(priv) < 0) { error (NULL, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (virMutexInit(&priv->lock) < 0) { + error(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + VIR_FREE(priv); return VIR_DRV_OPEN_ERROR; } if (flags & VIR_CONNECT_RO) @@ -3300,14 +3566,17 @@ remoteStorageClose (virConnectPtr conn) int ret = 0; struct private_data *priv = conn->storagePrivateData; - if (priv->localUses) { - priv->localUses--; - if (!priv->localUses) { - ret = doRemoteClose(conn, priv); - VIR_FREE(priv); - conn->storagePrivateData = NULL; - } - } + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + conn->storagePrivateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + } + if (priv) + remoteDriverUnlock(priv); return ret; } @@ -3319,6 +3588,8 @@ remoteNumOfStoragePools (virConnectPtr c remote_num_of_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_STORAGE_POOLS, (xdrproc_t) xdr_void, (char *) NULL, @@ -3328,6 +3599,7 @@ remoteNumOfStoragePools (virConnectPtr c rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -3339,6 +3611,8 @@ remoteListStoragePools (virConnectPtr co remote_list_storage_pools_args args; remote_list_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; + + remoteDriverLock(priv); if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); @@ -3371,6 +3645,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_list_storage_pools_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -3381,6 +3656,8 @@ remoteNumOfDefinedStoragePools (virConne remote_num_of_defined_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + memset (&ret, 0, sizeof ret); if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_STORAGE_POOLS, (xdrproc_t) xdr_void, (char *) NULL, @@ -3390,6 +3667,7 @@ remoteNumOfDefinedStoragePools (virConne rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -3402,6 +3680,8 @@ remoteListDefinedStoragePools (virConnec remote_list_defined_storage_pools_args args; remote_list_defined_storage_pools_ret ret; struct private_data *priv = conn->storagePrivateData; + + remoteDriverLock(priv); if (maxnames > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many storage pools requested")); @@ -3434,6 +3714,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_list_defined_storage_pools_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -3448,6 +3729,8 @@ remoteFindStoragePoolSources (virConnect remote_find_storage_pool_sources_ret ret; struct private_data *priv = conn->storagePrivateData; const char *emptyString = ""; + + remoteDriverLock(priv); args.type = (char*)type; /* @@ -3476,6 +3759,7 @@ remoteFindStoragePoolSources (virConnect xdr_free ((xdrproc_t) xdr_remote_find_storage_pool_sources_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -3488,6 +3772,8 @@ remoteStoragePoolLookupByUUID (virConnec remote_storage_pool_lookup_by_uuid_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); memset (&ret, 0, sizeof ret); @@ -3500,6 +3786,7 @@ remoteStoragePoolLookupByUUID (virConnec xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_uuid_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return pool; } @@ -3512,6 +3799,8 @@ remoteStoragePoolLookupByName (virConnec remote_storage_pool_lookup_by_name_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + args.name = (char *) name; memset (&ret, 0, sizeof ret); @@ -3524,6 +3813,7 @@ remoteStoragePoolLookupByName (virConnec xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_name_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return pool; } @@ -3535,6 +3825,8 @@ remoteStoragePoolLookupByVolume (virStor remote_storage_pool_lookup_by_volume_ret ret; struct private_data *priv = vol->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_vol (&args.vol, vol); memset (&ret, 0, sizeof ret); @@ -3547,6 +3839,7 @@ remoteStoragePoolLookupByVolume (virStor xdr_free ((xdrproc_t) &xdr_remote_storage_pool_lookup_by_volume_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return pool; } @@ -3559,6 +3852,8 @@ remoteStoragePoolCreateXML (virConnectPt remote_storage_pool_create_xml_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + args.xml = (char *) xmlDesc; args.flags = flags; @@ -3572,6 +3867,7 @@ remoteStoragePoolCreateXML (virConnectPt xdr_free ((xdrproc_t) &xdr_remote_storage_pool_create_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return pool; } @@ -3583,6 +3879,8 @@ remoteStoragePoolDefineXML (virConnectPt remote_storage_pool_define_xml_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + args.xml = (char *) xml; args.flags = flags; @@ -3596,6 +3894,7 @@ remoteStoragePoolDefineXML (virConnectPt xdr_free ((xdrproc_t) &xdr_remote_storage_pool_define_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return pool; } @@ -3605,6 +3904,8 @@ remoteStoragePoolUndefine (virStoragePoo int rv = -1; remote_storage_pool_undefine_args args; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); @@ -3616,6 +3917,7 @@ remoteStoragePoolUndefine (virStoragePoo rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3625,6 +3927,8 @@ remoteStoragePoolCreate (virStoragePoolP int rv = -1; remote_storage_pool_create_args args; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3637,6 +3941,7 @@ remoteStoragePoolCreate (virStoragePoolP rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3648,6 +3953,8 @@ remoteStoragePoolBuild (virStoragePoolPt remote_storage_pool_build_args args; struct private_data *priv = pool->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3659,6 +3966,7 @@ remoteStoragePoolBuild (virStoragePoolPt rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3668,6 +3976,8 @@ remoteStoragePoolDestroy (virStoragePool int rv = -1; remote_storage_pool_destroy_args args; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); @@ -3679,6 +3989,7 @@ remoteStoragePoolDestroy (virStoragePool rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3690,6 +4001,8 @@ remoteStoragePoolDelete (virStoragePoolP remote_storage_pool_delete_args args; struct private_data *priv = pool->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3701,6 +4014,7 @@ remoteStoragePoolDelete (virStoragePoolP rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3712,6 +4026,8 @@ remoteStoragePoolRefresh (virStoragePool remote_storage_pool_refresh_args args; struct private_data *priv = pool->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3723,6 +4039,7 @@ remoteStoragePoolRefresh (virStoragePool rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3733,6 +4050,8 @@ remoteStoragePoolGetInfo (virStoragePool remote_storage_pool_get_info_args args; remote_storage_pool_get_info_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); @@ -3750,6 +4069,7 @@ remoteStoragePoolGetInfo (virStoragePool rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3761,6 +4081,8 @@ remoteStoragePoolDumpXML (virStoragePool remote_storage_pool_dump_xml_args args; remote_storage_pool_dump_xml_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); args.flags = flags; @@ -3775,6 +4097,7 @@ remoteStoragePoolDumpXML (virStoragePool rv = ret.xml; done: + remoteDriverUnlock(priv); return rv; } @@ -3785,6 +4108,8 @@ remoteStoragePoolGetAutostart (virStorag remote_storage_pool_get_autostart_args args; remote_storage_pool_get_autostart_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); @@ -3799,6 +4124,7 @@ remoteStoragePoolGetAutostart (virStorag rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3808,6 +4134,8 @@ remoteStoragePoolSetAutostart (virStorag int rv = -1; remote_storage_pool_set_autostart_args args; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool (&args.pool, pool); args.autostart = autostart; @@ -3820,6 +4148,7 @@ remoteStoragePoolSetAutostart (virStorag rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -3831,6 +4160,8 @@ remoteStoragePoolNumOfVolumes (virStorag remote_storage_pool_num_of_volumes_args args; remote_storage_pool_num_of_volumes_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); make_nonnull_storage_pool(&args.pool, pool); @@ -3843,6 +4174,7 @@ remoteStoragePoolNumOfVolumes (virStorag rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -3854,6 +4186,8 @@ remoteStoragePoolListVolumes (virStorage remote_storage_pool_list_volumes_args args; remote_storage_pool_list_volumes_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); if (maxnames > REMOTE_STORAGE_VOL_NAME_LIST_MAX) { error (pool->conn, VIR_ERR_RPC, _("too many storage volumes requested")); @@ -3887,6 +4221,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_storage_pool_list_volumes_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -3901,6 +4236,8 @@ remoteStorageVolLookupByName (virStorage remote_storage_vol_lookup_by_name_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_pool(&args.pool, pool); args.name = (char *) name; @@ -3914,6 +4251,7 @@ remoteStorageVolLookupByName (virStorage xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_name_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return vol; } @@ -3926,6 +4264,8 @@ remoteStorageVolLookupByKey (virConnectP remote_storage_vol_lookup_by_key_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + args.key = (char *) key; memset (&ret, 0, sizeof ret); @@ -3938,6 +4278,7 @@ remoteStorageVolLookupByKey (virConnectP xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_key_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return vol; } @@ -3950,6 +4291,8 @@ remoteStorageVolLookupByPath (virConnect remote_storage_vol_lookup_by_path_ret ret; struct private_data *priv = conn->storagePrivateData; + remoteDriverLock(priv); + args.path = (char *) path; memset (&ret, 0, sizeof ret); @@ -3962,6 +4305,7 @@ remoteStorageVolLookupByPath (virConnect xdr_free ((xdrproc_t) &xdr_remote_storage_vol_lookup_by_path_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return vol; } @@ -3974,6 +4318,8 @@ remoteStorageVolCreateXML (virStoragePoo remote_storage_vol_create_xml_ret ret; struct private_data *priv = pool->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_pool (&args.pool, pool); args.xml = (char *) xmlDesc; args.flags = flags; @@ -3988,6 +4334,7 @@ remoteStorageVolCreateXML (virStoragePoo xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return vol; } @@ -3999,6 +4346,8 @@ remoteStorageVolDelete (virStorageVolPtr remote_storage_vol_delete_args args; struct private_data *priv = vol->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_vol (&args.vol, vol); args.flags = flags; @@ -4010,6 +4359,7 @@ remoteStorageVolDelete (virStorageVolPtr rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -4021,6 +4371,8 @@ remoteStorageVolGetInfo (virStorageVolPt remote_storage_vol_get_info_ret ret; struct private_data *priv = vol->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_vol (&args.vol, vol); memset (&ret, 0, sizeof ret); @@ -4036,6 +4388,7 @@ remoteStorageVolGetInfo (virStorageVolPt rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -4048,6 +4401,8 @@ remoteStorageVolDumpXML (virStorageVolPt remote_storage_vol_dump_xml_ret ret; struct private_data *priv = vol->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_vol (&args.vol, vol); args.flags = flags; @@ -4061,6 +4416,7 @@ remoteStorageVolDumpXML (virStorageVolPt rv = ret.xml; done: + remoteDriverUnlock(priv); return rv; } @@ -4072,6 +4428,8 @@ remoteStorageVolGetPath (virStorageVolPt remote_storage_vol_get_path_ret ret; struct private_data *priv = vol->conn->storagePrivateData; + remoteDriverLock(priv); + make_nonnull_storage_vol (&args.vol, vol); memset (&ret, 0, sizeof ret); @@ -4084,6 +4442,7 @@ remoteStorageVolGetPath (virStorageVolPt rv = ret.name; done: + remoteDriverUnlock(priv); return rv; } @@ -4095,19 +4454,63 @@ remoteDevMonOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { + if (inside_daemon) + return VIR_DRV_OPEN_DECLINED; + if (conn && conn->driver && STREQ (conn->driver->name, "remote")) { + struct private_data *priv = conn->privateData; /* If we're here, the remote driver is already * in use due to a) a QEMU uri, or b) a remote * URI. So we can re-use existing connection */ - conn->devMonPrivateData = conn->privateData; + remoteDriverLock(priv); + priv->localUses++; + conn->devMonPrivateData = priv; + remoteDriverUnlock(priv); return VIR_DRV_OPEN_SUCCESS; - } - - /* Decline open. Will fallback to appropriate local node driver. */ - return VIR_DRV_OPEN_DECLINED; + } else if (conn->networkDriver && + STREQ (conn->networkDriver->name, "remote")) { + struct private_data *priv = conn->networkPrivateData; + remoteDriverLock(priv); + conn->devMonPrivateData = priv; + priv->localUses++; + remoteDriverUnlock(priv); + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network APIs. + */ + struct private_data *priv; + int ret, rflags = 0; + if (VIR_ALLOC(priv) < 0) { + error (NULL, VIR_ERR_NO_MEMORY, _("struct private_data")); + return VIR_DRV_OPEN_ERROR; + } + if (virMutexInit(&priv->lock) < 0) { + error(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + VIR_FREE(priv); + return VIR_DRV_OPEN_ERROR; + } + if (flags & VIR_CONNECT_RO) + rflags |= VIR_DRV_OPEN_REMOTE_RO; + rflags |= VIR_DRV_OPEN_REMOTE_UNIX; + + priv->sock = -1; + ret = doRemoteOpen(conn, priv, auth, rflags); + if (ret != VIR_DRV_OPEN_SUCCESS) { + conn->devMonPrivateData = NULL; + VIR_FREE(priv); + } else { + priv->localUses = 1; + conn->devMonPrivateData = priv; + } + return ret; + } } static int remoteDevMonClose(virConnectPtr conn) @@ -4115,14 +4518,17 @@ static int remoteDevMonClose(virConnectP int ret = 0; struct private_data *priv = conn->devMonPrivateData; - if (priv->localUses) { - priv->localUses--; - if (!priv->localUses) { - ret = doRemoteClose(conn, priv); - VIR_FREE(priv); - conn->devMonPrivateData = NULL; - } - } + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + conn->devMonPrivateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + } + if (priv) + remoteDriverUnlock(priv); return ret; } @@ -4135,6 +4541,8 @@ static int remoteNodeNumOfDevices(virCon remote_node_num_of_devices_ret ret; struct private_data *priv = conn->devMonPrivateData; + remoteDriverLock(priv); + args.cap = cap ? (char **)&cap : NULL; args.flags = flags; @@ -4147,6 +4555,7 @@ static int remoteNodeNumOfDevices(virCon rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -4163,6 +4572,8 @@ static int remoteNodeListDevices(virConn remote_node_list_devices_ret ret; struct private_data *priv = conn->devMonPrivateData; + remoteDriverLock(priv); + if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) { error (conn, VIR_ERR_RPC, _("too many device names requested")); goto done; @@ -4196,6 +4607,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -4208,6 +4620,8 @@ static virNodeDevicePtr remoteNodeDevice virNodeDevicePtr dev = NULL; struct private_data *priv = conn->devMonPrivateData; + remoteDriverLock(priv); + args.name = (char *)name; memset (&ret, 0, sizeof ret); @@ -4221,6 +4635,7 @@ static virNodeDevicePtr remoteNodeDevice xdr_free ((xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return dev; } @@ -4232,6 +4647,8 @@ static char *remoteNodeDeviceDumpXML(vir remote_node_device_dump_xml_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; + remoteDriverLock(priv); + args.name = dev->name; args.flags = flags; @@ -4245,6 +4662,7 @@ static char *remoteNodeDeviceDumpXML(vir rv = ret.xml; done: + remoteDriverUnlock(priv); return rv; } @@ -4255,6 +4673,8 @@ static char *remoteNodeDeviceGetParent(v remote_node_device_get_parent_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; + remoteDriverLock(priv); + args.name = dev->name; memset (&ret, 0, sizeof ret); @@ -4267,6 +4687,7 @@ static char *remoteNodeDeviceGetParent(v rv = ret.parent ? *ret.parent : NULL; done: + remoteDriverUnlock(priv); return rv; } @@ -4277,6 +4698,8 @@ static int remoteNodeDeviceNumOfCaps(vir remote_node_device_num_of_caps_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; + remoteDriverLock(priv); + args.name = dev->name; memset (&ret, 0, sizeof ret); @@ -4288,6 +4711,7 @@ static int remoteNodeDeviceNumOfCaps(vir rv = ret.num; done: + remoteDriverUnlock(priv); return rv; } @@ -4301,6 +4725,8 @@ static int remoteNodeDeviceListCaps(virN remote_node_device_list_caps_ret ret; struct private_data *priv = dev->conn->devMonPrivateData; + remoteDriverLock(priv); + if (maxnames > REMOTE_NODE_DEVICE_CAPS_LIST_MAX) { error (dev->conn, VIR_ERR_RPC, _("too many capability names requested")); goto done; @@ -4333,6 +4759,7 @@ cleanup: xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret); done: + remoteDriverUnlock(priv); return rv; } @@ -5058,6 +5485,10 @@ static int remoteDomainEventRegister (vi int rv = -1; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + + remoteDriverLock(priv); + if (priv->eventFlushTimer < 0) { error (conn, VIR_ERR_NO_SUPPORT, _("no event support")); goto done; @@ -5079,6 +5510,7 @@ static int remoteDomainEventRegister (vi rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -5105,6 +5537,7 @@ static int remoteDomainEventDeregister ( rv = 0; done: + remoteDriverUnlock(priv); return rv; } @@ -5895,6 +6328,8 @@ remoteDomainEventFired(int watch, virConnectPtr conn = opaque; struct private_data *priv = conn->privateData; + remoteDriverLock(priv); + DEBUG("Event fired %d %d %d %X", watch, fd, event, event); if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { @@ -5951,7 +6386,7 @@ remoteDomainEventFired(int watch, } done: - return; + remoteDriverUnlock(priv); } void @@ -5959,8 +6394,12 @@ remoteDomainEventQueueFlush(int timer AT { virConnectPtr conn = opaque; struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); virDomainEventQueueDispatch(priv->domainEvents, priv->callbackList, virDomainEventDispatchDefaultFunc, NULL); virEventUpdateTimeout(priv->eventFlushTimer, -1); -} + + remoteDriverUnlock(priv); +} -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:40:18PM +0000, Daniel P. Berrange wrote:
Adds locking in the remote driver. This obtains the lock on every single API method. This is quite poor, not giving any parallelism - that will be added in 2 patch's time, when we drop the lock while waiting on I/O
remote_internal.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 481 insertions(+), 42 deletions(-)
I worry about remoteNetworkOpen, but the code there already is tortuous enough. remoteDevMonOpen is even more horrible, but it appears correct. Guarded +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora

On Tue, Jan 13, 2009 at 05:40:18PM +0000, Daniel P. Berrange wrote:
Adds locking in the remote driver. This obtains the lock on every single API method. This is quite poor, not giving any parallelism - that will be added in 2 patch's time, when we drop the lock while waiting on I/O
remote_internal.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 481 insertions(+), 42 deletions(-)
Comitted this patch now. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

When going into the call() method, we temporarily disable the event wakup callback. The call() method is already processing any async messages which arrive, so letting the main event loop wakup here is inefficient, and indeed will block the main event loop on the mutex while the main thread is in call(). We really don't want that ! remote_internal.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) Daniel diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -753,9 +753,7 @@ doRemoteOpen (virConnectPtr conn, DEBUG0("Adding Handler for remote events"); /* Set up a callback to listen on the socket data */ if ((priv->watch = virEventAddHandle(priv->sock, - VIR_EVENT_HANDLE_READABLE | - VIR_EVENT_HANDLE_ERROR | - VIR_EVENT_HANDLE_HANGUP, + VIR_EVENT_HANDLE_READABLE, remoteDomainEventFired, conn, NULL)) < 0) { DEBUG0("virEventAddHandle failed: No addHandleImpl defined." @@ -5559,11 +5557,11 @@ static int really_read (virConnectPtr co * else Bad Things will happen in the XDR code. */ static int -call (virConnectPtr conn, struct private_data *priv, - int flags /* if we are in virConnectOpen */, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) +doCall (virConnectPtr conn, struct private_data *priv, + int flags /* if we are in virConnectOpen */, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) { char buffer[REMOTE_MESSAGE_MAX]; char buffer2[4]; @@ -5752,6 +5750,34 @@ retry_read: xdr_destroy (&xdr); return -1; } +} + + +static int +call (virConnectPtr conn, struct private_data *priv, + int flags /* if we are in virConnectOpen */, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + int rv; + /* + * Avoid needless wake-ups of the event loop in the + * case where this call is being made from a different + * thread than the event loop. These wake-ups would + * cause the event loop thread to be blocked on the + * mutex for the duration of the call + */ + if (priv->watch >= 0) + virEventUpdateHandle(priv->watch, 0); + + rv = doCall(conn, priv,flags, proc_nr, + args_filter, args, + ret_filter, ret); + + if (priv->watch >= 0) + virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE); + return rv; } static int -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
When going into the call() method, we temporarily disable the event wakup callback. The call() method is already processing any async messages which arrive, so letting the main event loop wakup here is inefficient, and indeed will block the main event loop on the mutex while the main thread is in call(). We really don't want that !
remote_internal.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-)
Daniel
diff --git a/src/remote_internal.c b/src/remote_internal.c ... +doCall (virConnectPtr conn, struct private_data *priv, ... +static int +call (virConnectPtr conn, struct private_data *priv, + int flags /* if we are in virConnectOpen */, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + int rv; + /* + * Avoid needless wake-ups of the event loop in the + * case where this call is being made from a different + * thread than the event loop. These wake-ups would + * cause the event loop thread to be blocked on the + * mutex for the duration of the call + */ + if (priv->watch >= 0) + virEventUpdateHandle(priv->watch, 0); + + rv = doCall(conn, priv,flags, proc_nr, + args_filter, args, + ret_filter, ret); + + if (priv->watch >= 0) + virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE);
Looks fine. ACK. At first I wondered if someday the incoming event-update-handle type might be something other than VIR_EVENT_HANDLE_READABLE, in which case you'd want to save the original before doCall, and restore it afterward. Probably won't happen.

On Thu, Jan 15, 2009 at 03:19:39PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
When going into the call() method, we temporarily disable the event wakup callback. The call() method is already processing any async messages which arrive, so letting the main event loop wakup here is inefficient, and indeed will block the main event loop on the mutex while the main thread is in call(). We really don't want that !
remote_internal.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-)
Daniel
diff --git a/src/remote_internal.c b/src/remote_internal.c ... +doCall (virConnectPtr conn, struct private_data *priv, ... +static int +call (virConnectPtr conn, struct private_data *priv, + int flags /* if we are in virConnectOpen */, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + int rv; + /* + * Avoid needless wake-ups of the event loop in the + * case where this call is being made from a different + * thread than the event loop. These wake-ups would + * cause the event loop thread to be blocked on the + * mutex for the duration of the call + */ + if (priv->watch >= 0) + virEventUpdateHandle(priv->watch, 0); + + rv = doCall(conn, priv,flags, proc_nr, + args_filter, args, + ret_filter, ret); + + if (priv->watch >= 0) + virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE);
Looks fine. ACK.
Comitted this Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Minor problem in the remote driver when lacking support for virExec(), we report an error and then carry on executing anyway. Of course we get a EINVAL error later when we try to write() on a FD of -1. Trivial fix remote_internal.c | 1 + 1 file changed, 1 insertion(+) Daniel diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -690,6 +690,7 @@ doRemoteOpen (virConnectPtr conn, case trans_ext: error (conn, VIR_ERR_INVALID_ARG, _("transport methods unix, ssh and ext are not supported under Windows")); + goto failed; #endif /* WIN32 */ -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
Minor problem in the remote driver when lacking support for virExec(), we report an error and then carry on executing anyway. Of course we get a EINVAL error later when we try to write() on a FD of -1. Trivial fix
remote_internal.c | 1 + 1 file changed, 1 insertion(+)
Daniel
diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -690,6 +690,7 @@ doRemoteOpen (virConnectPtr conn, case trans_ext: error (conn, VIR_ERR_INVALID_ARG, _("transport methods unix, ssh and ext are not supported under Windows")); + goto failed;
ACK. an obvious improvement.

On Thu, Jan 15, 2009 at 03:29:27PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
Minor problem in the remote driver when lacking support for virExec(), we report an error and then carry on executing anyway. Of course we get a EINVAL error later when we try to write() on a FD of -1. Trivial fix
remote_internal.c | 1 + 1 file changed, 1 insertion(+)
Daniel
diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -690,6 +690,7 @@ doRemoteOpen (virConnectPtr conn, case trans_ext: error (conn, VIR_ERR_INVALID_ARG, _("transport methods unix, ssh and ext are not supported under Windows")); + goto failed;
ACK. an obvious improvement.
Comitted this. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:41:15PM +0000, Daniel P. Berrange wrote:
Minor problem in the remote driver when lacking support for virExec(), we report an error and then carry on executing anyway. Of course we get a EINVAL error later when we try to write() on a FD of -1. Trivial fix
remote_internal.c | 1 + 1 file changed, 1 insertion(+)
Daniel
diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -690,6 +690,7 @@ doRemoteOpen (virConnectPtr conn, case trans_ext: error (conn, VIR_ERR_INVALID_ARG, _("transport methods unix, ssh and ext are not supported under Windows")); + goto failed;
#endif /* WIN32 */
+1, obvious. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server. This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there. libvirt_private.syms | 1 remote_internal.c | 1527 ++++++++++++++++++++++++++++++++------------------- util.c | 33 - util.h | 2 4 files changed, 990 insertions(+), 573 deletions(-) Daniel diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -288,6 +288,7 @@ virEventAddHandle; virEventAddHandle; virEventRemoveHandle; virExec; +virSetNonBlock; virFormatMacAddr; virGetHostname; virParseMacAddr; diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -67,6 +67,8 @@ #include <libxml/uri.h> #include <netdb.h> + +#include <poll.h> /* AI_ADDRCONFIG is missing on some systems. */ #ifndef AI_ADDRCONFIG @@ -86,7 +88,43 @@ #include "util.h" #include "event.h" +#ifdef WIN32 +#define pipe(fds) _pipe(fds,4096, _O_BINARY) +#endif + + static int inside_daemon = 0; + +struct remote_thread_call; + + +enum { + REMOTE_MODE_WAIT_TX, + REMOTE_MODE_WAIT_RX, + REMOTE_MODE_COMPLETE, + REMOTE_MODE_ERROR, +}; + +struct remote_thread_call { + int mode; + + /* 4 byte length, followed by RPC message header+body */ + char buffer[4 + REMOTE_MESSAGE_MAX]; + unsigned int bufferLength; + unsigned int bufferOffset; + + unsigned int serial; + unsigned int proc_nr; + + virCond cond; + + xdrproc_t ret_filter; + char *ret; + + remote_error err; + + struct remote_thread_call *next; +}; struct private_data { virMutex lock; @@ -101,12 +139,24 @@ struct private_data { int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ FILE *debugLog; /* Debug remote protocol */ + #if HAVE_SASL sasl_conn_t *saslconn; /* SASL context */ + const char *saslDecoded; unsigned int saslDecodedLength; unsigned int saslDecodedOffset; -#endif + + const char *saslEncoded; + unsigned int saslEncodedLength; + unsigned int saslEncodedOffset; +#endif + + /* 4 byte length, followed by RPC message header+body */ + char buffer[4 + REMOTE_MESSAGE_MAX]; + unsigned int bufferLength; + unsigned int bufferOffset; + /* The list of domain event callbacks */ virDomainEventCallbackListPtr callbackList; /* The queue of domain events generated @@ -114,6 +164,11 @@ struct private_data { virDomainEventQueuePtr domainEvents; /* Timer for flushing domainEvents queue */ int eventFlushTimer; + + /* List of threads currently doing dispatch */ + int wakeupSend; + int wakeupRead; + struct remote_thread_call *waitDispatch; }; enum { @@ -160,7 +215,6 @@ static void make_nonnull_storage_pool (r static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); void remoteDomainEventFired(int watch, int fd, int event, void *data); -static void remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr); static void remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr); void remoteDomainEventQueueFlush(int timer, void *opaque); /*----------------------------------------------------------------------*/ @@ -274,6 +328,7 @@ doRemoteOpen (virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags) { + int wakeup[2]; char *transport_str = NULL; if (conn->uri) { @@ -696,6 +751,21 @@ doRemoteOpen (virConnectPtr conn, } /* switch (transport) */ + if (virSetNonBlock(priv->sock) < 0) { + errorf (conn, VIR_ERR_SYSTEM_ERROR, + _("unable to make socket non-blocking %s"), + strerror(errno)); + goto failed; + } + + if (pipe(wakeup) < 0) { + errorf (conn, VIR_ERR_SYSTEM_ERROR, + _("unable to make pipe %s"), + strerror(errno)); + goto failed; + } + priv->wakeupRead = wakeup[0]; + priv->wakeupSend = wakeup[1]; /* Try and authenticate with server */ if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1) @@ -768,6 +838,7 @@ doRemoteOpen (virConnectPtr conn, DEBUG0("virEventAddTimeout failed: No addTimeoutImpl defined. " "continuing without events."); virEventRemoveHandle(priv->watch); + priv->watch = -1; } } /* Successful. */ @@ -848,6 +919,7 @@ remoteOpen (virConnectPtr conn, } remoteDriverLock(priv); priv->localUses = 1; + priv->watch = -1; if (flags & VIR_CONNECT_RO) rflags |= VIR_DRV_OPEN_REMOTE_RO; @@ -1220,6 +1292,7 @@ doRemoteClose (virConnectPtr conn, struc virEventRemoveTimeout(priv->eventFlushTimer); /* Remove handle for remote events */ virEventRemoveHandle(priv->watch); + priv->watch = -1; } /* Close socket. */ @@ -5542,12 +5615,658 @@ done: /*----------------------------------------------------------------------*/ -static int really_write (virConnectPtr conn, struct private_data *priv, - int in_open, char *bytes, int len); -static int really_read (virConnectPtr conn, struct private_data *priv, - int in_open, char *bytes, int len); - -/* This function performs a remote procedure call to procedure PROC_NR. + +static struct remote_thread_call * +prepareCall(virConnectPtr conn, + struct private_data *priv, + int flags, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + XDR xdr; + struct remote_message_header hdr; + struct remote_thread_call *rv; + + if (VIR_ALLOC(rv) < 0) + return NULL; + + if (virCondInit(&rv->cond) < 0) { + VIR_FREE(rv); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + return NULL; + } + + /* Get a unique serial number for this message. */ + rv->serial = priv->counter++; + rv->proc_nr = proc_nr; + rv->ret_filter = ret_filter; + rv->ret = ret; + + hdr.prog = REMOTE_PROGRAM; + hdr.vers = REMOTE_PROTOCOL_VERSION; + hdr.proc = proc_nr; + hdr.direction = REMOTE_CALL; + hdr.serial = rv->serial; + hdr.status = REMOTE_OK; + + /* Serialise header followed by args. */ + xdrmem_create (&xdr, rv->buffer+4, REMOTE_MESSAGE_MAX, XDR_ENCODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_RPC, _("xdr_remote_message_header failed")); + goto error; + } + + if (!(*args_filter) (&xdr, args)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("marshalling args")); + goto error; + } + + /* Get the length stored in buffer. */ + rv->bufferLength = xdr_getpos (&xdr); + xdr_destroy (&xdr); + + /* Length must include the length word itself (always encoded in + * 4 bytes as per RFC 4506). + */ + rv->bufferLength += 4; + + /* Encode the length word. */ + xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); + if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("xdr_int (length word)")); + goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL; +} + + + +static int +processCallWrite(virConnectPtr conn, + struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + const char *bytes, int len) +{ + int ret; + + if (priv->uses_tls) { + tls_resend: + ret = gnutls_record_send (priv->session, bytes, len); + if (ret < 0) { + if (ret == GNUTLS_E_INTERRUPTED) + goto tls_resend; + if (ret == GNUTLS_E_AGAIN) + return 0; + + error (in_open ? NULL : conn, + VIR_ERR_GNUTLS_ERROR, gnutls_strerror (ret)); + return -1; + } + } else { + resend: + ret = send (priv->sock, bytes, len, 0); + if (ret == -1) { + if (errno == EINTR) + goto resend; + if (errno == EWOULDBLOCK) + return 0; + + error (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, strerror (errno)); + return -1; + + } + } + + return ret; +} + + +static int +processCallRead(virConnectPtr conn, + struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + int ret; + + if (priv->uses_tls) { + tls_resend: + ret = gnutls_record_recv (priv->session, bytes, len); + if (ret == GNUTLS_E_INTERRUPTED) + goto tls_resend; + if (ret == GNUTLS_E_AGAIN) + return 0; + + /* Treat 0 == EOF as an error */ + if (ret <= 0) { + if (ret < 0) + errorf (in_open ? NULL : conn, + VIR_ERR_GNUTLS_ERROR, + _("failed to read from TLS socket %s"), + gnutls_strerror (ret)); + else + errorf (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, + "%s", _("server closed connection")); + return -1; + } + } else { + resend: + ret = recv (priv->sock, bytes, len, 0); + if (ret <= 0) { + if (ret == -1) { + if (errno == EINTR) + goto resend; + if (errno == EWOULDBLOCK) + return 0; + + errorf (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, + _("failed to read from socket %s"), + strerror (errno)); + } else { + errorf (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, + "%s", _("server closed connection")); + } + return -1; + } + } + + return ret; +} + + +static int +processCallSendOne(virConnectPtr conn, + struct private_data *priv, + int in_open, + struct remote_thread_call *thecall) +{ +#if HAVE_SASL + if (priv->saslconn) { + const char *output; + unsigned int outputlen; + int err, ret; + + if (!priv->saslEncoded) { + err = sasl_encode(priv->saslconn, + thecall->buffer + thecall->bufferOffset, + thecall->bufferLength - thecall->bufferOffset, + &output, &outputlen); + if (err != SASL_OK) { + return -1; + } + priv->saslEncoded = output; + priv->saslEncodedLength = outputlen; + priv->saslEncodedOffset = 0; + + thecall->bufferOffset = thecall->bufferLength; + } + + ret = processCallWrite(conn, priv, in_open, + priv->saslEncoded + priv->saslEncodedOffset, + priv->saslEncodedLength - priv->saslEncodedOffset); + if (ret < 0) + return ret; + priv->saslEncodedOffset += ret; + + if (priv->saslEncodedOffset == priv->saslEncodedLength) { + priv->saslEncoded = NULL; + priv->saslEncodedOffset = priv->saslEncodedLength = 0; + thecall->mode = REMOTE_MODE_WAIT_RX; + } + } else { +#endif + int ret; + ret = processCallWrite(conn, priv, in_open, + thecall->buffer + thecall->bufferOffset, + thecall->bufferLength - thecall->bufferOffset); + if (ret < 0) + return ret; + thecall->bufferOffset += ret; + + if (thecall->bufferOffset == thecall->bufferLength) { + thecall->bufferOffset = thecall->bufferLength = 0; + thecall->mode = REMOTE_MODE_WAIT_RX; + } +#if HAVE_SASL + } +#endif + return 0; +} + + +static int +processCallSend(virConnectPtr conn, struct private_data *priv, + int in_open) { + struct remote_thread_call *thecall = priv->waitDispatch; + + while (thecall && + thecall->mode != REMOTE_MODE_WAIT_TX) + thecall = thecall->next; + + if (!thecall) + return -1; /* Shouldn't happen, but you never know... */ + + while (thecall) { + int ret = processCallSendOne(conn, priv, in_open, thecall); + if (ret < 0) + return ret; + + if (thecall->mode == REMOTE_MODE_WAIT_TX) + return 0; /* Blocking write, to back to event loop */ + + thecall = thecall->next; + } + + return 0; /* No more calls to send, all done */ +} + +static int +processCallRecvSome(virConnectPtr conn, struct private_data *priv, + int in_open) { + unsigned int wantData; + + /* Start by reading length word */ + if (priv->bufferLength == 0) + priv->bufferLength = 4; + + wantData = priv->bufferLength - priv->bufferOffset; + +#if HAVE_SASL + if (priv->saslconn) { + if (priv->saslDecoded == NULL) { + char encoded[8192]; + unsigned int encodedLen = sizeof(encoded); + int ret, err; + ret = processCallRead(conn, priv, in_open, + encoded, encodedLen); + if (ret < 0) + return -1; + if (ret == 0) + return 0; + + err = sasl_decode(priv->saslconn, encoded, ret, + &priv->saslDecoded, &priv->saslDecodedLength); + if (ret != SASL_OK) + return -1; + priv->saslDecodedOffset = 0; + } + + if ((priv->saslDecodedLength - priv->saslDecodedOffset) < wantData) + wantData = (priv->saslDecodedLength - priv->saslDecodedOffset); + + memcpy(priv->buffer + priv->bufferOffset, + priv->saslDecoded + priv->saslDecodedOffset, + wantData); + priv->saslDecodedOffset += wantData; + priv->bufferOffset += wantData; + if (priv->saslDecodedOffset == priv->saslDecodedLength) { + priv->saslDecodedLength = priv->saslDecodedLength = 0; + priv->saslDecoded = NULL; + } + + return wantData; + } else { +#endif + int ret; + + ret = processCallRead(conn, priv, in_open, + priv->buffer + priv->bufferOffset, + wantData); + if (ret < 0) + return -1; + if (ret == 0) + return 0; + + priv->bufferOffset += ret; + + return ret; +#if HAVE_SASL + } +#endif +} + + +static void +processCallAsyncEvent(virConnectPtr conn, struct private_data *priv, + int in_open, + remote_message_header *hdr, + XDR *xdr) { + /* An async message has come in while we were waiting for the + * response. Process it to pull it off the wire, and try again + */ + DEBUG0("Encountered an event while waiting for a response"); + + if (in_open) { + DEBUG("Ignoring bogus event %d received while in open", hdr->proc); + return; + } + + if (hdr->proc == REMOTE_PROC_DOMAIN_EVENT) { + remoteDomainQueueEvent(conn, xdr); + virEventUpdateTimeout(priv->eventFlushTimer, 0); + } else { + DEBUG("Unexpected event proc %d", hdr->proc); + } +} + +static int +processCallRecvLen(virConnectPtr conn, struct private_data *priv, + int in_open) { + XDR xdr; + int len; + + xdrmem_create (&xdr, priv->buffer, priv->bufferLength, XDR_DECODE); + if (!xdr_int (&xdr, &len)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("xdr_int (length word, reply)")); + return -1; + } + xdr_destroy (&xdr); + + /* Length includes length word - adjust to real length to read. */ + len -= 4; + + if (len < 0 || len > REMOTE_MESSAGE_MAX) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("packet received from server too large")); + return -1; + } + + /* Extend our declared buffer length and carry + on reading the header + payload */ + priv->bufferLength += len; + DEBUG("Got length, now need %d total (%d more)", priv->bufferLength, len); + return 0; +} + + +static int +processCallRecvMsg(virConnectPtr conn, struct private_data *priv, + int in_open) { + XDR xdr; + struct remote_message_header hdr; + int len = priv->bufferLength - 4; + struct remote_thread_call *thecall; + + /* Deserialise reply header. */ + xdrmem_create (&xdr, priv->buffer + 4, len, XDR_DECODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("invalid header in reply")); + return -1; + } + + /* Check program, version, etc. are what we expect. */ + if (hdr.prog != REMOTE_PROGRAM) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown program (received %x, expected %x)"), + hdr.prog, REMOTE_PROGRAM); + return -1; + } + if (hdr.vers != REMOTE_PROTOCOL_VERSION) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown protocol version (received %x, expected %x)"), + hdr.vers, REMOTE_PROTOCOL_VERSION); + return -1; + } + + /* Async events from server need special handling */ + if (hdr.direction == REMOTE_MESSAGE) { + processCallAsyncEvent(conn, priv, in_open, + &hdr, &xdr); + xdr_destroy(&xdr); + return 0; + } + + if (hdr.direction != REMOTE_REPLY) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("got unexpected RPC call %d from server"), + hdr.proc); + xdr_destroy(&xdr); + return -1; + } + + /* Ok, definitely got an RPC reply now find + out who's been waiting for it */ + + thecall = priv->waitDispatch; + while (thecall && + thecall->serial != hdr.serial) + thecall = thecall->next; + + if (!thecall) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("no call waiting for reply with serial %d"), + hdr.serial); + xdr_destroy(&xdr); + return -1; + } + + if (hdr.proc != thecall->proc_nr) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown procedure (received %x, expected %x)"), + hdr.proc, thecall->proc_nr); + xdr_destroy (&xdr); + return -1; + } + + /* Status is either REMOTE_OK (meaning that what follows is a ret + * structure), or REMOTE_ERROR (and what follows is a remote_error + * structure). + */ + switch (hdr.status) { + case REMOTE_OK: + if (!(*thecall->ret_filter) (&xdr, thecall->ret)) { + error (in_open ? NULL : conn, VIR_ERR_RPC, + _("unmarshalling ret")); + return -1; + } + thecall->mode = REMOTE_MODE_COMPLETE; + xdr_destroy (&xdr); + return 0; + + case REMOTE_ERROR: + memset (&thecall->err, 0, sizeof thecall->err); + if (!xdr_remote_error (&xdr, &thecall->err)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("unmarshalling remote_error")); + return -1; + } + xdr_destroy (&xdr); + thecall->mode = REMOTE_MODE_ERROR; + return 0; + + default: + virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown status (received %x)"), + hdr.status); + xdr_destroy (&xdr); + return -1; + } +} + + +static int +processCallRecv(virConnectPtr conn, struct private_data *priv, + int in_open) { + int ret; + + /* Read as much data as is available, until we get + * EGAIN + */ + for (;;) { + DEBUG("Do %d %d", priv->bufferLength, priv->bufferOffset); + ret = processCallRecvSome(conn, priv, in_open); + DEBUG("Got %d\n", ret); + if (ret < 0) + return -1; + if (ret == 0) + return 0; /* Blocking on read */ + + /* Check for completion of our goal */ + if (priv->bufferOffset == priv->bufferLength) { + if (priv->bufferOffset == 4) { + ret = processCallRecvLen(conn, priv, in_open); + } else { + ret = processCallRecvMsg(conn, priv, in_open); + priv->bufferOffset = priv->bufferLength = 0; + } + if (ret < 0) + return -1; + } + } +} + +/* + * Process all calls pending dispatch/receive until we + * get a reply to our own call. Then quit and pass the buck + * to someone else. + */ +static int +processCalls(virConnectPtr conn, + struct private_data *priv, + int in_open, + struct remote_thread_call *thiscall) +{ + struct pollfd fds[2]; + int ret; + + /* XXX weeeeeeeendows hate, perhaps gnulib poll() will work ? */ + + fds[0].fd = priv->sock; + fds[1].fd = priv->wakeupRead; + + for (;;) { + struct remote_thread_call *tmp = priv->waitDispatch; + struct remote_thread_call *prev; + char ignore; + + fds[0].events = fds[0].revents = 0; + fds[1].events = fds[1].revents = 0; + + fds[1].events = POLLIN; + while (tmp) { + if (tmp->mode == REMOTE_MODE_WAIT_RX) + fds[0].events |= POLLIN; + if (tmp->mode == REMOTE_MODE_WAIT_TX) + fds[0].events |= POLLOUT; + + tmp = tmp->next; + } + + /* Release lock while poll'ing so other threads + * can stuff themselves on the queue */ + remoteDriverUnlock(priv); + + repoll: + ret = poll(fds, ARRAY_CARDINALITY(fds), -1); + if (ret < 0 && errno == EINTR) + goto repoll; + remoteDriverLock(priv); + + if (fds[1].revents) { + DEBUG0("Woken up from poll by other thread"); + saferead(priv->wakeupRead, &ignore, sizeof(ignore)); + } + + if (ret < 0) { + if (errno == EWOULDBLOCK) + continue; + DEBUG("Poll unexpectedly failed %d\n", errno); + return -1; + } + + if (fds[0].revents & POLLOUT) { + if (processCallSend(conn, priv, in_open) < 0) + return -1; + } + + if (fds[0].revents & POLLIN) { + if (processCallRecv(conn, priv, in_open) < 0) + return -1; + } + + /* Iterate through waiting threads and if + * any are complete then tell 'em to wakeup + */ + tmp = priv->waitDispatch; + prev = NULL; + while (tmp) { + if (tmp != thiscall && + (tmp->mode == REMOTE_MODE_COMPLETE || + tmp->mode == REMOTE_MODE_ERROR)) { + /* Take them out of the list */ + if (prev) + prev->next = tmp->next; + else + priv->waitDispatch = tmp->next; + + /* And wake them up.... + * ...they won't actually wakeup until + * we release our mutex a short while + * later... + */ + DEBUG("Waking up sleep %d %p %p", tmp->proc_nr, tmp, priv->waitDispatch); + virCondSignal(&tmp->cond); + } + prev = tmp; + tmp = tmp->next; + } + + /* Now see if *we* are done */ + if (thiscall->mode == REMOTE_MODE_COMPLETE || + thiscall->mode == REMOTE_MODE_ERROR) { + /* We're at head of the list already, so + * remove us + */ + priv->waitDispatch = thiscall->next; + DEBUG("Giving up the buck %d %p %p", thiscall->proc_nr, thiscall, priv->waitDispatch); + /* See if someone else is still waiting + * and if so, then pass the buck ! */ + if (priv->waitDispatch) { + DEBUG("Passing the buck to %d %p", priv->waitDispatch->proc_nr, priv->waitDispatch); + virCondSignal(&priv->waitDispatch->cond); + } + return 0; + } + + + if (fds[0].revents & (POLLHUP | POLLERR)) { + DEBUG0("Got poll hangup/error"); + return -1; + } + } +} + +/* + * This function performs a remote procedure call to procedure PROC_NR. * * NB. This does not free the args structure (not desirable, since you * often want this allocated on the stack or else it contains strings @@ -5556,204 +6275,29 @@ static int really_read (virConnectPtr co * * NB(2). Make sure to memset (&ret, 0, sizeof ret) before calling, * else Bad Things will happen in the XDR code. - */ -static int -doCall (virConnectPtr conn, struct private_data *priv, - int flags /* if we are in virConnectOpen */, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) -{ - char buffer[REMOTE_MESSAGE_MAX]; - char buffer2[4]; - struct remote_message_header hdr; - XDR xdr; - int len; - struct remote_error rerror; - - /* Get a unique serial number for this message. */ - int serial = priv->counter++; - - hdr.prog = REMOTE_PROGRAM; - hdr.vers = REMOTE_PROTOCOL_VERSION; - hdr.proc = proc_nr; - hdr.direction = REMOTE_CALL; - hdr.serial = serial; - hdr.status = REMOTE_OK; - - /* Serialise header followed by args. */ - xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("xdr_remote_message_header failed")); - return -1; - } - - if (!(*args_filter) (&xdr, args)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("marshalling args")); - return -1; - } - - /* Get the length stored in buffer. */ - len = xdr_getpos (&xdr); - xdr_destroy (&xdr); - - /* Length must include the length word itself (always encoded in - * 4 bytes as per RFC 4506). - */ - len += 4; - - /* Encode the length word. */ - xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_ENCODE); - if (!xdr_int (&xdr, &len)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("xdr_int (length word)")); - return -1; - } - xdr_destroy (&xdr); - - /* Send length word followed by header+args. */ - if (really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1 || - really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len-4) == -1) - return -1; - -retry_read: - /* Read and deserialise length word. */ - if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1) - return -1; - - xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); - if (!xdr_int (&xdr, &len)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("xdr_int (length word, reply)")); - return -1; - } - xdr_destroy (&xdr); - - /* Length includes length word - adjust to real length to read. */ - len -= 4; - - if (len < 0 || len > REMOTE_MESSAGE_MAX) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("packet received from server too large")); - return -1; - } - - /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len) == -1) - return -1; - - /* Deserialise reply header. */ - xdrmem_create (&xdr, buffer, len, XDR_DECODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("invalid header in reply")); - return -1; - } - - /* Check program, version, etc. are what we expect. */ - if (hdr.prog != REMOTE_PROGRAM) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown program (received %x, expected %x)"), - hdr.prog, REMOTE_PROGRAM); - return -1; - } - if (hdr.vers != REMOTE_PROTOCOL_VERSION) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown protocol version (received %x, expected %x)"), - hdr.vers, REMOTE_PROTOCOL_VERSION); - return -1; - } - - if (hdr.proc == REMOTE_PROC_DOMAIN_EVENT && - hdr.direction == REMOTE_MESSAGE) { - /* An async message has come in while we were waiting for the - * response. Process it to pull it off the wire, and try again - */ - DEBUG0("Encountered an event while waiting for a response"); - - remoteDomainQueueEvent(conn, &xdr); - virEventUpdateTimeout(priv->eventFlushTimer, 0); - - DEBUG0("Retrying read"); - xdr_destroy (&xdr); - goto retry_read; - } - if (hdr.proc != proc_nr) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown procedure (received %x, expected %x)"), - hdr.proc, proc_nr); - return -1; - } - if (hdr.direction != REMOTE_REPLY) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown direction (received %x, expected %x)"), - hdr.direction, REMOTE_REPLY); - return -1; - } - if (hdr.serial != serial) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown serial (received %x, expected %x)"), - hdr.serial, serial); - return -1; - } - - /* Status is either REMOTE_OK (meaning that what follows is a ret - * structure), or REMOTE_ERROR (and what follows is a remote_error - * structure). - */ - switch (hdr.status) { - case REMOTE_OK: - if (!(*ret_filter) (&xdr, ret)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("unmarshalling ret")); - return -1; - } - xdr_destroy (&xdr); - return 0; - - case REMOTE_ERROR: - memset (&rerror, 0, sizeof rerror); - if (!xdr_remote_error (&xdr, &rerror)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("unmarshalling remote_error")); - return -1; - } - xdr_destroy (&xdr); - /* See if caller asked us to keep quiet about missing RPCs - * eg for interop with older servers */ - if (flags & REMOTE_CALL_QUIET_MISSING_RPC && - rerror.domain == VIR_FROM_REMOTE && - rerror.code == VIR_ERR_RPC && - rerror.level == VIR_ERR_ERROR && - STRPREFIX(*rerror.message, "unknown procedure")) { - return -2; - } - server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, &rerror); - xdr_free ((xdrproc_t) xdr_remote_error, (char *) &rerror); - return -1; - - default: - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown status (received %x)"), - hdr.status); - xdr_destroy (&xdr); - return -1; - } -} - - + * + * NB(3) You must have the private_data lock before calling this + * + * NB(4) This is very complicated. Due to connection cloning, multiple + * threads can want to use the socket at once. Obviously only one of + * them can. So if someone's using the socket, other threads are put + * to sleep on condition variables. THe existing thread may completely + * send & receive their RPC call/reply while they're asleep. Or it + * may only get around to dealing with sending the call. Or it may + * get around to neither. So upon waking up from slumber, the other + * thread may or may not have more work todo. + * + * We call this dance 'passing the buck' + * + * http://en.wikipedia.org/wiki/Passing_the_buck + * + * "Buck passing or passing the buck is the action of transferring + * responsibility or blame unto another person. It is also used as + * a strategy in power politics when the actions of one country/ + * nation are blamed on another, providing an opportunity for war." + * + * NB(5) Don't Panic! + */ static int call (virConnectPtr conn, struct private_data *priv, int flags /* if we are in virConnectOpen */, @@ -5762,6 +6306,84 @@ call (virConnectPtr conn, struct private xdrproc_t ret_filter, char *ret) { int rv; + struct remote_thread_call *thiscall; + + DEBUG("Doing call %d %p", proc_nr, priv->waitDispatch); + thiscall = prepareCall(conn, priv, flags, proc_nr, + args_filter, args, + ret_filter, ret); + + if (!thiscall) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_NO_MEMORY, NULL); + return -1; + } + + /* Check to see if another thread is dispatching */ + if (priv->waitDispatch) { + /* Stick ourselves on the end of the wait queue */ + struct remote_thread_call *tmp = priv->waitDispatch; + char ignore = 1; + while (tmp && tmp->next) + tmp = tmp->next; + if (tmp) + tmp->next = thiscall; + else + priv->waitDispatch = thiscall; + + /* Force other thread to wakup from poll */ + safewrite(priv->wakeupSend, &ignore, sizeof(ignore)); + + DEBUG("Going to sleep %d %p %p", proc_nr, priv->waitDispatch, thiscall); + /* Go to sleep while other thread is working... */ + if (virCondWait(&thiscall->cond, &priv->lock) < 0) { + if (priv->waitDispatch == thiscall) { + priv->waitDispatch = thiscall->next; + } else { + tmp = priv->waitDispatch; + while (tmp && tmp->next && + tmp->next != thiscall) { + tmp = tmp->next; + } + if (tmp && tmp->next == thiscall) + tmp->next = thiscall->next; + } + VIR_FREE(thiscall); + return -1; + } + + DEBUG("Wokeup from sleep %d %p %p", proc_nr, priv->waitDispatch, thiscall); + /* Two reasons we can be woken up + * 1. Other thread has got our reply ready for us + * 2. Other thread is all done, and its out turn to + * be the dispatcher to finish waiting for + * out reply + */ + if (thiscall->mode == REMOTE_MODE_COMPLETE || + thiscall->mode == REMOTE_MODE_ERROR) { + /* + * We avoided catching the buck and our reply is ready ! + * We've already had 'thiscall' removed from the list + * so just need to (maybe) handle errors & free it + */ + goto cleanup; + } + + /* Grr, someone passed the buck onto us ... */ + + } else { + /* We're first to catch the buck */ + priv->waitDispatch = thiscall; + } + + DEBUG("We have the buck %d %p %p", proc_nr, priv->waitDispatch, thiscall); + /* + * The buck stops here! + * + * At this point we're about to own the dispatch + * process... + */ + /* * Avoid needless wake-ups of the event loop in the * case where this call is being made from a different @@ -5772,207 +6394,146 @@ call (virConnectPtr conn, struct private if (priv->watch >= 0) virEventUpdateHandle(priv->watch, 0); - rv = doCall(conn, priv,flags, proc_nr, - args_filter, args, - ret_filter, ret); + rv = processCalls(conn, priv, + flags & REMOTE_CALL_IN_OPEN ? 1 : 0, + thiscall); if (priv->watch >= 0) virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE); - return rv; -} - -static int -really_write_buf (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - const char *bytes, int len) -{ - const char *p; - int err; - - p = bytes; - if (priv->uses_tls) { - do { - err = gnutls_record_send (priv->session, p, len); - if (err < 0) { - if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } else { - do { - err = send (priv->sock, p, len, 0); - if (err == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } - - return 0; -} - -static int -really_write_plain (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - return really_write_buf(conn, priv, in_open, bytes, len); -} - -#if HAVE_SASL -static int -really_write_sasl (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - const char *output; - unsigned int outputlen; - int err; - - err = sasl_encode(priv->saslconn, bytes, len, &output, &outputlen); - if (err != SASL_OK) { - return -1; - } - - return really_write_buf(conn, priv, in_open, output, outputlen); -} -#endif - -static int -really_write (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ -#if HAVE_SASL - if (priv->saslconn) - return really_write_sasl(conn, priv, in_open, bytes, len); - else -#endif - return really_write_plain(conn, priv, in_open, bytes, len); -} - -static int -really_read_buf (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - int err; - - if (priv->uses_tls) { - tlsreread: - err = gnutls_record_recv (priv->session, bytes, len); - if (err < 0) { - if (err == GNUTLS_E_INTERRUPTED) - goto tlsreread; - error (in_open ? NULL : conn, - VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, _("socket closed unexpectedly")); - return -1; - } - } else { - reread: - err = recv (priv->sock, bytes, len, 0); - if (err == -1) { - if (errno == EINTR) - goto reread; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, _("socket closed unexpectedly")); - return -1; - } - } - - return err; -} - -static int -really_read_plain (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - do { - int ret = really_read_buf (conn, priv, in_open, bytes, len); - if (ret < 0) - return -1; - - len -= ret; - bytes += ret; - } while (len > 0); - - return 0; -} - -#if HAVE_SASL -static int -really_read_sasl (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - do { - int want, got; - if (priv->saslDecoded == NULL) { - char encoded[8192]; - int encodedLen = sizeof(encoded); - int err, ret; - ret = really_read_buf (conn, priv, in_open, encoded, encodedLen); - if (ret < 0) - return -1; - - err = sasl_decode(priv->saslconn, encoded, ret, - &priv->saslDecoded, &priv->saslDecodedLength); - } - - got = priv->saslDecodedLength - priv->saslDecodedOffset; - want = len; - if (want > got) - want = got; - - memcpy(bytes, priv->saslDecoded + priv->saslDecodedOffset, want); - priv->saslDecodedOffset += want; - if (priv->saslDecodedOffset == priv->saslDecodedLength) { - priv->saslDecoded = NULL; - priv->saslDecodedOffset = priv->saslDecodedLength = 0; - } - bytes += want; - len -= want; - } while (len > 0); - - return 0; -} -#endif - -static int -really_read (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ -#if HAVE_SASL - if (priv->saslconn) - return really_read_sasl (conn, priv, in_open, bytes, len); - else -#endif - return really_read_plain (conn, priv, in_open, bytes, len); -} + + if (rv < 0) { + VIR_FREE(thiscall); + return -1; + } + +cleanup: + DEBUG("All done with our call %d %p %p", proc_nr, priv->waitDispatch, thiscall); + if (thiscall->mode == REMOTE_MODE_ERROR) { + /* See if caller asked us to keep quiet about missing RPCs + * eg for interop with older servers */ + if (flags & REMOTE_CALL_QUIET_MISSING_RPC && + thiscall->err.domain == VIR_FROM_REMOTE && + thiscall->err.code == VIR_ERR_RPC && + thiscall->err.level == VIR_ERR_ERROR && + STRPREFIX(*thiscall->err.message, "unknown procedure")) { + rv = -2; + } else { + server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + &thiscall->err); + rv = -1; + } + } else { + rv = 0; + } + VIR_FREE(thiscall); + return rv; +} + +/** + * remoteDomainReadEvent + * + * Read the event data off the wire + */ +static virDomainEventPtr +remoteDomainReadEvent(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_ret ret; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&ret, 0, sizeof ret); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_ret(xdr, &ret) ) { + error (conn, VIR_ERR_RPC, + _("remoteDomainProcessEvent: unmarshalling ret")); + return NULL; + } + + dom = get_nonnull_domain(conn,ret.dom); + if (!dom) + return NULL; + + event = virDomainEventNewFromDom(dom, ret.event, ret.detail); + + virDomainFree(dom); + return event; +} + +static void +remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr) +{ + struct private_data *priv = conn->privateData; + virDomainEventPtr event; + + event = remoteDomainReadEvent(conn, xdr); + if (!event) + return; + + if (virDomainEventQueuePush(priv->domainEvents, + event) < 0) { + DEBUG0("Error adding event to queue"); + virDomainEventFree(event); + } +} + +/** remoteDomainEventFired: + * + * The callback for monitoring the remote socket + * for event data + */ +void +remoteDomainEventFired(int watch, + int fd, + int event, + void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + /* This should be impossible, but it doesn't hurt to check */ + if (priv->waitDispatch) + goto done; + + DEBUG("Event fired %d %d %d %X", watch, fd, event, event); + + if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { + DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " + "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); + virEventRemoveHandle(watch); + priv->watch = -1; + goto done; + } + + if (fd != priv->sock) { + virEventRemoveHandle(watch); + priv->watch = -1; + goto done; + } + + if (processCallRecv(conn, priv, 0) < 0) + DEBUG0("Something went wrong during async message processing"); + +done: + remoteDriverUnlock(priv); +} + +void +remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + virDomainEventQueueDispatch(priv->domainEvents, priv->callbackList, + virDomainEventDispatchDefaultFunc, NULL); + virEventUpdateTimeout(priv->eventFlushTimer, -1); + + remoteDriverUnlock(priv); +} + /* For errors internal to this library. */ static void @@ -6272,161 +6833,3 @@ remoteRegister (void) return 0; } -/** - * remoteDomainReadEvent - * - * Read the event data off the wire - */ -static virDomainEventPtr -remoteDomainReadEvent(virConnectPtr conn, XDR *xdr) -{ - remote_domain_event_ret ret; - virDomainPtr dom; - virDomainEventPtr event = NULL; - memset (&ret, 0, sizeof ret); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_ret(xdr, &ret) ) { - error (conn, VIR_ERR_RPC, - _("remoteDomainProcessEvent: unmarshalling ret")); - return NULL; - } - - dom = get_nonnull_domain(conn,ret.dom); - if (!dom) - return NULL; - - event = virDomainEventNewFromDom(dom, ret.event, ret.detail); - - virDomainFree(dom); - return event; -} - -static void -remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr) -{ - struct private_data *priv = conn->privateData; - virDomainEventPtr event; - - event = remoteDomainReadEvent(conn, xdr); - if (!event) - return; - - DEBUG0("Calling domain event callbacks (no queue)"); - virDomainEventDispatch(event, priv->callbackList, - virDomainEventDispatchDefaultFunc, NULL); - virDomainEventFree(event); -} - -static void -remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr) -{ - struct private_data *priv = conn->privateData; - virDomainEventPtr event; - - event = remoteDomainReadEvent(conn, xdr); - if (!event) - return; - - if (virDomainEventQueuePush(priv->domainEvents, - event) < 0) { - DEBUG0("Error adding event to queue"); - virDomainEventFree(event); - } -} - -/** remoteDomainEventFired: - * - * The callback for monitoring the remote socket - * for event data - */ -void -remoteDomainEventFired(int watch, - int fd, - int event, - void *opaque) -{ - char buffer[REMOTE_MESSAGE_MAX]; - char buffer2[4]; - struct remote_message_header hdr; - XDR xdr; - int len; - - virConnectPtr conn = opaque; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - DEBUG("Event fired %d %d %d %X", watch, fd, event, event); - - if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { - DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " - "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); - virEventRemoveHandle(watch); - goto done; - } - - if (fd != priv->sock) { - virEventRemoveHandle(watch); - goto done; - } - - /* Read and deserialise length word. */ - if (really_read (conn, priv, 0, buffer2, sizeof buffer2) == -1) - goto done; - - xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); - if (!xdr_int (&xdr, &len)) { - error (conn, VIR_ERR_RPC, _("xdr_int (length word, reply)")); - goto done; - } - xdr_destroy (&xdr); - - /* Length includes length word - adjust to real length to read. */ - len -= 4; - - if (len < 0 || len > REMOTE_MESSAGE_MAX) { - error (conn, VIR_ERR_RPC, _("packet received from server too large")); - goto done; - } - - /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, 0, buffer, len) == -1) { - error (conn, VIR_ERR_RPC, _("error reading buffer from memory")); - goto done; - } - - /* Deserialise reply header. */ - xdrmem_create (&xdr, buffer, len, XDR_DECODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - error (conn, VIR_ERR_RPC, _("invalid header in event firing")); - goto done; - } - - if (hdr.proc == REMOTE_PROC_DOMAIN_EVENT && - hdr.direction == REMOTE_MESSAGE) { - DEBUG0("Encountered an async event"); - remoteDomainProcessEvent(conn, &xdr); - } else { - DEBUG0("invalid proc in event firing"); - error (conn, VIR_ERR_RPC, _("invalid proc in event firing")); - } - -done: - remoteDriverUnlock(priv); -} - -void -remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) -{ - virConnectPtr conn = opaque; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - virDomainEventQueueDispatch(priv->domainEvents, priv->callbackList, - virDomainEventDispatchDefaultFunc, NULL); - virEventUpdateTimeout(priv->eventFlushTimer, -1); - - remoteDriverUnlock(priv); -} diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -34,6 +34,7 @@ #include <poll.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/ioctl.h> #if HAVE_SYS_WAIT_H #include <sys/wait.h> #endif @@ -155,8 +156,28 @@ virArgvToString(const char *const *argv) return ret; } +int virSetNonBlock(int fd) { +#ifndef WIN32 + int flags; + if ((flags = fcntl(fd, F_GETFL)) < 0) + return -1; + flags |= O_NONBLOCK; + if ((fcntl(fd, F_SETFL, flags)) < 0) + return -1; +#else + unsigned long flag = 1; -#ifndef __MINGW32__ + /* This is actually Gnulib's replacement rpl_ioctl function. + * We can't call ioctlsocket directly in any case. + */ + if (ioctl (fd, FIONBIO, (void *) &flag) == -1) + return -1; +#endif + return 0; +} + + +#ifndef WIN32 static int virSetCloseExec(int fd) { int flags; @@ -164,16 +185,6 @@ static int virSetCloseExec(int fd) { return -1; flags |= FD_CLOEXEC; if ((fcntl(fd, F_SETFD, flags)) < 0) - return -1; - return 0; -} - -static int virSetNonBlock(int fd) { - int flags; - if ((flags = fcntl(fd, F_GETFL)) < 0) - return -1; - flags |= O_NONBLOCK; - if ((fcntl(fd, F_SETFL, flags)) < 0) return -1; return 0; } diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -37,6 +37,8 @@ enum { VIR_EXEC_NONBLOCK = (1 << 0), VIR_EXEC_DAEMON = (1 << 1), }; + +int virSetNonBlock(int fd); int virExec(virConnectPtr conn, const char *const*argv, -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:41:48PM +0000, Daniel P. Berrange wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
The new code is actually a bit easier to read than the old one I think, but I didn't grasp all the details I must admit. The only worry I have with the "pass the buck" scheme is the piling up on recursive calls, I don't think there is any risk with the normal libvirt APIs as they are all 'terminal calls' in a sense, but I'm wondering what's happening say in conjunction with a high flow of events back to a client, the client doing calls as a result of the events etc ... Seems we are safe because no direct call from within the library is triggered by the reception of an event. Maybe I'm just wrong though, anyway the best is to push the change and monitor what is happening in the worst case circumstances. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Jan 15, 2009 at 04:03:16PM +0100, Daniel Veillard wrote:
On Tue, Jan 13, 2009 at 05:41:48PM +0000, Daniel P. Berrange wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
The new code is actually a bit easier to read than the old one I think, but I didn't grasp all the details I must admit. The only worry I have with the "pass the buck" scheme is the piling up on recursive calls, I don't think there is any risk with the normal libvirt APIs as they are all 'terminal calls' in a sense, but I'm wondering what's happening say in conjunction with a high flow of events back to a client, the client doing calls as a result of the events etc ... Seems we are safe because no direct call from within the library is triggered by the reception of an event.
We shouldn't get recursion here. There are two scenarios 1. Thread is in the call() function, when an event arrives -> The event is put on a queue, and a 0 second timer is activated in the app's event loop -> Once call() finishes, the 0 second timer fires, and the event is dispatched to the app. 2. Event arrives when no one is in call() function. -> The event is dispatched to app straightaway Now, the apps' callback which receives the event, may in turn make libvirt calls. This won't cause any recursion because the two scenarios above both guarentee that the event callback is not run from within the contxt of a call() command. When processing queued events we are also careful to handle our data structures such that a different thread can still safely make calls / receive & queue more events. There's always a possible impl bug in there, but I believe the general structure / design is correctly coping the neccessary scenarios Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 15, 2009 at 03:12:11PM +0000, Daniel P. Berrange wrote:
On Thu, Jan 15, 2009 at 04:03:16PM +0100, Daniel Veillard wrote:
On Tue, Jan 13, 2009 at 05:41:48PM +0000, Daniel P. Berrange wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
The new code is actually a bit easier to read than the old one I think, but I didn't grasp all the details I must admit. The only worry I have with the "pass the buck" scheme is the piling up on recursive calls, I don't think there is any risk with the normal libvirt APIs as they are all 'terminal calls' in a sense, but I'm wondering what's happening say in conjunction with a high flow of events back to a client, the client doing calls as a result of the events etc ... Seems we are safe because no direct call from within the library is triggered by the reception of an event.
We shouldn't get recursion here. There are two scenarios
1. Thread is in the call() function, when an event arrives
-> The event is put on a queue, and a 0 second timer is activated in the app's event loop -> Once call() finishes, the 0 second timer fires, and the event is dispatched to the app.
2. Event arrives when no one is in call() function.
-> The event is dispatched to app straightaway
Now, the apps' callback which receives the event, may in turn make libvirt calls. This won't cause any recursion because the two scenarios above both guarentee that the event callback is not run from within the contxt of a call() command.
When processing queued events we are also careful to handle our data structures such that a different thread can still safely make calls / receive & queue more events.
There's always a possible impl bug in there, but I believe the general structure / design is correctly coping the neccessary scenarios
Okay, thanks, I agree that the best at this point is to push it to get broader testing, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Tue, Jan 13, 2009 at 05:41:48PM +0000, Daniel P. Berrange wrote:
+ /* Get a unique serial number for this message. */ + rv->serial = priv->counter++;
This (and similar) are safe because we hold the remoteDriverLock until we enter the actual call? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

On Thu, Jan 15, 2009 at 05:07:01PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:41:48PM +0000, Daniel P. Berrange wrote:
+ /* Get a unique serial number for this message. */ + rv->serial = priv->counter++;
This (and similar) are safe because we hold the remoteDriverLock until we enter the actual call?
That is correct - the first thing all the public API entry points do is grab the lock. The lock is only then released, when the main thread is waiting on I/O in the poll() call: + remoteDriverUnlock(priv); + + repoll: + ret = poll(fds, ARRAY_CARDINALITY(fds), -1); + if (ret < 0 && errno == EINTR) + goto repoll; + remoteDriverLock(priv); Or when additional threads are sleeping on the condition variable. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 15, 2009 at 05:22:52PM +0000, Daniel P. Berrange wrote:
On Thu, Jan 15, 2009 at 05:07:01PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:41:48PM +0000, Daniel P. Berrange wrote:
+ /* Get a unique serial number for this message. */ + rv->serial = priv->counter++;
This (and similar) are safe because we hold the remoteDriverLock until we enter the actual call?
That is correct - the first thing all the public API entry points do is grab the lock.
The lock is only then released, when the main thread is waiting on I/O in the poll() call:
+ remoteDriverUnlock(priv); + + repoll: + ret = poll(fds, ARRAY_CARDINALITY(fds), -1); + if (ret < 0 && errno == EINTR) + goto repoll; + remoteDriverLock(priv);
Or when additional threads are sleeping on the condition variable.
+1 then. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
Quick summary: dense ;-) though lots of moved code. I haven't finished, but did find at least one problem, below.
diff --git a/src/remote_internal.c b/src/remote_internal.c ... @@ -114,6 +164,11 @@ struct private_data { virDomainEventQueuePtr domainEvents; /* Timer for flushing domainEvents queue */ int eventFlushTimer; + + /* List of threads currently doing dispatch */ + int wakeupSend; + int wakeupRead;
How about appending "FD" to indicate these are file descriptors. The names combined with the comment (which must apply to waitDispatch) made me wonder what they represented. Only when I saw them used in safewrite /saferead calls did I get it.
+ struct remote_thread_call *waitDispatch; };
enum { @@ -160,7 +215,6 @@ static void make_nonnull_storage_pool (r static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); void remoteDomainEventFired(int watch, int fd, int event, void *data); -static void remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr); static void remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr); void remoteDomainEventQueueFlush(int timer, void *opaque); /*----------------------------------------------------------------------*/ @@ -274,6 +328,7 @@ doRemoteOpen (virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags) { + int wakeup[2];
Add "fd" to this name, too? Not as big a deal, since this is local and the first use makes it obvious.
char *transport_str = NULL;
if (conn->uri) { @@ -696,6 +751,21 @@ doRemoteOpen (virConnectPtr conn,
} /* switch (transport) */
+ if (virSetNonBlock(priv->sock) < 0) { + errorf (conn, VIR_ERR_SYSTEM_ERROR, + _("unable to make socket non-blocking %s"), + strerror(errno)); + goto failed; + } + + if (pipe(wakeup) < 0) { + errorf (conn, VIR_ERR_SYSTEM_ERROR, + _("unable to make pipe %s"), + strerror(errno)); + goto failed; + } + priv->wakeupRead = wakeup[0]; + priv->wakeupSend = wakeup[1];
/* Try and authenticate with server */ if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1) @@ -768,6 +838,7 @@ doRemoteOpen (virConnectPtr conn, DEBUG0("virEventAddTimeout failed: No addTimeoutImpl defined. " "continuing without events."); virEventRemoveHandle(priv->watch); + priv->watch = -1; } } /* Successful. */ @@ -848,6 +919,7 @@ remoteOpen (virConnectPtr conn, } remoteDriverLock(priv); priv->localUses = 1; + priv->watch = -1;
if (flags & VIR_CONNECT_RO) rflags |= VIR_DRV_OPEN_REMOTE_RO; @@ -1220,6 +1292,7 @@ doRemoteClose (virConnectPtr conn, struc virEventRemoveTimeout(priv->eventFlushTimer); /* Remove handle for remote events */ virEventRemoveHandle(priv->watch); + priv->watch = -1; }
/* Close socket. */ @@ -5542,12 +5615,658 @@ done:
/*----------------------------------------------------------------------*/
-static int really_write (virConnectPtr conn, struct private_data *priv, - int in_open, char *bytes, int len); -static int really_read (virConnectPtr conn, struct private_data *priv, - int in_open, char *bytes, int len); - -/* This function performs a remote procedure call to procedure PROC_NR. + +static struct remote_thread_call * +prepareCall(virConnectPtr conn, + struct private_data *priv, + int flags, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + XDR xdr; + struct remote_message_header hdr; + struct remote_thread_call *rv; + + if (VIR_ALLOC(rv) < 0) + return NULL; + + if (virCondInit(&rv->cond) < 0) { + VIR_FREE(rv); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + return NULL; + } + + /* Get a unique serial number for this message. */ + rv->serial = priv->counter++; + rv->proc_nr = proc_nr; + rv->ret_filter = ret_filter; + rv->ret = ret; + + hdr.prog = REMOTE_PROGRAM; + hdr.vers = REMOTE_PROTOCOL_VERSION; + hdr.proc = proc_nr; + hdr.direction = REMOTE_CALL; + hdr.serial = rv->serial; + hdr.status = REMOTE_OK; + + /* Serialise header followed by args. */ + xdrmem_create (&xdr, rv->buffer+4, REMOTE_MESSAGE_MAX, XDR_ENCODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_RPC, _("xdr_remote_message_header failed")); + goto error; + } + + if (!(*args_filter) (&xdr, args)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("marshalling args")); + goto error; + } + + /* Get the length stored in buffer. */ + rv->bufferLength = xdr_getpos (&xdr); + xdr_destroy (&xdr); + + /* Length must include the length word itself (always encoded in + * 4 bytes as per RFC 4506). + */ + rv->bufferLength += 4; + + /* Encode the length word. */ + xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); + if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("xdr_int (length word)"));
I haven't done enough xdr* work to know, and man pages didn't provide an immediate answer: Is there no need to call xdr_destroy on this error path? I'd expect xdrmem_create to do any allocation, not xdr_int. There are many like this.
+ goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL;
The above should free rv, not ret: VIR_FREE(rv);

On Fri, Jan 16, 2009 at 12:49:06PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
Quick summary: dense ;-) though lots of moved code.
I haven't finished, but did find at least one problem, below.
diff --git a/src/remote_internal.c b/src/remote_internal.c ... @@ -114,6 +164,11 @@ struct private_data { virDomainEventQueuePtr domainEvents; /* Timer for flushing domainEvents queue */ int eventFlushTimer; + + /* List of threads currently doing dispatch */ + int wakeupSend; + int wakeupRead;
How about appending "FD" to indicate these are file descriptors. The names combined with the comment (which must apply to waitDispatch) made me wonder what they represented. Only when I saw them used in safewrite /saferead calls did I get it.
Yes, good idea - and its not really a list of threads either, so the comment is a little misleading :-)
+ /* Serialise header followed by args. */ + xdrmem_create (&xdr, rv->buffer+4, REMOTE_MESSAGE_MAX, XDR_ENCODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_RPC, _("xdr_remote_message_header failed")); + goto error; + } + + if (!(*args_filter) (&xdr, args)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("marshalling args")); + goto error; + } + + /* Get the length stored in buffer. */ + rv->bufferLength = xdr_getpos (&xdr); + xdr_destroy (&xdr); + + /* Length must include the length word itself (always encoded in + * 4 bytes as per RFC 4506). + */ + rv->bufferLength += 4; + + /* Encode the length word. */ + xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); + if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("xdr_int (length word)"));
I haven't done enough xdr* work to know, and man pages didn't provide an immediate answer: Is there no need to call xdr_destroy on this error path? I'd expect xdrmem_create to do any allocation, not xdr_int. There are many like this.
Yes, the 'error:' codepath should be calling 'xdr_destroy(&xdr)' to ensure free'ing of memory.
+ goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL;
The above should free rv, not ret:
VIR_FREE(rv);
Doh, bad naming convention for arguments - we always use 'ret' for return values. I should rename the argument to 'reply' since its what contains the RPC reply, so we can use the normal convention of 'ret' for return value. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:49:06PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
<snip>
+ goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL; The above should free rv, not ret:
VIR_FREE(rv);
Doh, bad naming convention for arguments - we always use 'ret' for return values. I should rename the argument to 'reply' since its what contains the RPC reply, so we can use the normal convention of 'ret' for return value.
I actually just tracked this down independently: the above bug was crashing virt-manager, where we incorrectly were passing 'None' to interfaceStats occasionally. So, good catch Jim :) Thanks, Cole

On Fri, Jan 16, 2009 at 10:18:32AM -0500, Cole Robinson wrote:
Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:49:06PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
<snip>
+ goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL; The above should free rv, not ret:
VIR_FREE(rv);
Doh, bad naming convention for arguments - we always use 'ret' for return values. I should rename the argument to 'reply' since its what contains the RPC reply, so we can use the normal convention of 'ret' for return value.
I actually just tracked this down independently: the above bug was crashing virt-manager, where we incorrectly were passing 'None' to interfaceStats occasionally. So, good catch Jim :)
That mistake should never have got anywhere near the RPC code! We are supposed to test all mandatory parameter for non-NULL in the libvirt.c file. I see that the 'path' arg in virDomainInterfaceStats is not being checked for non-NULL, so that's another bug to fix. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Jan 16, 2009 at 12:11:16PM +0000, Daniel P. Berrange wrote:
@@ -114,6 +164,11 @@ struct private_data { virDomainEventQueuePtr domainEvents; /* Timer for flushing domainEvents queue */ int eventFlushTimer; + + /* List of threads currently doing dispatch */ + int wakeupSend; + int wakeupRead;
How about appending "FD" to indicate these are file descriptors. The names combined with the comment (which must apply to waitDispatch) made me wonder what they represented. Only when I saw them used in safewrite /saferead calls did I get it.
Yes, good idea - and its not really a list of threads either, so the comment is a little misleading :-)
+ /* Encode the length word. */ + xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); + if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("xdr_int (length word)"));
I haven't done enough xdr* work to know, and man pages didn't provide an immediate answer: Is there no need to call xdr_destroy on this error path? I'd expect xdrmem_create to do any allocation, not xdr_int. There are many like this.
Yes, the 'error:' codepath should be calling 'xdr_destroy(&xdr)' to ensure free'ing of memory.
+ goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL;
The above should free rv, not ret:
VIR_FREE(rv);
Here is an update with those suggested renames & bug fixes in it. It also addresses the error reporting issue mentioned in http://www.redhat.com/archives/libvir-list/2009-January/msg00428.html That code should not have been using DEBUG() - it now correctly raises a real error including the error string, not just errno. There were two other bugs with missing error raising in the path for sasl_encode/decode. Everything upto this patch is committed, so this is diffed against current CVS. libvirt_private.syms | 1 remote_internal.c | 1539 ++++++++++++++++++++++++++++++++------------------- util.c | 33 - util.h | 2 4 files changed, 1002 insertions(+), 573 deletions(-) Daniel diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -290,6 +290,7 @@ virEnumToString; virEventAddHandle; virEventRemoveHandle; virExec; +virSetNonBlock; virFormatMacAddr; virGetHostname; virParseMacAddr; diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -68,6 +68,8 @@ #include <netdb.h> +#include <poll.h> + /* AI_ADDRCONFIG is missing on some systems. */ #ifndef AI_ADDRCONFIG # define AI_ADDRCONFIG 0 @@ -86,8 +88,44 @@ #include "util.h" #include "event.h" +#ifdef WIN32 +#define pipe(fds) _pipe(fds,4096, _O_BINARY) +#endif + + static int inside_daemon = 0; +struct remote_thread_call; + + +enum { + REMOTE_MODE_WAIT_TX, + REMOTE_MODE_WAIT_RX, + REMOTE_MODE_COMPLETE, + REMOTE_MODE_ERROR, +}; + +struct remote_thread_call { + int mode; + + /* 4 byte length, followed by RPC message header+body */ + char buffer[4 + REMOTE_MESSAGE_MAX]; + unsigned int bufferLength; + unsigned int bufferOffset; + + unsigned int serial; + unsigned int proc_nr; + + virCond cond; + + xdrproc_t ret_filter; + char *ret; + + remote_error err; + + struct remote_thread_call *next; +}; + struct private_data { virMutex lock; @@ -101,12 +139,24 @@ struct private_data { int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ FILE *debugLog; /* Debug remote protocol */ + #if HAVE_SASL sasl_conn_t *saslconn; /* SASL context */ + const char *saslDecoded; unsigned int saslDecodedLength; unsigned int saslDecodedOffset; -#endif + + const char *saslEncoded; + unsigned int saslEncodedLength; + unsigned int saslEncodedOffset; +#endif + + /* 4 byte length, followed by RPC message header+body */ + char buffer[4 + REMOTE_MESSAGE_MAX]; + unsigned int bufferLength; + unsigned int bufferOffset; + /* The list of domain event callbacks */ virDomainEventCallbackListPtr callbackList; /* The queue of domain events generated @@ -114,6 +164,13 @@ struct private_data { virDomainEventQueuePtr domainEvents; /* Timer for flushing domainEvents queue */ int eventFlushTimer; + + /* Self-pipe to wakeup threads waiting in poll() */ + int wakeupSendFD; + int wakeupReadFD; + + /* List of threads currently waiting for dispatch */ + struct remote_thread_call *waitDispatch; }; enum { @@ -160,7 +217,6 @@ static void make_nonnull_network (remote static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); void remoteDomainEventFired(int watch, int fd, int event, void *data); -static void remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr); static void remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr); void remoteDomainEventQueueFlush(int timer, void *opaque); /*----------------------------------------------------------------------*/ @@ -274,6 +330,7 @@ doRemoteOpen (virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags) { + int wakeupFD[2]; char *transport_str = NULL; if (conn->uri) { @@ -696,6 +753,21 @@ doRemoteOpen (virConnectPtr conn, } /* switch (transport) */ + if (virSetNonBlock(priv->sock) < 0) { + errorf (conn, VIR_ERR_SYSTEM_ERROR, + _("unable to make socket non-blocking %s"), + strerror(errno)); + goto failed; + } + + if (pipe(wakeupFD) < 0) { + errorf (conn, VIR_ERR_SYSTEM_ERROR, + _("unable to make pipe %s"), + strerror(errno)); + goto failed; + } + priv->wakeupReadFD = wakeupFD[0]; + priv->wakeupSendFD = wakeupFD[1]; /* Try and authenticate with server */ if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1) @@ -768,6 +840,7 @@ doRemoteOpen (virConnectPtr conn, DEBUG0("virEventAddTimeout failed: No addTimeoutImpl defined. " "continuing without events."); virEventRemoveHandle(priv->watch); + priv->watch = -1; } } /* Successful. */ @@ -848,6 +921,7 @@ remoteOpen (virConnectPtr conn, } remoteDriverLock(priv); priv->localUses = 1; + priv->watch = -1; if (flags & VIR_CONNECT_RO) rflags |= VIR_DRV_OPEN_REMOTE_RO; @@ -1220,6 +1294,7 @@ doRemoteClose (virConnectPtr conn, struc virEventRemoveTimeout(priv->eventFlushTimer); /* Remove handle for remote events */ virEventRemoveHandle(priv->watch); + priv->watch = -1; } /* Close socket. */ @@ -5537,12 +5612,665 @@ done: /*----------------------------------------------------------------------*/ -static int really_write (virConnectPtr conn, struct private_data *priv, - int in_open, char *bytes, int len); -static int really_read (virConnectPtr conn, struct private_data *priv, - int in_open, char *bytes, int len); - -/* This function performs a remote procedure call to procedure PROC_NR. + +static struct remote_thread_call * +prepareCall(virConnectPtr conn, + struct private_data *priv, + int flags, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + XDR xdr; + struct remote_message_header hdr; + struct remote_thread_call *rv; + + if (VIR_ALLOC(rv) < 0) + return NULL; + + if (virCondInit(&rv->cond) < 0) { + VIR_FREE(rv); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + return NULL; + } + + /* Get a unique serial number for this message. */ + rv->serial = priv->counter++; + rv->proc_nr = proc_nr; + rv->ret_filter = ret_filter; + rv->ret = ret; + + hdr.prog = REMOTE_PROGRAM; + hdr.vers = REMOTE_PROTOCOL_VERSION; + hdr.proc = proc_nr; + hdr.direction = REMOTE_CALL; + hdr.serial = rv->serial; + hdr.status = REMOTE_OK; + + /* Serialise header followed by args. */ + xdrmem_create (&xdr, rv->buffer+4, REMOTE_MESSAGE_MAX, XDR_ENCODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_RPC, _("xdr_remote_message_header failed")); + goto error; + } + + if (!(*args_filter) (&xdr, args)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("marshalling args")); + goto error; + } + + /* Get the length stored in buffer. */ + rv->bufferLength = xdr_getpos (&xdr); + xdr_destroy (&xdr); + + /* Length must include the length word itself (always encoded in + * 4 bytes as per RFC 4506). + */ + rv->bufferLength += 4; + + /* Encode the length word. */ + xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); + if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("xdr_int (length word)")); + goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + xdr_destroy (&xdr); + VIR_FREE(rv); + return NULL; +} + + + +static int +processCallWrite(virConnectPtr conn, + struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + const char *bytes, int len) +{ + int ret; + + if (priv->uses_tls) { + tls_resend: + ret = gnutls_record_send (priv->session, bytes, len); + if (ret < 0) { + if (ret == GNUTLS_E_INTERRUPTED) + goto tls_resend; + if (ret == GNUTLS_E_AGAIN) + return 0; + + error (in_open ? NULL : conn, + VIR_ERR_GNUTLS_ERROR, gnutls_strerror (ret)); + return -1; + } + } else { + resend: + ret = send (priv->sock, bytes, len, 0); + if (ret == -1) { + if (errno == EINTR) + goto resend; + if (errno == EWOULDBLOCK) + return 0; + + error (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, strerror (errno)); + return -1; + + } + } + + return ret; +} + + +static int +processCallRead(virConnectPtr conn, + struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + int ret; + + if (priv->uses_tls) { + tls_resend: + ret = gnutls_record_recv (priv->session, bytes, len); + if (ret == GNUTLS_E_INTERRUPTED) + goto tls_resend; + if (ret == GNUTLS_E_AGAIN) + return 0; + + /* Treat 0 == EOF as an error */ + if (ret <= 0) { + if (ret < 0) + errorf (in_open ? NULL : conn, + VIR_ERR_GNUTLS_ERROR, + _("failed to read from TLS socket %s"), + gnutls_strerror (ret)); + else + errorf (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, + "%s", _("server closed connection")); + return -1; + } + } else { + resend: + ret = recv (priv->sock, bytes, len, 0); + if (ret <= 0) { + if (ret == -1) { + if (errno == EINTR) + goto resend; + if (errno == EWOULDBLOCK) + return 0; + + errorf (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, + _("failed to read from socket %s"), + strerror (errno)); + } else { + errorf (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, + "%s", _("server closed connection")); + } + return -1; + } + } + + return ret; +} + + +static int +processCallSendOne(virConnectPtr conn, + struct private_data *priv, + int in_open, + struct remote_thread_call *thecall) +{ +#if HAVE_SASL + if (priv->saslconn) { + const char *output; + unsigned int outputlen; + int err, ret; + + if (!priv->saslEncoded) { + err = sasl_encode(priv->saslconn, + thecall->buffer + thecall->bufferOffset, + thecall->bufferLength - thecall->bufferOffset, + &output, &outputlen); + if (err != SASL_OK) { + errorf (in_open ? NULL : conn, VIR_ERR_INTERNAL_ERROR, + _("failed to encode SASL data: %s"), + sasl_errstring(err, NULL, NULL)); + return -1; + } + priv->saslEncoded = output; + priv->saslEncodedLength = outputlen; + priv->saslEncodedOffset = 0; + + thecall->bufferOffset = thecall->bufferLength; + } + + ret = processCallWrite(conn, priv, in_open, + priv->saslEncoded + priv->saslEncodedOffset, + priv->saslEncodedLength - priv->saslEncodedOffset); + if (ret < 0) + return ret; + priv->saslEncodedOffset += ret; + + if (priv->saslEncodedOffset == priv->saslEncodedLength) { + priv->saslEncoded = NULL; + priv->saslEncodedOffset = priv->saslEncodedLength = 0; + thecall->mode = REMOTE_MODE_WAIT_RX; + } + } else { +#endif + int ret; + ret = processCallWrite(conn, priv, in_open, + thecall->buffer + thecall->bufferOffset, + thecall->bufferLength - thecall->bufferOffset); + if (ret < 0) + return ret; + thecall->bufferOffset += ret; + + if (thecall->bufferOffset == thecall->bufferLength) { + thecall->bufferOffset = thecall->bufferLength = 0; + thecall->mode = REMOTE_MODE_WAIT_RX; + } +#if HAVE_SASL + } +#endif + return 0; +} + + +static int +processCallSend(virConnectPtr conn, struct private_data *priv, + int in_open) { + struct remote_thread_call *thecall = priv->waitDispatch; + + while (thecall && + thecall->mode != REMOTE_MODE_WAIT_TX) + thecall = thecall->next; + + if (!thecall) + return -1; /* Shouldn't happen, but you never know... */ + + while (thecall) { + int ret = processCallSendOne(conn, priv, in_open, thecall); + if (ret < 0) + return ret; + + if (thecall->mode == REMOTE_MODE_WAIT_TX) + return 0; /* Blocking write, to back to event loop */ + + thecall = thecall->next; + } + + return 0; /* No more calls to send, all done */ +} + +static int +processCallRecvSome(virConnectPtr conn, struct private_data *priv, + int in_open) { + unsigned int wantData; + + /* Start by reading length word */ + if (priv->bufferLength == 0) + priv->bufferLength = 4; + + wantData = priv->bufferLength - priv->bufferOffset; + +#if HAVE_SASL + if (priv->saslconn) { + if (priv->saslDecoded == NULL) { + char encoded[8192]; + unsigned int encodedLen = sizeof(encoded); + int ret, err; + ret = processCallRead(conn, priv, in_open, + encoded, encodedLen); + if (ret < 0) + return -1; + if (ret == 0) + return 0; + + err = sasl_decode(priv->saslconn, encoded, ret, + &priv->saslDecoded, &priv->saslDecodedLength); + if (err != SASL_OK) { + errorf (in_open ? NULL : conn, VIR_ERR_INTERNAL_ERROR, + _("failed to decode SASL data: %s"), + sasl_errstring(err, NULL, NULL)); + return -1; + } + priv->saslDecodedOffset = 0; + } + + if ((priv->saslDecodedLength - priv->saslDecodedOffset) < wantData) + wantData = (priv->saslDecodedLength - priv->saslDecodedOffset); + + memcpy(priv->buffer + priv->bufferOffset, + priv->saslDecoded + priv->saslDecodedOffset, + wantData); + priv->saslDecodedOffset += wantData; + priv->bufferOffset += wantData; + if (priv->saslDecodedOffset == priv->saslDecodedLength) { + priv->saslDecodedLength = priv->saslDecodedLength = 0; + priv->saslDecoded = NULL; + } + + return wantData; + } else { +#endif + int ret; + + ret = processCallRead(conn, priv, in_open, + priv->buffer + priv->bufferOffset, + wantData); + if (ret < 0) + return -1; + if (ret == 0) + return 0; + + priv->bufferOffset += ret; + + return ret; +#if HAVE_SASL + } +#endif +} + + +static void +processCallAsyncEvent(virConnectPtr conn, struct private_data *priv, + int in_open, + remote_message_header *hdr, + XDR *xdr) { + /* An async message has come in while we were waiting for the + * response. Process it to pull it off the wire, and try again + */ + DEBUG0("Encountered an event while waiting for a response"); + + if (in_open) { + DEBUG("Ignoring bogus event %d received while in open", hdr->proc); + return; + } + + if (hdr->proc == REMOTE_PROC_DOMAIN_EVENT) { + remoteDomainQueueEvent(conn, xdr); + virEventUpdateTimeout(priv->eventFlushTimer, 0); + } else { + DEBUG("Unexpected event proc %d", hdr->proc); + } +} + +static int +processCallRecvLen(virConnectPtr conn, struct private_data *priv, + int in_open) { + XDR xdr; + int len; + + xdrmem_create (&xdr, priv->buffer, priv->bufferLength, XDR_DECODE); + if (!xdr_int (&xdr, &len)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("xdr_int (length word, reply)")); + return -1; + } + xdr_destroy (&xdr); + + /* Length includes length word - adjust to real length to read. */ + len -= 4; + + if (len < 0 || len > REMOTE_MESSAGE_MAX) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("packet received from server too large")); + return -1; + } + + /* Extend our declared buffer length and carry + on reading the header + payload */ + priv->bufferLength += len; + DEBUG("Got length, now need %d total (%d more)", priv->bufferLength, len); + return 0; +} + + +static int +processCallRecvMsg(virConnectPtr conn, struct private_data *priv, + int in_open) { + XDR xdr; + struct remote_message_header hdr; + int len = priv->bufferLength - 4; + struct remote_thread_call *thecall; + + /* Deserialise reply header. */ + xdrmem_create (&xdr, priv->buffer + 4, len, XDR_DECODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("invalid header in reply")); + return -1; + } + + /* Check program, version, etc. are what we expect. */ + if (hdr.prog != REMOTE_PROGRAM) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown program (received %x, expected %x)"), + hdr.prog, REMOTE_PROGRAM); + return -1; + } + if (hdr.vers != REMOTE_PROTOCOL_VERSION) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown protocol version (received %x, expected %x)"), + hdr.vers, REMOTE_PROTOCOL_VERSION); + return -1; + } + + /* Async events from server need special handling */ + if (hdr.direction == REMOTE_MESSAGE) { + processCallAsyncEvent(conn, priv, in_open, + &hdr, &xdr); + xdr_destroy(&xdr); + return 0; + } + + if (hdr.direction != REMOTE_REPLY) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("got unexpected RPC call %d from server"), + hdr.proc); + xdr_destroy(&xdr); + return -1; + } + + /* Ok, definitely got an RPC reply now find + out who's been waiting for it */ + + thecall = priv->waitDispatch; + while (thecall && + thecall->serial != hdr.serial) + thecall = thecall->next; + + if (!thecall) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("no call waiting for reply with serial %d"), + hdr.serial); + xdr_destroy(&xdr); + return -1; + } + + if (hdr.proc != thecall->proc_nr) { + virRaiseError (in_open ? NULL : conn, + NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown procedure (received %x, expected %x)"), + hdr.proc, thecall->proc_nr); + xdr_destroy (&xdr); + return -1; + } + + /* Status is either REMOTE_OK (meaning that what follows is a ret + * structure), or REMOTE_ERROR (and what follows is a remote_error + * structure). + */ + switch (hdr.status) { + case REMOTE_OK: + if (!(*thecall->ret_filter) (&xdr, thecall->ret)) { + error (in_open ? NULL : conn, VIR_ERR_RPC, + _("unmarshalling ret")); + return -1; + } + thecall->mode = REMOTE_MODE_COMPLETE; + xdr_destroy (&xdr); + return 0; + + case REMOTE_ERROR: + memset (&thecall->err, 0, sizeof thecall->err); + if (!xdr_remote_error (&xdr, &thecall->err)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("unmarshalling remote_error")); + return -1; + } + xdr_destroy (&xdr); + thecall->mode = REMOTE_MODE_ERROR; + return 0; + + default: + virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + _("unknown status (received %x)"), + hdr.status); + xdr_destroy (&xdr); + return -1; + } +} + + +static int +processCallRecv(virConnectPtr conn, struct private_data *priv, + int in_open) { + int ret; + + /* Read as much data as is available, until we get + * EGAIN + */ + for (;;) { + ret = processCallRecvSome(conn, priv, in_open); + + if (ret < 0) + return -1; + if (ret == 0) + return 0; /* Blocking on read */ + + /* Check for completion of our goal */ + if (priv->bufferOffset == priv->bufferLength) { + if (priv->bufferOffset == 4) { + ret = processCallRecvLen(conn, priv, in_open); + } else { + ret = processCallRecvMsg(conn, priv, in_open); + priv->bufferOffset = priv->bufferLength = 0; + } + if (ret < 0) + return -1; + } + } +} + +/* + * Process all calls pending dispatch/receive until we + * get a reply to our own call. Then quit and pass the buck + * to someone else. + */ +static int +processCalls(virConnectPtr conn, + struct private_data *priv, + int in_open, + struct remote_thread_call *thiscall) +{ + struct pollfd fds[2]; + int ret; + + fds[0].fd = priv->sock; + fds[1].fd = priv->wakeupReadFD; + + for (;;) { + struct remote_thread_call *tmp = priv->waitDispatch; + struct remote_thread_call *prev; + char ignore; + + fds[0].events = fds[0].revents = 0; + fds[1].events = fds[1].revents = 0; + + fds[1].events = POLLIN; + while (tmp) { + if (tmp->mode == REMOTE_MODE_WAIT_RX) + fds[0].events |= POLLIN; + if (tmp->mode == REMOTE_MODE_WAIT_TX) + fds[0].events |= POLLOUT; + + tmp = tmp->next; + } + + /* Release lock while poll'ing so other threads + * can stuff themselves on the queue */ + remoteDriverUnlock(priv); + + repoll: + ret = poll(fds, ARRAY_CARDINALITY(fds), -1); + if (ret < 0 && errno == EINTR) + goto repoll; + remoteDriverLock(priv); + + if (fds[1].revents) { + DEBUG0("Woken up from poll by other thread"); + saferead(priv->wakeupReadFD, &ignore, sizeof(ignore)); + } + + if (ret < 0) { + if (errno == EWOULDBLOCK) + continue; + errorf (in_open ? NULL : conn, VIR_ERR_INTERNAL_ERROR, + _("poll on socket failed %s"), strerror(errno)); + return -1; + } + + if (fds[0].revents & POLLOUT) { + if (processCallSend(conn, priv, in_open) < 0) + return -1; + } + + if (fds[0].revents & POLLIN) { + if (processCallRecv(conn, priv, in_open) < 0) + return -1; + } + + /* Iterate through waiting threads and if + * any are complete then tell 'em to wakeup + */ + tmp = priv->waitDispatch; + prev = NULL; + while (tmp) { + if (tmp != thiscall && + (tmp->mode == REMOTE_MODE_COMPLETE || + tmp->mode == REMOTE_MODE_ERROR)) { + /* Take them out of the list */ + if (prev) + prev->next = tmp->next; + else + priv->waitDispatch = tmp->next; + + /* And wake them up.... + * ...they won't actually wakeup until + * we release our mutex a short while + * later... + */ + DEBUG("Waking up sleep %d %p %p", tmp->proc_nr, tmp, priv->waitDispatch); + virCondSignal(&tmp->cond); + } + prev = tmp; + tmp = tmp->next; + } + + /* Now see if *we* are done */ + if (thiscall->mode == REMOTE_MODE_COMPLETE || + thiscall->mode == REMOTE_MODE_ERROR) { + /* We're at head of the list already, so + * remove us + */ + priv->waitDispatch = thiscall->next; + DEBUG("Giving up the buck %d %p %p", thiscall->proc_nr, thiscall, priv->waitDispatch); + /* See if someone else is still waiting + * and if so, then pass the buck ! */ + if (priv->waitDispatch) { + DEBUG("Passing the buck to %d %p", priv->waitDispatch->proc_nr, priv->waitDispatch); + virCondSignal(&priv->waitDispatch->cond); + } + return 0; + } + + + if (fds[0].revents & (POLLHUP | POLLERR)) { + errorf(in_open ? NULL : conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("received hangup / error event on socket")); + return -1; + } + } +} + +/* + * This function performs a remote procedure call to procedure PROC_NR. * * NB. This does not free the args structure (not desirable, since you * often want this allocated on the stack or else it contains strings @@ -5551,204 +6279,29 @@ static int really_read (virConnectPtr co * * NB(2). Make sure to memset (&ret, 0, sizeof ret) before calling, * else Bad Things will happen in the XDR code. - */ -static int -doCall (virConnectPtr conn, struct private_data *priv, - int flags /* if we are in virConnectOpen */, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) -{ - char buffer[REMOTE_MESSAGE_MAX]; - char buffer2[4]; - struct remote_message_header hdr; - XDR xdr; - int len; - struct remote_error rerror; - - /* Get a unique serial number for this message. */ - int serial = priv->counter++; - - hdr.prog = REMOTE_PROGRAM; - hdr.vers = REMOTE_PROTOCOL_VERSION; - hdr.proc = proc_nr; - hdr.direction = REMOTE_CALL; - hdr.serial = serial; - hdr.status = REMOTE_OK; - - /* Serialise header followed by args. */ - xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("xdr_remote_message_header failed")); - return -1; - } - - if (!(*args_filter) (&xdr, args)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("marshalling args")); - return -1; - } - - /* Get the length stored in buffer. */ - len = xdr_getpos (&xdr); - xdr_destroy (&xdr); - - /* Length must include the length word itself (always encoded in - * 4 bytes as per RFC 4506). - */ - len += 4; - - /* Encode the length word. */ - xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_ENCODE); - if (!xdr_int (&xdr, &len)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("xdr_int (length word)")); - return -1; - } - xdr_destroy (&xdr); - - /* Send length word followed by header+args. */ - if (really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1 || - really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len-4) == -1) - return -1; - -retry_read: - /* Read and deserialise length word. */ - if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1) - return -1; - - xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); - if (!xdr_int (&xdr, &len)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("xdr_int (length word, reply)")); - return -1; - } - xdr_destroy (&xdr); - - /* Length includes length word - adjust to real length to read. */ - len -= 4; - - if (len < 0 || len > REMOTE_MESSAGE_MAX) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("packet received from server too large")); - return -1; - } - - /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len) == -1) - return -1; - - /* Deserialise reply header. */ - xdrmem_create (&xdr, buffer, len, XDR_DECODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("invalid header in reply")); - return -1; - } - - /* Check program, version, etc. are what we expect. */ - if (hdr.prog != REMOTE_PROGRAM) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown program (received %x, expected %x)"), - hdr.prog, REMOTE_PROGRAM); - return -1; - } - if (hdr.vers != REMOTE_PROTOCOL_VERSION) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown protocol version (received %x, expected %x)"), - hdr.vers, REMOTE_PROTOCOL_VERSION); - return -1; - } - - if (hdr.proc == REMOTE_PROC_DOMAIN_EVENT && - hdr.direction == REMOTE_MESSAGE) { - /* An async message has come in while we were waiting for the - * response. Process it to pull it off the wire, and try again - */ - DEBUG0("Encountered an event while waiting for a response"); - - remoteDomainQueueEvent(conn, &xdr); - virEventUpdateTimeout(priv->eventFlushTimer, 0); - - DEBUG0("Retrying read"); - xdr_destroy (&xdr); - goto retry_read; - } - if (hdr.proc != proc_nr) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown procedure (received %x, expected %x)"), - hdr.proc, proc_nr); - return -1; - } - if (hdr.direction != REMOTE_REPLY) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown direction (received %x, expected %x)"), - hdr.direction, REMOTE_REPLY); - return -1; - } - if (hdr.serial != serial) { - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown serial (received %x, expected %x)"), - hdr.serial, serial); - return -1; - } - - /* Status is either REMOTE_OK (meaning that what follows is a ret - * structure), or REMOTE_ERROR (and what follows is a remote_error - * structure). - */ - switch (hdr.status) { - case REMOTE_OK: - if (!(*ret_filter) (&xdr, ret)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("unmarshalling ret")); - return -1; - } - xdr_destroy (&xdr); - return 0; - - case REMOTE_ERROR: - memset (&rerror, 0, sizeof rerror); - if (!xdr_remote_error (&xdr, &rerror)) { - error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - VIR_ERR_RPC, _("unmarshalling remote_error")); - return -1; - } - xdr_destroy (&xdr); - /* See if caller asked us to keep quiet about missing RPCs - * eg for interop with older servers */ - if (flags & REMOTE_CALL_QUIET_MISSING_RPC && - rerror.domain == VIR_FROM_REMOTE && - rerror.code == VIR_ERR_RPC && - rerror.level == VIR_ERR_ERROR && - STRPREFIX(*rerror.message, "unknown procedure")) { - return -2; - } - server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, &rerror); - xdr_free ((xdrproc_t) xdr_remote_error, (char *) &rerror); - return -1; - - default: - virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("unknown status (received %x)"), - hdr.status); - xdr_destroy (&xdr); - return -1; - } -} - - + * + * NB(3) You must have the private_data lock before calling this + * + * NB(4) This is very complicated. Due to connection cloning, multiple + * threads can want to use the socket at once. Obviously only one of + * them can. So if someone's using the socket, other threads are put + * to sleep on condition variables. THe existing thread may completely + * send & receive their RPC call/reply while they're asleep. Or it + * may only get around to dealing with sending the call. Or it may + * get around to neither. So upon waking up from slumber, the other + * thread may or may not have more work todo. + * + * We call this dance 'passing the buck' + * + * http://en.wikipedia.org/wiki/Passing_the_buck + * + * "Buck passing or passing the buck is the action of transferring + * responsibility or blame unto another person. It is also used as + * a strategy in power politics when the actions of one country/ + * nation are blamed on another, providing an opportunity for war." + * + * NB(5) Don't Panic! + */ static int call (virConnectPtr conn, struct private_data *priv, int flags /* if we are in virConnectOpen */, @@ -5757,6 +6310,87 @@ call (virConnectPtr conn, struct private xdrproc_t ret_filter, char *ret) { int rv; + struct remote_thread_call *thiscall; + + DEBUG("Doing call %d %p", proc_nr, priv->waitDispatch); + thiscall = prepareCall(conn, priv, flags, proc_nr, + args_filter, args, + ret_filter, ret); + + if (!thiscall) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_NO_MEMORY, NULL); + return -1; + } + + /* Check to see if another thread is dispatching */ + if (priv->waitDispatch) { + /* Stick ourselves on the end of the wait queue */ + struct remote_thread_call *tmp = priv->waitDispatch; + char ignore = 1; + while (tmp && tmp->next) + tmp = tmp->next; + if (tmp) + tmp->next = thiscall; + else + priv->waitDispatch = thiscall; + + /* Force other thread to wakup from poll */ + safewrite(priv->wakeupSendFD, &ignore, sizeof(ignore)); + + DEBUG("Going to sleep %d %p %p", proc_nr, priv->waitDispatch, thiscall); + /* Go to sleep while other thread is working... */ + if (virCondWait(&thiscall->cond, &priv->lock) < 0) { + if (priv->waitDispatch == thiscall) { + priv->waitDispatch = thiscall->next; + } else { + tmp = priv->waitDispatch; + while (tmp && tmp->next && + tmp->next != thiscall) { + tmp = tmp->next; + } + if (tmp && tmp->next == thiscall) + tmp->next = thiscall->next; + } + errorf(flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to wait on condition")); + VIR_FREE(thiscall); + return -1; + } + + DEBUG("Wokeup from sleep %d %p %p", proc_nr, priv->waitDispatch, thiscall); + /* Two reasons we can be woken up + * 1. Other thread has got our reply ready for us + * 2. Other thread is all done, and it is our turn to + * be the dispatcher to finish waiting for + * our reply + */ + if (thiscall->mode == REMOTE_MODE_COMPLETE || + thiscall->mode == REMOTE_MODE_ERROR) { + /* + * We avoided catching the buck and our reply is ready ! + * We've already had 'thiscall' removed from the list + * so just need to (maybe) handle errors & free it + */ + goto cleanup; + } + + /* Grr, someone passed the buck onto us ... */ + + } else { + /* We're first to catch the buck */ + priv->waitDispatch = thiscall; + } + + DEBUG("We have the buck %d %p %p", proc_nr, priv->waitDispatch, thiscall); + /* + * The buck stops here! + * + * At this point we're about to own the dispatch + * process... + */ + /* * Avoid needless wake-ups of the event loop in the * case where this call is being made from a different @@ -5767,207 +6401,146 @@ call (virConnectPtr conn, struct private if (priv->watch >= 0) virEventUpdateHandle(priv->watch, 0); - rv = doCall(conn, priv,flags, proc_nr, - args_filter, args, - ret_filter, ret); + rv = processCalls(conn, priv, + flags & REMOTE_CALL_IN_OPEN ? 1 : 0, + thiscall); if (priv->watch >= 0) virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE); - return rv; -} - -static int -really_write_buf (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - const char *bytes, int len) -{ - const char *p; - int err; - - p = bytes; - if (priv->uses_tls) { - do { - err = gnutls_record_send (priv->session, p, len); - if (err < 0) { - if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } else { - do { - err = send (priv->sock, p, len, 0); - if (err == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } - - return 0; -} - -static int -really_write_plain (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - return really_write_buf(conn, priv, in_open, bytes, len); -} - -#if HAVE_SASL -static int -really_write_sasl (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - const char *output; - unsigned int outputlen; - int err; - - err = sasl_encode(priv->saslconn, bytes, len, &output, &outputlen); - if (err != SASL_OK) { - return -1; - } - - return really_write_buf(conn, priv, in_open, output, outputlen); -} -#endif - -static int -really_write (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ -#if HAVE_SASL - if (priv->saslconn) - return really_write_sasl(conn, priv, in_open, bytes, len); - else -#endif - return really_write_plain(conn, priv, in_open, bytes, len); -} - -static int -really_read_buf (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - int err; - - if (priv->uses_tls) { - tlsreread: - err = gnutls_record_recv (priv->session, bytes, len); - if (err < 0) { - if (err == GNUTLS_E_INTERRUPTED) - goto tlsreread; - error (in_open ? NULL : conn, - VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, _("socket closed unexpectedly")); - return -1; - } - } else { - reread: - err = recv (priv->sock, bytes, len, 0); - if (err == -1) { - if (errno == EINTR) - goto reread; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, _("socket closed unexpectedly")); - return -1; - } - } - - return err; -} - -static int -really_read_plain (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - do { - int ret = really_read_buf (conn, priv, in_open, bytes, len); - if (ret < 0) - return -1; - - len -= ret; - bytes += ret; - } while (len > 0); - - return 0; -} - -#if HAVE_SASL -static int -really_read_sasl (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - do { - int want, got; - if (priv->saslDecoded == NULL) { - char encoded[8192]; - int encodedLen = sizeof(encoded); - int err, ret; - ret = really_read_buf (conn, priv, in_open, encoded, encodedLen); - if (ret < 0) - return -1; - - err = sasl_decode(priv->saslconn, encoded, ret, - &priv->saslDecoded, &priv->saslDecodedLength); - } - - got = priv->saslDecodedLength - priv->saslDecodedOffset; - want = len; - if (want > got) - want = got; - - memcpy(bytes, priv->saslDecoded + priv->saslDecodedOffset, want); - priv->saslDecodedOffset += want; - if (priv->saslDecodedOffset == priv->saslDecodedLength) { - priv->saslDecoded = NULL; - priv->saslDecodedOffset = priv->saslDecodedLength = 0; - } - bytes += want; - len -= want; - } while (len > 0); - - return 0; -} -#endif - -static int -really_read (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ -#if HAVE_SASL - if (priv->saslconn) - return really_read_sasl (conn, priv, in_open, bytes, len); - else -#endif - return really_read_plain (conn, priv, in_open, bytes, len); -} + + if (rv < 0) { + VIR_FREE(thiscall); + return -1; + } + +cleanup: + DEBUG("All done with our call %d %p %p", proc_nr, priv->waitDispatch, thiscall); + if (thiscall->mode == REMOTE_MODE_ERROR) { + /* See if caller asked us to keep quiet about missing RPCs + * eg for interop with older servers */ + if (flags & REMOTE_CALL_QUIET_MISSING_RPC && + thiscall->err.domain == VIR_FROM_REMOTE && + thiscall->err.code == VIR_ERR_RPC && + thiscall->err.level == VIR_ERR_ERROR && + STRPREFIX(*thiscall->err.message, "unknown procedure")) { + rv = -2; + } else { + server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, + &thiscall->err); + rv = -1; + } + } else { + rv = 0; + } + VIR_FREE(thiscall); + return rv; +} + +/** + * remoteDomainReadEvent + * + * Read the event data off the wire + */ +static virDomainEventPtr +remoteDomainReadEvent(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_ret ret; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&ret, 0, sizeof ret); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_ret(xdr, &ret) ) { + error (conn, VIR_ERR_RPC, + _("remoteDomainProcessEvent: unmarshalling ret")); + return NULL; + } + + dom = get_nonnull_domain(conn,ret.dom); + if (!dom) + return NULL; + + event = virDomainEventNewFromDom(dom, ret.event, ret.detail); + + virDomainFree(dom); + return event; +} + +static void +remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr) +{ + struct private_data *priv = conn->privateData; + virDomainEventPtr event; + + event = remoteDomainReadEvent(conn, xdr); + if (!event) + return; + + if (virDomainEventQueuePush(priv->domainEvents, + event) < 0) { + DEBUG0("Error adding event to queue"); + virDomainEventFree(event); + } +} + +/** remoteDomainEventFired: + * + * The callback for monitoring the remote socket + * for event data + */ +void +remoteDomainEventFired(int watch, + int fd, + int event, + void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + /* This should be impossible, but it doesn't hurt to check */ + if (priv->waitDispatch) + goto done; + + DEBUG("Event fired %d %d %d %X", watch, fd, event, event); + + if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { + DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " + "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); + virEventRemoveHandle(watch); + priv->watch = -1; + goto done; + } + + if (fd != priv->sock) { + virEventRemoveHandle(watch); + priv->watch = -1; + goto done; + } + + if (processCallRecv(conn, priv, 0) < 0) + DEBUG0("Something went wrong during async message processing"); + +done: + remoteDriverUnlock(priv); +} + +void +remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + virDomainEventQueueDispatch(priv->domainEvents, priv->callbackList, + virDomainEventDispatchDefaultFunc, NULL); + virEventUpdateTimeout(priv->eventFlushTimer, -1); + + remoteDriverUnlock(priv); +} + /* For errors internal to this library. */ static void @@ -6267,161 +6840,3 @@ remoteRegister (void) return 0; } -/** - * remoteDomainReadEvent - * - * Read the event data off the wire - */ -static virDomainEventPtr -remoteDomainReadEvent(virConnectPtr conn, XDR *xdr) -{ - remote_domain_event_ret ret; - virDomainPtr dom; - virDomainEventPtr event = NULL; - memset (&ret, 0, sizeof ret); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_ret(xdr, &ret) ) { - error (conn, VIR_ERR_RPC, - _("remoteDomainProcessEvent: unmarshalling ret")); - return NULL; - } - - dom = get_nonnull_domain(conn,ret.dom); - if (!dom) - return NULL; - - event = virDomainEventNewFromDom(dom, ret.event, ret.detail); - - virDomainFree(dom); - return event; -} - -static void -remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr) -{ - struct private_data *priv = conn->privateData; - virDomainEventPtr event; - - event = remoteDomainReadEvent(conn, xdr); - if (!event) - return; - - DEBUG0("Calling domain event callbacks (no queue)"); - virDomainEventDispatch(event, priv->callbackList, - virDomainEventDispatchDefaultFunc, NULL); - virDomainEventFree(event); -} - -static void -remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr) -{ - struct private_data *priv = conn->privateData; - virDomainEventPtr event; - - event = remoteDomainReadEvent(conn, xdr); - if (!event) - return; - - if (virDomainEventQueuePush(priv->domainEvents, - event) < 0) { - DEBUG0("Error adding event to queue"); - virDomainEventFree(event); - } -} - -/** remoteDomainEventFired: - * - * The callback for monitoring the remote socket - * for event data - */ -void -remoteDomainEventFired(int watch, - int fd, - int event, - void *opaque) -{ - char buffer[REMOTE_MESSAGE_MAX]; - char buffer2[4]; - struct remote_message_header hdr; - XDR xdr; - int len; - - virConnectPtr conn = opaque; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - DEBUG("Event fired %d %d %d %X", watch, fd, event, event); - - if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { - DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " - "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); - virEventRemoveHandle(watch); - goto done; - } - - if (fd != priv->sock) { - virEventRemoveHandle(watch); - goto done; - } - - /* Read and deserialise length word. */ - if (really_read (conn, priv, 0, buffer2, sizeof buffer2) == -1) - goto done; - - xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); - if (!xdr_int (&xdr, &len)) { - error (conn, VIR_ERR_RPC, _("xdr_int (length word, reply)")); - goto done; - } - xdr_destroy (&xdr); - - /* Length includes length word - adjust to real length to read. */ - len -= 4; - - if (len < 0 || len > REMOTE_MESSAGE_MAX) { - error (conn, VIR_ERR_RPC, _("packet received from server too large")); - goto done; - } - - /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, 0, buffer, len) == -1) { - error (conn, VIR_ERR_RPC, _("error reading buffer from memory")); - goto done; - } - - /* Deserialise reply header. */ - xdrmem_create (&xdr, buffer, len, XDR_DECODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - error (conn, VIR_ERR_RPC, _("invalid header in event firing")); - goto done; - } - - if (hdr.proc == REMOTE_PROC_DOMAIN_EVENT && - hdr.direction == REMOTE_MESSAGE) { - DEBUG0("Encountered an async event"); - remoteDomainProcessEvent(conn, &xdr); - } else { - DEBUG0("invalid proc in event firing"); - error (conn, VIR_ERR_RPC, _("invalid proc in event firing")); - } - -done: - remoteDriverUnlock(priv); -} - -void -remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) -{ - virConnectPtr conn = opaque; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - virDomainEventQueueDispatch(priv->domainEvents, priv->callbackList, - virDomainEventDispatchDefaultFunc, NULL); - virEventUpdateTimeout(priv->eventFlushTimer, -1); - - remoteDriverUnlock(priv); -} diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -34,6 +34,7 @@ #include <poll.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/ioctl.h> #if HAVE_SYS_WAIT_H #include <sys/wait.h> #endif @@ -155,8 +156,28 @@ virArgvToString(const char *const *argv) return ret; } +int virSetNonBlock(int fd) { +#ifndef WIN32 + int flags; + if ((flags = fcntl(fd, F_GETFL)) < 0) + return -1; + flags |= O_NONBLOCK; + if ((fcntl(fd, F_SETFL, flags)) < 0) + return -1; +#else + unsigned long flag = 1; -#ifndef __MINGW32__ + /* This is actually Gnulib's replacement rpl_ioctl function. + * We can't call ioctlsocket directly in any case. + */ + if (ioctl (fd, FIONBIO, (void *) &flag) == -1) + return -1; +#endif + return 0; +} + + +#ifndef WIN32 static int virSetCloseExec(int fd) { int flags; @@ -168,16 +189,6 @@ static int virSetCloseExec(int fd) { return 0; } -static int virSetNonBlock(int fd) { - int flags; - if ((flags = fcntl(fd, F_GETFL)) < 0) - return -1; - flags |= O_NONBLOCK; - if ((fcntl(fd, F_SETFL, flags)) < 0) - return -1; - return 0; -} - static int __virExec(virConnectPtr conn, const char *const*argv, diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -38,6 +38,8 @@ enum { VIR_EXEC_DAEMON = (1 << 1), }; +int virSetNonBlock(int fd); + int virExec(virConnectPtr conn, const char *const*argv, const char *const*envp, -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Fri, Jan 16, 2009 at 12:11:16PM +0000, Daniel P. Berrange wrote:
@@ -114,6 +164,11 @@ struct private_data { virDomainEventQueuePtr domainEvents; /* Timer for flushing domainEvents queue */ int eventFlushTimer; + + /* List of threads currently doing dispatch */ + int wakeupSend; + int wakeupRead;
How about appending "FD" to indicate these are file descriptors. The names combined with the comment (which must apply to waitDispatch) made me wonder what they represented. Only when I saw them used in safewrite /saferead calls did I get it.
Yes, good idea - and its not really a list of threads either, so the comment is a little misleading :-)
+ /* Encode the length word. */ + xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); + if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, + _("xdr_int (length word)"));
I haven't done enough xdr* work to know, and man pages didn't provide an immediate answer: Is there no need to call xdr_destroy on this error path? I'd expect xdrmem_create to do any allocation, not xdr_int. There are many like this.
Yes, the 'error:' codepath should be calling 'xdr_destroy(&xdr)' to ensure free'ing of memory.
+ goto error; + } + xdr_destroy (&xdr); + + return rv; + +error: + VIR_FREE(ret); + return NULL;
The above should free rv, not ret:
VIR_FREE(rv);
Here is an update with those suggested renames & bug fixes in it.
It also addresses the error reporting issue mentioned in
http://www.redhat.com/archives/libvir-list/2009-January/msg00428.html
That code should not have been using DEBUG() - it now correctly raises a real error including the error string, not just errno. There were two other bugs with missing error raising in the path for sasl_encode/decode.
Everything upto this patch is committed, so this is diffed against current CVS.
All looks fine, but for a reverted change and some added strerror uses. While merging with your earlier changes (I effectively reverted the old 8/25 on a new branch, replacing it with this one and then rebased the remaining change sets), I got this conflict <<<<<<< HEAD:src/remote_internal.c if (pipe(wakeupFD) < 0) { errorf (conn, VIR_ERR_SYSTEM_ERROR, _("unable to make pipe %s"), strerror(errno)); ======= if (pipe(wakeup) < 0) { virReportSystemError(conn, errno, "%s", _("unable to make pipe")); >>>>>>> 03e5096... Remove use of strerror():src/remote_internal.c that suggests that your new wakeupFD-using change reintroduces a use of strerror that was previously removed. But it's no big deal, since we're planning to clean up the remaining strerror uses pretty soon.

On Tue, Jan 20, 2009 at 03:21:51PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
Here is an update with those suggested renames & bug fixes in it.
It also addresses the error reporting issue mentioned in
http://www.redhat.com/archives/libvir-list/2009-January/msg00428.html
That code should not have been using DEBUG() - it now correctly raises a real error including the error string, not just errno. There were two other bugs with missing error raising in the path for sasl_encode/decode.
Everything upto this patch is committed, so this is diffed against current CVS.
All looks fine, but for a reverted change and some added strerror uses. While merging with your earlier changes (I effectively reverted the old 8/25 on a new branch, replacing it with this one and then rebased the remaining change sets), I got this conflict
This patch wasn't intended to be able to just slot into the original series of patches - its only intended to work against current CVS head.
<<<<<<< HEAD:src/remote_internal.c if (pipe(wakeupFD) < 0) { errorf (conn, VIR_ERR_SYSTEM_ERROR, _("unable to make pipe %s"), strerror(errno)); ======= if (pipe(wakeup) < 0) { virReportSystemError(conn, errno, "%s", _("unable to make pipe")); >>>>>>> 03e5096... Remove use of strerror():src/remote_internal.c
that suggests that your new wakeupFD-using change reintroduces a use of strerror that was previously removed.
Nope, neither this patch, nor the original version uses the function virReportSystemError() since that is only added by a later patch in the series. There's no regression there. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
Nope, neither this patch, nor the original version uses the function virReportSystemError() since that is only added by a later patch in the series. There's no regression there.
Oh, good. Then go for it.

Jim Meyering <jim@meyering.net> wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch re-writes the code for dispatching RPC calls in the remote driver to allow use from multiple threads. Only one thread is allowed to send/recv on the socket at a time though. If another thread comes along it will put itself on a queue and go to sleep. The first thread may actually get around to transmitting the 2nd thread's request while it is waiting for its own reply. It may even get the 2nd threads reply, if its own RPC call is being really slow. So when a thread wakes up from sleeping, it has to check whether its own RPC call has already been processed. Likewise when a thread owning the socket finishes with its own wor, it may have to pass the buck to another thread. The upshot of this, is that we have mutliple RPC calls executing in parallel, and requests+reply are no longer guarenteed to be FIFO on the wire if talking to a new enough server.
This refactoring required use of a self-pipe/poll trick for sync between threads, but fortunately gnulib now provides this on Windows too, so there's no compatability problem there.
Quick summary: dense ;-) though lots of moved code.
I haven't finished,...
Modulo the things I mentioned, and the following nits, the rest looked fine. Typo in a comment: $ g grep -n EGAIN src/remote_internal.c:6110: * EGAIN printing a raw errno value (rather than a strerror-style string) in a DEBUG statement: $ g grep -n 'DEB.*, errno' src/remote_internal.c:6190: \ DEBUG("Poll unexpectedly failed %d\n", errno); comment typos: + /* Two reasons we can be woken up + * 1. Other thread has got our reply ready for us + * 2. Other thread is all done, and its out turn to + * be the dispatcher to finish waiting for + * out reply + */ s/its out/it's our/ s/out reply/our reply/

The virGetLastError() and virConnGetLastError() methods are not even remotely thread safe, and the virCopyLastError/virConnCopyLastError methods don't help in this goal, being open to a race condition. This patch changes the internal impl of the global error object to store its virError instance in a thread local variable. All errors are now reported against the global error object. In the public API entry points, we explicitly reset the global error object to ensure no stale errors are hanging around. In all error paths we also set a generic error, if the internal driver forget to set an explicit error. Finally we also copy the global error to the per connection error object for back-compatability, though the global object remains non-threadsafe for application access. src/datatypes.c | 31 src/datatypes.h | 15 src/libvirt.c | 3276 ++++++++++++++++++++++++++++++++--------------- src/virterror.c | 258 +++ src/virterror_internal.h | 5 tests/cpuset | 2 tests/read-bufsiz | 2 tests/start | 4 tests/undefine | 8 tests/vcpupin | 4 10 files changed, 2507 insertions(+), 1098 deletions(-) Daniel diff --git a/src/datatypes.c b/src/datatypes.c --- a/src/datatypes.c +++ b/src/datatypes.c @@ -195,8 +195,6 @@ virReleaseConnect(virConnectPtr conn) { virHashFree(conn->nodeDevices, (virHashDeallocator) virNodeDeviceFree); virResetError(&conn->err); - if (virLastErr.conn == conn) - virLastErr.conn = NULL; xmlFreeURI(conn->uri); @@ -219,7 +217,7 @@ virUnrefConnect(virConnectPtr conn) { int refs; if ((!VIR_IS_CONNECT(conn))) { - virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } virMutexLock(&conn->lock); @@ -253,7 +251,7 @@ virGetDomain(virConnectPtr conn, const c virDomainPtr ret = NULL; if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL)) { - virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } virMutexLock(&conn->lock); @@ -323,10 +321,6 @@ virReleaseDomain(virDomainPtr domain) { virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, _("domain missing from connection hash table")); - if (conn->err.dom == domain) - conn->err.dom = NULL; - if (virLastErr.dom == domain) - virLastErr.dom = NULL; domain->magic = -1; domain->id = -1; VIR_FREE(domain->name); @@ -358,7 +352,7 @@ virUnrefDomain(virDomainPtr domain) { int refs; if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibConnError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } virMutexLock(&domain->conn->lock); @@ -393,7 +387,7 @@ virGetNetwork(virConnectPtr conn, const virNetworkPtr ret = NULL; if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL)) { - virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } virMutexLock(&conn->lock); @@ -459,11 +453,6 @@ virReleaseNetwork(virNetworkPtr network) virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, _("network missing from connection hash table")); - if (conn->err.net == network) - conn->err.net = NULL; - if (virLastErr.net == network) - virLastErr.net = NULL; - network->magic = -1; VIR_FREE(network->name); VIR_FREE(network); @@ -494,7 +483,7 @@ virUnrefNetwork(virNetworkPtr network) { int refs; if (!VIR_IS_CONNECTED_NETWORK(network)) { - virLibConnError(network->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } virMutexLock(&network->conn->lock); @@ -530,7 +519,7 @@ virGetStoragePool(virConnectPtr conn, co virStoragePoolPtr ret = NULL; if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL)) { - virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } virMutexLock(&conn->lock); @@ -627,7 +616,7 @@ virUnrefStoragePool(virStoragePoolPtr po int refs; if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { - virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } virMutexLock(&pool->conn->lock); @@ -664,7 +653,7 @@ virGetStorageVol(virConnectPtr conn, con virStorageVolPtr ret = NULL; if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (key == NULL)) { - virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } virMutexLock(&conn->lock); @@ -765,7 +754,7 @@ virUnrefStorageVol(virStorageVolPtr vol) int refs; if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { - virLibConnError(vol->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } virMutexLock(&vol->conn->lock); @@ -801,7 +790,7 @@ virGetNodeDevice(virConnectPtr conn, con virNodeDevicePtr ret = NULL; if ((!VIR_IS_CONNECT(conn)) || (name == NULL)) { - virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } virMutexLock(&conn->lock); diff --git a/src/datatypes.h b/src/datatypes.h --- a/src/datatypes.h +++ b/src/datatypes.h @@ -95,6 +95,10 @@ * Internal structure associated to a connection */ struct _virConnect { + /* All the variables from here, until the 'lock' declaration + * are setup at time of connection open, and never changed + * since. Thus no need to lock when accessing them + */ unsigned int magic; /* specific value to check */ int flags; /* a set of connection flags */ xmlURIPtr uri; /* connection URI */ @@ -114,11 +118,6 @@ struct _virConnect { void * storagePrivateData; void * devMonPrivateData; - /* Per-connection error. */ - virError err; /* the last error */ - virErrorFunc handler; /* associated handlet */ - void *userData; /* the user data */ - /* * The lock mutex must be acquired before accessing/changing * any of members following this point, or changing the ref @@ -126,6 +125,12 @@ struct _virConnect { * this connection */ virMutex lock; + + /* Per-connection error. */ + virError err; /* the last error */ + virErrorFunc handler; /* associated handlet */ + void *userData; /* the user data */ + virHashTablePtr domains; /* hash table for known domains */ virHashTablePtr networks; /* hash table for known domains */ virHashTablePtr storagePools;/* hash table for known storage pools */ diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -256,7 +256,8 @@ virInitialize(void) initialized = 1; - if (virThreadInitialize() < 0) + if (virThreadInitialize() < 0 || + virErrorInitialize() < 0) return -1; #ifdef ENABLE_DEBUG @@ -847,6 +848,8 @@ do_open (const char *name, int i, res; virConnectPtr ret; + virResetLastError(); + ret = virGetConnect(); if (ret == NULL) return NULL; @@ -1000,17 +1003,8 @@ do_open (const char *name, failed: if (ret->driver) ret->driver->close (ret); - /* If no global error was set, copy any error set - in the connection object we're about to dispose of */ - if (virLastErr.code == VIR_ERR_OK) { - memcpy(&virLastErr, &ret->err, sizeof(ret->err)); - memset(&ret->err, 0, sizeof(ret->err)); - } - - /* Still no error set, then raise a generic error */ - if (virLastErr.code == VIR_ERR_OK) - virLibConnError (NULL, VIR_ERR_INTERNAL_ERROR, - _("unable to open connection")); + /* Ensure a global error is set in case driver forgot */ + virSetGlobalError(); virUnrefConnect(ret); @@ -1105,8 +1099,12 @@ virConnectClose(virConnectPtr conn) { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } if (conn->networkDriver) conn->networkDriver->close (conn); @@ -1128,12 +1126,20 @@ virConnectClose(virConnectPtr conn) int virDrvSupportsFeature (virConnectPtr conn, int feature) { + int ret; DEBUG("conn=%p, feature=%d", conn, feature); - if (!VIR_IS_CONNECT(conn)) - return (-1); - - return VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn, feature); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + ret = VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn, feature); + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return ret; } /** @@ -1153,6 +1159,8 @@ virConnectGetType(virConnectPtr conn) const char *ret; DEBUG("conn=%p", conn); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); @@ -1183,20 +1191,30 @@ virConnectGetVersion(virConnectPtr conn, { DEBUG("conn=%p, hvVer=%p", conn, hvVer); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; } if (hvVer == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->driver->version) - return conn->driver->version (conn, hvVer); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->version) { + int ret = conn->driver->version (conn, hvVer); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -1217,15 +1235,25 @@ virConnectGetHostname (virConnectPtr con { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return NULL; - } - - if (conn->driver->getHostname) - return conn->driver->getHostname (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return NULL; + } + + if (conn->driver->getHostname) { + char *ret = conn->driver->getHostname (conn); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -1248,8 +1276,9 @@ char * virConnectGetURI (virConnectPtr conn) { char *name; - - DEBUG("conn=%p", conn); + DEBUG("conn=%p", conn); + + virResetLastError(); if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -1259,15 +1288,23 @@ virConnectGetURI (virConnectPtr conn) /* Drivers may override getURI, but if they don't then * we provide a default implementation. */ - if (conn->driver->getURI) - return conn->driver->getURI (conn); + if (conn->driver->getURI) { + name = conn->driver->getURI (conn); + if (!name) + goto error; + } name = (char *)xmlSaveUri(conn->uri); if (!name) { virLibConnError (conn, VIR_ERR_NO_MEMORY, __FUNCTION__); - return NULL; + goto error; } return name; + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return NULL; } /** @@ -1287,15 +1324,24 @@ virConnectGetMaxVcpus(virConnectPtr conn { DEBUG("conn=%p, type=%s", conn, type); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->driver->getMaxVcpus) - return conn->driver->getMaxVcpus (conn, type); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + if (conn->driver->getMaxVcpus) { + int ret = conn->driver->getMaxVcpus (conn, type); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -1314,20 +1360,29 @@ virConnectListDomains(virConnectPtr conn { DEBUG("conn=%p, ids=%p, maxids=%d", conn, ids, maxids); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; } if ((ids == NULL) || (maxids < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->driver->listDomains) - return conn->driver->listDomains (conn, ids, maxids); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->listDomains) { + int ret = conn->driver->listDomains (conn, ids, maxids); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -1344,15 +1399,24 @@ virConnectNumOfDomains(virConnectPtr con { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->driver->numOfDomains) - return conn->driver->numOfDomains (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + goto error; + } + + if (conn->driver->numOfDomains) { + int ret = conn->driver->numOfDomains (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -1375,7 +1439,9 @@ virDomainGetConnect (virDomainPtr dom) { DEBUG("dom=%p", dom); - if (!VIR_IS_DOMAIN (dom)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return NULL; } @@ -1403,23 +1469,33 @@ virDomainCreateXML(virConnectPtr conn, c { DEBUG("conn=%p, xmlDesc=%s, flags=%d", conn, xmlDesc, flags); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (xmlDesc == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); - } - - if (conn->driver->domainCreateXML) - return conn->driver->domainCreateXML (conn, xmlDesc, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainCreateXML) { + virDomainPtr ret; + ret = conn->driver->domainCreateXML (conn, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -1459,19 +1535,30 @@ virDomainLookupByID(virConnectPtr conn, { DEBUG("conn=%p, id=%d", conn, id); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (id < 0) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->driver->domainLookupByID) - return conn->driver->domainLookupByID (conn, id); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->domainLookupByID) { + virDomainPtr ret; + ret = conn->driver->domainLookupByID (conn, id); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -1490,19 +1577,30 @@ virDomainLookupByUUID(virConnectPtr conn { DEBUG("conn=%p, uuid=%s", conn, uuid); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (uuid == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->driver->domainLookupByUUID) - return conn->driver->domainLookupByUUID (conn, uuid); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->domainLookupByUUID) { + virDomainPtr ret; + ret = conn->driver->domainLookupByUUID (conn, uuid); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -1525,14 +1623,15 @@ virDomainLookupByUUIDString(virConnectPt DEBUG("conn=%p, uuidstr=%s", conn, uuidstr); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (uuidstr == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - + goto error; } /* XXX: sexpr_uuid() also supports 'xxxx-xxxx-xxxx-xxxx' format. * We needn't it here. Right? @@ -1550,12 +1649,17 @@ virDomainLookupByUUIDString(virConnectPt if (ret!=VIR_UUID_BUFLEN) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } for (i = 0; i < VIR_UUID_BUFLEN; i++) uuid[i] = raw[i] & 0xFF; return virDomainLookupByUUID(conn, &uuid[0]); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return NULL; } /** @@ -1573,19 +1677,30 @@ virDomainLookupByName(virConnectPtr conn { DEBUG("conn=%p, name=%s", conn, name); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (name == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->driver->domainLookupByName) - return conn->driver->domainLookupByName (conn, name); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->domainLookupByName) { + virDomainPtr dom; + dom = conn->driver->domainLookupByName (conn, name); + if (!dom) + goto error; + return dom; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -1607,21 +1722,32 @@ virDomainDestroy(virDomainPtr domain) DEBUG("domain=%p", domain); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - if (conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->driver->domainDestroy) - return conn->driver->domainDestroy (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainDestroy) { + int ret; + ret = conn->driver->domainDestroy (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -1639,12 +1765,14 @@ virDomainFree(virDomainPtr domain) { DEBUG("domain=%p", domain); - if (!VIR_IS_DOMAIN(domain)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (-1); } if (virUnrefDomain(domain) < 0) - return (-1); + return -1; return(0); } @@ -1666,21 +1794,32 @@ virDomainSuspend(virDomainPtr domain) virConnectPtr conn; DEBUG("domain=%p", domain); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainSuspend) - return conn->driver->domainSuspend (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSuspend) { + int ret; + ret = conn->driver->domainSuspend (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -1700,21 +1839,32 @@ virDomainResume(virDomainPtr domain) virConnectPtr conn; DEBUG("domain=%p", domain); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainResume) - return conn->driver->domainResume (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainResume) { + int ret; + ret = conn->driver->domainResume (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -1737,18 +1887,20 @@ virDomainSave(virDomainPtr domain, const virConnectPtr conn; DEBUG("domain=%p, to=%s", domain, to); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } conn = domain->conn; if (to == NULL) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } /* @@ -1771,10 +1923,19 @@ virDomainSave(virDomainPtr domain, const } - if (conn->driver->domainSave) - return conn->driver->domainSave (domain, to); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->domainSave) { + int ret; + ret = conn->driver->domainSave (domain, to); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -1793,17 +1954,19 @@ virDomainRestore(virConnectPtr conn, con char filepath[4096]; DEBUG("conn=%p, from=%s", conn, from); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (from == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } /* @@ -1814,21 +1977,36 @@ virDomainRestore(virConnectPtr conn, con unsigned int len, t; t = strlen(from); - if (getcwd(filepath, sizeof(filepath) - (t + 3)) == NULL) - return (-1); + if (getcwd(filepath, sizeof(filepath) - (t + 3)) == NULL) { + virLibConnError(conn, VIR_ERR_SYSTEM_ERROR, + _("cannot get working directory")); + goto error; + } len = strlen(filepath); /* that should be covered by getcwd() semantic, but be 100% sure */ - if (len > sizeof(filepath) - (t + 3)) - return (-1); + if (len > sizeof(filepath) - (t + 3)) { + virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, + _("path too long")); + goto error; + } filepath[len] = '/'; strcpy(&filepath[len + 1], from); from = &filepath[0]; } - if (conn->driver->domainRestore) - return conn->driver->domainRestore (conn, from); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->domainRestore) { + int ret; + ret = conn->driver->domainRestore (conn, from); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -1851,18 +2029,20 @@ virDomainCoreDump(virDomainPtr domain, c virConnectPtr conn; DEBUG("domain=%p, to=%s, flags=%d", domain, to, flags); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } conn = domain->conn; if (to == NULL) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } /* @@ -1873,22 +2053,37 @@ virDomainCoreDump(virDomainPtr domain, c unsigned int len, t; t = strlen(to); - if (getcwd(filepath, sizeof(filepath) - (t + 3)) == NULL) - return (-1); + if (getcwd(filepath, sizeof(filepath) - (t + 3)) == NULL) { + virLibDomainError(domain, VIR_ERR_SYSTEM_ERROR, + _("cannot get current directory")); + goto error; + } len = strlen(filepath); /* that should be covered by getcwd() semantic, but be 100% sure */ - if (len > sizeof(filepath) - (t + 3)) - return (-1); + if (len > sizeof(filepath) - (t + 3)) { + virLibDomainError(domain, VIR_ERR_INTERNAL_ERROR, + _("path too long")); + goto error; + } filepath[len] = '/'; strcpy(&filepath[len + 1], to); to = &filepath[0]; } - if (conn->driver->domainCoreDump) - return conn->driver->domainCoreDump (domain, to, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->domainCoreDump) { + int ret; + ret = conn->driver->domainCoreDump (domain, to, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -1911,21 +2106,32 @@ virDomainShutdown(virDomainPtr domain) virConnectPtr conn; DEBUG("domain=%p", domain); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainShutdown) - return conn->driver->domainShutdown (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainShutdown) { + int ret; + ret = conn->driver->domainShutdown (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -1946,21 +2152,32 @@ virDomainReboot(virDomainPtr domain, uns virConnectPtr conn; DEBUG("domain=%p, flags=%u", domain, flags); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainReboot) - return conn->driver->domainReboot (domain, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainReboot) { + int ret; + ret = conn->driver->domainReboot (domain, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -1978,6 +2195,8 @@ virDomainGetName(virDomainPtr domain) { DEBUG("domain=%p", domain); + virResetLastError(); + if (!VIR_IS_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (NULL); @@ -1999,12 +2218,16 @@ virDomainGetUUID(virDomainPtr domain, un { DEBUG("domain=%p, uuid=%p", domain, uuid); + virResetLastError(); + if (!VIR_IS_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (-1); } if (uuid == NULL) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return (-1); } @@ -2029,20 +2252,27 @@ virDomainGetUUIDString(virDomainPtr doma unsigned char uuid[VIR_UUID_BUFLEN]; DEBUG("domain=%p, buf=%p", domain, buf); + virResetLastError(); + if (!VIR_IS_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (-1); } if (buf == NULL) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } if (virDomainGetUUID(domain, &uuid[0])) - return (-1); + goto error; virUUIDFormat(uuid, buf); return (0); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); + return -1; } /** @@ -2058,6 +2288,8 @@ virDomainGetID(virDomainPtr domain) { DEBUG("domain=%p", domain); + virResetLastError(); + if (!VIR_IS_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return ((unsigned int) -1); @@ -2080,17 +2312,28 @@ virDomainGetOSType(virDomainPtr domain) virConnectPtr conn; DEBUG("domain=%p", domain); - if (!VIR_IS_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (NULL); - } - - conn = domain->conn; - - if (conn->driver->domainGetOSType) - return conn->driver->domainGetOSType (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (NULL); + } + + conn = domain->conn; + + if (conn->driver->domainGetOSType) { + char *ret; + ret = conn->driver->domainGetOSType (domain); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return NULL; } @@ -2110,6 +2353,8 @@ virDomainGetMaxMemory(virDomainPtr domai virConnectPtr conn; DEBUG("domain=%p", domain); + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (0); @@ -2117,10 +2362,19 @@ virDomainGetMaxMemory(virDomainPtr domai conn = domain->conn; - if (conn->driver->domainGetMaxMemory) - return conn->driver->domainGetMaxMemory (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->domainGetMaxMemory) { + unsigned long ret; + ret = conn->driver->domainGetMaxMemory (domain); + if (ret == 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return 0; } @@ -2142,28 +2396,35 @@ virDomainSetMaxMemory(virDomainPtr domai virConnectPtr conn; DEBUG("domain=%p, memory=%lu", domain, memory); - if (domain == NULL) { - TODO - return (-1); - } - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (memory < 4096) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - conn = domain->conn; - - if (conn->driver->domainSetMaxMemory) - return conn->driver->domainSetMaxMemory (domain, memory); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + conn = domain->conn; + + if (conn->driver->domainSetMaxMemory) { + int ret; + ret = conn->driver->domainSetMaxMemory (domain, memory); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -2185,29 +2446,36 @@ virDomainSetMemory(virDomainPtr domain, virConnectPtr conn; DEBUG("domain=%p, memory=%lu", domain, memory); - if (domain == NULL) { - TODO - return (-1); - } - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (memory < 4096) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainSetMemory) - return conn->driver->domainSetMemory (domain, memory); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSetMemory) { + int ret; + ret = conn->driver->domainSetMemory (domain, memory); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -2228,23 +2496,34 @@ virDomainGetInfo(virDomainPtr domain, vi virConnectPtr conn; DEBUG("domain=%p, info=%p", domain, info); + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (-1); } if (info == NULL) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } memset(info, 0, sizeof(virDomainInfo)); conn = domain->conn; - if (conn->driver->domainGetInfo) - return conn->driver->domainGetInfo (domain, info); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->domainGetInfo) { + int ret; + ret = conn->driver->domainGetInfo (domain, info); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -2265,17 +2544,28 @@ virDomainGetXMLDesc(virDomainPtr domain, virConnectPtr conn; DEBUG("domain=%p, flags=%d", domain, flags); - if (!VIR_IS_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (NULL); - } - - conn = domain->conn; - - if (conn->driver->domainDumpXML) - return conn->driver->domainDumpXML (domain, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (NULL); + } + + conn = domain->conn; + + if (conn->driver->domainDumpXML) { + char *ret; + ret = conn->driver->domainDumpXML (domain, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return NULL; } @@ -2344,24 +2634,26 @@ virDomainMigrate (virDomainPtr domain, DEBUG("domain=%p, dconn=%p, flags=%lu, dname=%s, uri=%s, bandwidth=%lu", domain, dconn, flags, dname, uri, bandwidth); - if (!VIR_IS_DOMAIN (domain)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return NULL; } conn = domain->conn; /* Source connection. */ if (!VIR_IS_CONNECT (dconn)) { virLibConnError (conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return NULL; - } - - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return NULL; + goto error; + } + + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (dconn->flags & VIR_CONNECT_RO) { /* NB, delibrately report error against source object, not dest here */ virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return NULL; + goto error; } /* Check that migration is supported by both drivers. */ @@ -2377,7 +2669,7 @@ virDomainMigrate (virDomainPtr domain, version = 2; else { virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; + goto error; } /* Prepare the migration. @@ -2412,13 +2704,13 @@ virDomainMigrate (virDomainPtr domain, */ if (!conn->driver->domainDumpXML) { virLibConnError (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__); - return NULL; + goto error; } dom_xml = conn->driver->domainDumpXML (domain, VIR_DOMAIN_XML_SECURE); if (!dom_xml) - return NULL; + goto error; ret = dconn->driver->domainMigratePrepare2 (dconn, &cookie, &cookielen, uri, &uri_out, flags, dname, @@ -2468,6 +2760,11 @@ virDomainMigrate (virDomainPtr domain, free (uri_out); free (cookie); return ddomain; + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); + return NULL; } /* @@ -2486,6 +2783,8 @@ virDomainMigratePrepare (virConnectPtr d { DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, flags=%lu, dname=%s, bandwidth=%lu", dconn, cookie, cookielen, uri_in, uri_out, flags, dname, bandwidth); + virResetLastError(); + if (!VIR_IS_CONNECT (dconn)) { virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return -1; @@ -2493,15 +2792,24 @@ virDomainMigratePrepare (virConnectPtr d if (dconn->flags & VIR_CONNECT_RO) { virLibConnError(dconn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return -1; - } - - if (dconn->driver->domainMigratePrepare) - return dconn->driver->domainMigratePrepare (dconn, cookie, cookielen, - uri_in, uri_out, - flags, dname, bandwidth); + goto error; + } + + if (dconn->driver->domainMigratePrepare) { + int ret; + ret = dconn->driver->domainMigratePrepare (dconn, cookie, cookielen, + uri_in, uri_out, + flags, dname, bandwidth); + if (ret < 0) + goto error; + return ret; + } virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dconn); return -1; } @@ -2521,7 +2829,9 @@ virDomainMigratePerform (virDomainPtr do virConnectPtr conn; DEBUG("domain=%p, cookie=%p, cookielen=%d, uri=%s, flags=%lu, dname=%s, bandwidth=%lu", domain, cookie, cookielen, uri, flags, dname, bandwidth); - if (!VIR_IS_DOMAIN (domain)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return -1; } @@ -2529,15 +2839,24 @@ virDomainMigratePerform (virDomainPtr do if (domain->conn->flags & VIR_CONNECT_RO) { virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return -1; - } - - if (conn->driver->domainMigratePerform) - return conn->driver->domainMigratePerform (domain, cookie, cookielen, - uri, - flags, dname, bandwidth); + goto error; + } + + if (conn->driver->domainMigratePerform) { + int ret; + ret = conn->driver->domainMigratePerform (domain, cookie, cookielen, + uri, + flags, dname, bandwidth); + if (ret < 0) + goto error; + return ret; + } virLibDomainError (domain, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -2555,6 +2874,8 @@ virDomainMigrateFinish (virConnectPtr dc { DEBUG("dconn=%p, dname=%s, cookie=%p, cookielen=%d, uri=%s, flags=%lu", dconn, dname, cookie, cookielen, uri, flags); + virResetLastError(); + if (!VIR_IS_CONNECT (dconn)) { virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return NULL; @@ -2562,15 +2883,24 @@ virDomainMigrateFinish (virConnectPtr dc if (dconn->flags & VIR_CONNECT_RO) { virLibConnError(dconn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return NULL; - } - - if (dconn->driver->domainMigrateFinish) - return dconn->driver->domainMigrateFinish (dconn, dname, - cookie, cookielen, - uri, flags); + goto error; + } + + if (dconn->driver->domainMigrateFinish) { + virDomainPtr ret; + ret = dconn->driver->domainMigrateFinish (dconn, dname, + cookie, cookielen, + uri, flags); + if (!ret) + goto error; + return ret; + } virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dconn); return NULL; } @@ -2592,6 +2922,8 @@ virDomainMigratePrepare2 (virConnectPtr { DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, flags=%lu, dname=%s, bandwidth=%lu, dom_xml=%s", dconn, cookie, cookielen, uri_in, uri_out, flags, dname, bandwidth, dom_xml); + virResetLastError(); + if (!VIR_IS_CONNECT (dconn)) { virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return -1; @@ -2599,16 +2931,25 @@ virDomainMigratePrepare2 (virConnectPtr if (dconn->flags & VIR_CONNECT_RO) { virLibConnError(dconn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return -1; - } - - if (dconn->driver->domainMigratePrepare2) - return dconn->driver->domainMigratePrepare2 (dconn, cookie, cookielen, - uri_in, uri_out, - flags, dname, bandwidth, - dom_xml); + goto error; + } + + if (dconn->driver->domainMigratePrepare2) { + int ret; + ret = dconn->driver->domainMigratePrepare2 (dconn, cookie, cookielen, + uri_in, uri_out, + flags, dname, bandwidth, + dom_xml); + if (ret < 0) + goto error; + return ret; + } virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dconn); return -1; } @@ -2627,6 +2968,8 @@ virDomainMigrateFinish2 (virConnectPtr d { DEBUG("dconn=%p, dname=%s, cookie=%p, cookielen=%d, uri=%s, flags=%lu, retcode=%d", dconn, dname, cookie, cookielen, uri, flags, retcode); + virResetLastError(); + if (!VIR_IS_CONNECT (dconn)) { virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return NULL; @@ -2634,16 +2977,25 @@ virDomainMigrateFinish2 (virConnectPtr d if (dconn->flags & VIR_CONNECT_RO) { virLibConnError(dconn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return NULL; - } - - if (dconn->driver->domainMigrateFinish2) - return dconn->driver->domainMigrateFinish2 (dconn, dname, - cookie, cookielen, - uri, flags, - retcode); + goto error; + } + + if (dconn->driver->domainMigrateFinish2) { + virDomainPtr ret; + ret = dconn->driver->domainMigrateFinish2 (dconn, dname, + cookie, cookielen, + uri, flags, + retcode); + if (!ret) + goto error; + return ret; + } virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dconn); return NULL; } @@ -2662,19 +3014,30 @@ virNodeGetInfo(virConnectPtr conn, virNo { DEBUG("conn=%p, info=%p", conn, info); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); } if (info == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->driver->nodeGetInfo) - return conn->driver->nodeGetInfo (conn, info); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->nodeGetInfo) { + int ret; + ret = conn->driver->nodeGetInfo (conn, info); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -2693,15 +3056,26 @@ virConnectGetCapabilities (virConnectPtr { DEBUG("conn=%p", conn); + virResetLastError(); + if (!VIR_IS_CONNECT (conn)) { virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return NULL; } - if (conn->driver->getCapabilities) - return conn->driver->getCapabilities (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->getCapabilities) { + char *ret; + ret = conn->driver->getCapabilities (conn); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -2718,15 +3092,26 @@ virNodeGetFreeMemory(virConnectPtr conn) { DEBUG("conn=%p", conn); + virResetLastError(); + if (!VIR_IS_CONNECT (conn)) { virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return 0; } - if (conn->driver->getFreeMemory) - return conn->driver->getFreeMemory (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->driver->getFreeMemory) { + unsigned long long ret; + ret = conn->driver->getFreeMemory (conn); + if (ret == 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return 0; } @@ -2746,6 +3131,8 @@ virDomainGetSchedulerType(virDomainPtr d char *schedtype; DEBUG("domain=%p, nparams=%p", domain, nparams); + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return NULL; @@ -2754,10 +3141,16 @@ virDomainGetSchedulerType(virDomainPtr d if (conn->driver->domainGetSchedulerType){ schedtype = conn->driver->domainGetSchedulerType (domain, nparams); + if (!schedtype) + goto error; return schedtype; } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return NULL; } @@ -2783,16 +3176,27 @@ virDomainGetSchedulerParameters(virDomai virConnectPtr conn; DEBUG("domain=%p, params=%p, nparams=%p", domain, params, nparams); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return -1; - } - conn = domain->conn; - - if (conn->driver->domainGetSchedulerParameters) - return conn->driver->domainGetSchedulerParameters (domain, params, nparams); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + conn = domain->conn; + + if (conn->driver->domainGetSchedulerParameters) { + int ret; + ret = conn->driver->domainGetSchedulerParameters (domain, params, nparams); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -2815,20 +3219,31 @@ virDomainSetSchedulerParameters(virDomai virConnectPtr conn; DEBUG("domain=%p, params=%p, nparams=%d", domain, params, nparams); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return -1; - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return -1; - } - conn = domain->conn; - - if (conn->driver->domainSetSchedulerParameters) - return conn->driver->domainSetSchedulerParameters (domain, params, nparams); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + conn = domain->conn; + + if (conn->driver->domainSetSchedulerParameters) { + int ret; + ret = conn->driver->domainSetSchedulerParameters (domain, params, nparams); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -2864,25 +3279,31 @@ virDomainBlockStats (virDomainPtr dom, c struct _virDomainBlockStats stats2 = { -1, -1, -1, -1, -1 }; DEBUG("domain=%p, path=%s, stats=%p, size=%zi", dom, path, stats, size); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } if (!stats || size > sizeof stats2) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, __FUNCTION__); - return -1; - } - if (!VIR_IS_CONNECTED_DOMAIN (dom)) { - virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return -1; + goto error; } conn = dom->conn; if (conn->driver->domainBlockStats) { if (conn->driver->domainBlockStats (dom, path, &stats2) == -1) - return -1; + goto error; memcpy (stats, &stats2, size); return 0; } virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dom->conn); return -1; } @@ -2916,25 +3337,31 @@ virDomainInterfaceStats (virDomainPtr do -1, -1, -1, -1 }; DEBUG("domain=%p, path=%s, stats=%p, size=%zi", dom, path, stats, size); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } if (!stats || size > sizeof stats2) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, __FUNCTION__); - return -1; - } - if (!VIR_IS_CONNECTED_DOMAIN (dom)) { - virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return -1; + goto error; } conn = dom->conn; if (conn->driver->domainInterfaceStats) { if (conn->driver->domainInterfaceStats (dom, path, &stats2) == -1) - return -1; + goto error; memcpy (stats, &stats2, size); return 0; } virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dom->conn); return -1; } @@ -2989,6 +3416,8 @@ virDomainBlockPeek (virDomainPtr dom, DEBUG("domain=%p, path=%s, offset=%lld, size=%zi, buffer=%p", dom, path, offset, size, buffer); + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return -1; @@ -2997,33 +3426,42 @@ virDomainBlockPeek (virDomainPtr dom, if (dom->conn->flags & VIR_CONNECT_RO) { virLibDomainError(dom, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + goto error; } if (!path) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, _("path is NULL")); - return -1; + goto error; } if (flags != 0) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, _("flags must be zero")); - return -1; + goto error; } /* Allow size == 0 as an access test. */ if (size > 0 && !buffer) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, _("buffer is NULL")); - return -1; - } - - if (conn->driver->domainBlockPeek) - return conn->driver->domainBlockPeek (dom, path, offset, size, - buffer, flags); + goto error; + } + + if (conn->driver->domainBlockPeek) { + int ret; + ret =conn->driver->domainBlockPeek (dom, path, offset, size, + buffer, flags); + if (ret < 0) + goto error; + return ret; + } virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dom->conn); return -1; } @@ -3069,6 +3507,8 @@ virDomainMemoryPeek (virDomainPtr dom, DEBUG ("domain=%p, start=%lld, size=%zi, buffer=%p, flags=%d", dom, start, size, buffer, flags); + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return -1; @@ -3077,7 +3517,7 @@ virDomainMemoryPeek (virDomainPtr dom, if (dom->conn->flags & VIR_CONNECT_RO) { virLibDomainError(dom, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + goto error; } /* Flags must be VIR_MEMORY_VIRTUAL at the moment. @@ -3104,21 +3544,30 @@ virDomainMemoryPeek (virDomainPtr dom, if (flags != VIR_MEMORY_VIRTUAL) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, _("flags parameter must be VIR_MEMORY_VIRTUAL")); - return -1; + goto error; } /* Allow size == 0 as an access test. */ if (size > 0 && !buffer) { virLibDomainError (dom, VIR_ERR_INVALID_ARG, _("buffer is NULL but size is non-zero")); - return -1; - } - - if (conn->driver->domainMemoryPeek) - return conn->driver->domainMemoryPeek (dom, start, size, - buffer, flags); + goto error; + } + + if (conn->driver->domainMemoryPeek) { + int ret; + ret = conn->driver->domainMemoryPeek (dom, start, size, + buffer, flags); + if (ret < 0) + goto error; + return ret; + } virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dom->conn); return -1; } @@ -3144,23 +3593,34 @@ virDomainPtr virDomainDefineXML(virConnectPtr conn, const char *xml) { DEBUG("conn=%p, xml=%s", conn, xml); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (xml == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->driver->domainDefineXML) - return conn->driver->domainDefineXML (conn, xml); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->domainDefineXML) { + virDomainPtr ret; + ret = conn->driver->domainDefineXML (conn, xml); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -3177,20 +3637,31 @@ virDomainUndefine(virDomainPtr domain) { virConnectPtr conn; DEBUG("domain=%p", domain); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - conn = domain->conn; - if (conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->driver->domainUndefine) - return conn->driver->domainUndefine (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainUndefine) { + int ret; + ret = conn->driver->domainUndefine (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3207,15 +3678,26 @@ virConnectNumOfDefinedDomains(virConnect { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->driver->numOfDefinedDomains) - return conn->driver->numOfDefinedDomains (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->driver->numOfDefinedDomains) { + int ret; + ret = conn->driver->numOfDefinedDomains (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3235,6 +3717,8 @@ virConnectListDefinedDomains(virConnectP int maxnames) { DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); @@ -3242,13 +3726,22 @@ virConnectListDefinedDomains(virConnectP if ((names == NULL) || (maxnames < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->driver->listDefinedDomains) - return conn->driver->listDefinedDomains (conn, names, maxnames); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->listDefinedDomains) { + int ret; + ret = conn->driver->listDefinedDomains (conn, names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3266,24 +3759,31 @@ virDomainCreate(virDomainPtr domain) { virConnectPtr conn; DEBUG("domain=%p", domain); - if (domain == NULL) { - TODO - return (-1); - } - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - conn = domain->conn; - if (conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->driver->domainCreate) - return conn->driver->domainCreate (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainCreate) { + int ret; + ret = conn->driver->domainCreate (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3305,21 +3805,32 @@ virDomainGetAutostart(virDomainPtr domai virConnectPtr conn; DEBUG("domain=%p, autostart=%p", domain, autostart); - if (!VIR_IS_DOMAIN(domain)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (-1); } if (!autostart) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainGetAutostart) - return conn->driver->domainGetAutostart (domain, autostart); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainGetAutostart) { + int ret; + ret = conn->driver->domainGetAutostart (domain, autostart); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3340,22 +3851,33 @@ virDomainSetAutostart(virDomainPtr domai virConnectPtr conn; DEBUG("domain=%p, autostart=%d", domain, autostart); - if (!VIR_IS_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->driver->domainSetAutostart) - return conn->driver->domainSetAutostart (domain, autostart); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + + conn = domain->conn; + + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainSetAutostart) { + int ret; + ret = conn->driver->domainSetAutostart (domain, autostart); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3378,29 +3900,36 @@ virDomainSetVcpus(virDomainPtr domain, u virConnectPtr conn; DEBUG("domain=%p, nvcpus=%u", domain, nvcpus); - if (domain == NULL) { - TODO - return (-1); - } - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (nvcpus < 1) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - conn = domain->conn; - - if (conn->driver->domainSetVcpus) - return conn->driver->domainSetVcpus (domain, nvcpus); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + conn = domain->conn; + + if (conn->driver->domainSetVcpus) { + int ret; + ret = conn->driver->domainSetVcpus (domain, nvcpus); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3429,30 +3958,37 @@ virDomainPinVcpu(virDomainPtr domain, un virConnectPtr conn; DEBUG("domain=%p, vcpu=%u, cpumap=%p, maplen=%d", domain, vcpu, cpumap, maplen); - if (domain == NULL) { - TODO - return (-1); - } - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if ((vcpu > 32000) || (cpumap == NULL) || (maplen < 1)) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainPinVcpu) - return conn->driver->domainPinVcpu (domain, vcpu, cpumap, maplen); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainPinVcpu) { + int ret; + ret = conn->driver->domainPinVcpu (domain, vcpu, cpumap, maplen); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3484,30 +4020,37 @@ virDomainGetVcpus(virDomainPtr domain, v virConnectPtr conn; DEBUG("domain=%p, info=%p, maxinfo=%d, cpumaps=%p, maplen=%d", domain, info, maxinfo, cpumaps, maplen); - if (domain == NULL) { - TODO - return (-1); - } + virResetLastError(); + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); return (-1); } if ((info == NULL) || (maxinfo < 1)) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } if (cpumaps != NULL && maplen < 1) { virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainGetVcpus) - return conn->driver->domainGetVcpus (domain, info, maxinfo, - cpumaps, maplen); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainGetVcpus) { + int ret; + ret = conn->driver->domainGetVcpus (domain, info, maxinfo, + cpumaps, maplen); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3529,17 +4072,28 @@ virDomainGetMaxVcpus(virDomainPtr domain virConnectPtr conn; DEBUG("domain=%p", domain); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - - conn = domain->conn; - - if (conn->driver->domainGetMaxVcpus) - return conn->driver->domainGetMaxVcpus (domain); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + + conn = domain->conn; + + if (conn->driver->domainGetMaxVcpus) { + int ret; + ret = conn->driver->domainGetMaxVcpus (domain); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3559,20 +4113,31 @@ virDomainAttachDevice(virDomainPtr domai virConnectPtr conn; DEBUG("domain=%p, xml=%s", domain, xml); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - conn = domain->conn; - - if (conn->driver->domainAttachDevice) - return conn->driver->domainAttachDevice (domain, xml); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + conn = domain->conn; + + if (conn->driver->domainAttachDevice) { + int ret; + ret = conn->driver->domainAttachDevice (domain, xml); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3591,20 +4156,31 @@ virDomainDetachDevice(virDomainPtr domai virConnectPtr conn; DEBUG("domain=%p, xml=%s", domain, xml); - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) { - virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - conn = domain->conn; - - if (conn->driver->domainDetachDevice) - return conn->driver->domainDetachDevice (domain, xml); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + conn = domain->conn; + + if (conn->driver->domainDetachDevice) { + int ret; + ret = conn->driver->domainDetachDevice (domain, xml); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); return -1; } @@ -3633,6 +4209,8 @@ virNodeGetCellsFreeMemory(virConnectPtr DEBUG("conn=%p, freeMems=%p, startCell=%d, maxCells=%d", conn, freeMems, startCell, maxCells); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); @@ -3640,13 +4218,22 @@ virNodeGetCellsFreeMemory(virConnectPtr if ((freeMems == NULL) || (maxCells <= 0) || (startCell < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->driver->nodeGetCellsFreeMemory) - return conn->driver->nodeGetCellsFreeMemory (conn, freeMems, startCell, maxCells); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->driver->nodeGetCellsFreeMemory) { + int ret; + ret = conn->driver->nodeGetCellsFreeMemory (conn, freeMems, startCell, maxCells); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3669,7 +4256,9 @@ virNetworkGetConnect (virNetworkPtr net) { DEBUG("net=%p", net); - if (!VIR_IS_NETWORK (net)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_NETWORK (net)) { virLibNetworkError (NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return NULL; } @@ -3689,15 +4278,26 @@ virConnectNumOfNetworks(virConnectPtr co { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->numOfNetworks) - return conn->networkDriver->numOfNetworks (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->networkDriver && conn->networkDriver->numOfNetworks) { + int ret; + ret = conn->networkDriver->numOfNetworks (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3716,6 +4316,8 @@ virConnectListNetworks(virConnectPtr con { DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); @@ -3723,13 +4325,22 @@ virConnectListNetworks(virConnectPtr con if ((names == NULL) || (maxnames < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->listNetworks) - return conn->networkDriver->listNetworks (conn, names, maxnames); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->listNetworks) { + int ret; + ret = conn->networkDriver->listNetworks (conn, names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3746,15 +4357,26 @@ virConnectNumOfDefinedNetworks(virConnec { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->numOfDefinedNetworks) - return conn->networkDriver->numOfDefinedNetworks (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->networkDriver && conn->networkDriver->numOfDefinedNetworks) { + int ret; + ret = conn->networkDriver->numOfDefinedNetworks (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3774,6 +4396,8 @@ virConnectListDefinedNetworks(virConnect { DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); @@ -3781,14 +4405,23 @@ virConnectListDefinedNetworks(virConnect if ((names == NULL) || (maxnames < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->listDefinedNetworks) - return conn->networkDriver->listDefinedNetworks (conn, - names, maxnames); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->listDefinedNetworks) { + int ret; + ret = conn->networkDriver->listDefinedNetworks (conn, + names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -3807,19 +4440,30 @@ virNetworkLookupByName(virConnectPtr con { DEBUG("conn=%p, name=%s", conn, name); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (name == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->networkDriver && conn->networkDriver->networkLookupByName) - return conn->networkDriver->networkLookupByName (conn, name); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkLookupByName) { + virNetworkPtr ret; + ret = conn->networkDriver->networkLookupByName (conn, name); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -3838,19 +4482,30 @@ virNetworkLookupByUUID(virConnectPtr con { DEBUG("conn=%p, uuid=%s", conn, uuid); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (uuid == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->networkDriver && conn->networkDriver->networkLookupByUUID) - return conn->networkDriver->networkLookupByUUID (conn, uuid); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkLookupByUUID){ + virNetworkPtr ret; + ret = conn->networkDriver->networkLookupByUUID (conn, uuid); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -3872,13 +4527,15 @@ virNetworkLookupByUUIDString(virConnectP int ret; DEBUG("conn=%p, uuidstr=%s", conn, uuidstr); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (uuidstr == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } /* XXX: sexpr_uuid() also supports 'xxxx-xxxx-xxxx-xxxx' format. @@ -3897,12 +4554,17 @@ virNetworkLookupByUUIDString(virConnectP if (ret!=VIR_UUID_BUFLEN) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } for (i = 0; i < VIR_UUID_BUFLEN; i++) uuid[i] = raw[i] & 0xFF; return virNetworkLookupByUUID(conn, &uuid[0]); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return NULL; } /** @@ -3920,23 +4582,34 @@ virNetworkCreateXML(virConnectPtr conn, { DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (xmlDesc == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); - } - - if (conn->networkDriver && conn->networkDriver->networkCreateXML) - return conn->networkDriver->networkCreateXML (conn, xmlDesc); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkCreateXML) { + virNetworkPtr ret; + ret = conn->networkDriver->networkCreateXML (conn, xmlDesc); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -3954,23 +4627,34 @@ virNetworkDefineXML(virConnectPtr conn, { DEBUG("conn=%p, xml=%s", conn, xml); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (xml == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->networkDriver && conn->networkDriver->networkDefineXML) - return conn->networkDriver->networkDefineXML (conn, xml); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkDefineXML) { + virNetworkPtr ret; + ret = conn->networkDriver->networkDefineXML (conn, xml); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -3987,6 +4671,8 @@ virNetworkUndefine(virNetworkPtr network virConnectPtr conn; DEBUG("network=%p", network); + virResetLastError(); + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); @@ -3994,13 +4680,22 @@ virNetworkUndefine(virNetworkPtr network conn = network->conn; if (conn->flags & VIR_CONNECT_RO) { virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->networkUndefine) - return conn->networkDriver->networkUndefine (network); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkUndefine) { + int ret; + ret = conn->networkDriver->networkUndefine (network); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return -1; } @@ -4019,10 +4714,8 @@ virNetworkCreate(virNetworkPtr network) virConnectPtr conn; DEBUG("network=%p", network); - if (network == NULL) { - TODO - return (-1); - } + virResetLastError(); + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); @@ -4030,13 +4723,22 @@ virNetworkCreate(virNetworkPtr network) conn = network->conn; if (conn->flags & VIR_CONNECT_RO) { virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->networkCreate) - return conn->networkDriver->networkCreate (network); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkCreate) { + int ret; + ret = conn->networkDriver->networkCreate (network); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return -1; } @@ -4057,6 +4759,8 @@ virNetworkDestroy(virNetworkPtr network) virConnectPtr conn; DEBUG("network=%p", network); + virResetLastError(); + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); @@ -4065,13 +4769,22 @@ virNetworkDestroy(virNetworkPtr network) conn = network->conn; if (conn->flags & VIR_CONNECT_RO) { virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->networkDriver && conn->networkDriver->networkDestroy) - return conn->networkDriver->networkDestroy (network); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->networkDriver && conn->networkDriver->networkDestroy) { + int ret; + ret = conn->networkDriver->networkDestroy (network); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return -1; } @@ -4089,7 +4802,9 @@ virNetworkFree(virNetworkPtr network) { DEBUG("network=%p", network); - if (!VIR_IS_NETWORK(network)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); } @@ -4112,6 +4827,8 @@ virNetworkGetName(virNetworkPtr network) { DEBUG("network=%p", network); + virResetLastError(); + if (!VIR_IS_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (NULL); @@ -4133,18 +4850,25 @@ virNetworkGetUUID(virNetworkPtr network, { DEBUG("network=%p, uuid=%p", network, uuid); + virResetLastError(); + if (!VIR_IS_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); } if (uuid == NULL) { virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } memcpy(uuid, &network->uuid[0], VIR_UUID_BUFLEN); return (0); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); + return -1; } /** @@ -4163,13 +4887,15 @@ virNetworkGetUUIDString(virNetworkPtr ne unsigned char uuid[VIR_UUID_BUFLEN]; DEBUG("network=%p, buf=%p", network, buf); + virResetLastError(); + if (!VIR_IS_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); } if (buf == NULL) { virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } if (virNetworkGetUUID(network, &uuid[0])) @@ -4177,6 +4903,11 @@ virNetworkGetUUIDString(virNetworkPtr ne virUUIDFormat(uuid, buf); return (0); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); + return -1; } /** @@ -4196,21 +4927,32 @@ virNetworkGetXMLDesc(virNetworkPtr netwo virConnectPtr conn; DEBUG("network=%p, flags=%d", network, flags); - if (!VIR_IS_NETWORK(network)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (NULL); } if (flags != 0) { virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } conn = network->conn; - if (conn->networkDriver && conn->networkDriver->networkDumpXML) - return conn->networkDriver->networkDumpXML (network, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->networkDriver && conn->networkDriver->networkDumpXML) { + char *ret; + ret = conn->networkDriver->networkDumpXML (network, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return NULL; } @@ -4230,17 +4972,28 @@ virNetworkGetBridgeName(virNetworkPtr ne virConnectPtr conn; DEBUG("network=%p", network); - if (!VIR_IS_NETWORK(network)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (NULL); } conn = network->conn; - if (conn->networkDriver && conn->networkDriver->networkGetBridgeName) - return conn->networkDriver->networkGetBridgeName (network); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->networkDriver && conn->networkDriver->networkGetBridgeName) { + char *ret; + ret = conn->networkDriver->networkGetBridgeName (network); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return NULL; } @@ -4262,21 +5015,32 @@ virNetworkGetAutostart(virNetworkPtr net virConnectPtr conn; DEBUG("network=%p, autostart=%p", network, autostart); - if (!VIR_IS_NETWORK(network)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); } if (!autostart) { virLibNetworkError(network, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } conn = network->conn; - if (conn->networkDriver && conn->networkDriver->networkGetAutostart) - return conn->networkDriver->networkGetAutostart (network, autostart); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->networkDriver && conn->networkDriver->networkGetAutostart) { + int ret; + ret = conn->networkDriver->networkGetAutostart (network, autostart); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return -1; } @@ -4297,22 +5061,33 @@ virNetworkSetAutostart(virNetworkPtr net virConnectPtr conn; DEBUG("network=%p, autostart=%d", network, autostart); - if (!VIR_IS_NETWORK(network)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_NETWORK(network)) { virLibNetworkError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); } if (network->conn->flags & VIR_CONNECT_RO) { virLibNetworkError(network, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); + goto error; } conn = network->conn; - if (conn->networkDriver && conn->networkDriver->networkSetAutostart) - return conn->networkDriver->networkSetAutostart (network, autostart); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->networkDriver && conn->networkDriver->networkSetAutostart) { + int ret; + ret = conn->networkDriver->networkSetAutostart (network, autostart); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(network->conn); return -1; } @@ -4336,7 +5111,9 @@ virStoragePoolGetConnect (virStoragePool { DEBUG("pool=%p", pool); - if (!VIR_IS_STORAGE_POOL (pool)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_POOL (pool)) { virLibStoragePoolError (NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return NULL; } @@ -4356,15 +5133,26 @@ virConnectNumOfStoragePools (virConnectP { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->numOfPools) - return conn->storageDriver->numOfPools (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->numOfPools) { + int ret; + ret = conn->storageDriver->numOfPools (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -4387,6 +5175,8 @@ virConnectListStoragePools (virConnectPt { DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); @@ -4394,15 +5184,23 @@ virConnectListStoragePools (virConnectPt if ((names == NULL) || (maxnames < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->listPools) - return conn->storageDriver->listPools (conn, names, maxnames); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; - + goto error; + } + + if (conn->storageDriver && conn->storageDriver->listPools) { + int ret; + ret = conn->storageDriver->listPools (conn, names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return -1; } @@ -4419,15 +5217,26 @@ virConnectNumOfDefinedStoragePools(virCo { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->numOfDefinedPools) - return conn->storageDriver->numOfDefinedPools (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->numOfDefinedPools) { + int ret; + ret = conn->storageDriver->numOfDefinedPools (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -4451,6 +5260,8 @@ virConnectListDefinedStoragePools(virCon { DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); @@ -4458,13 +5269,22 @@ virConnectListDefinedStoragePools(virCon if ((names == NULL) || (maxnames < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->listDefinedPools) - return conn->storageDriver->listDefinedPools (conn, names, maxnames); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->listDefinedPools) { + int ret; + ret = conn->storageDriver->listDefinedPools (conn, names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -4496,24 +5316,37 @@ virConnectFindStoragePoolSources(virConn const char *srcSpec, unsigned int flags) { - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return NULL; + DEBUG("conn=%p, type=%s, src=%s, flags=%u", conn, type ? type : "", srcSpec ? srcSpec : "", flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + goto error; } if (type == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return NULL; - } - - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return NULL; - } - - if (conn->storageDriver && conn->storageDriver->findPoolSources) - return conn->storageDriver->findPoolSources(conn, type, srcSpec, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->findPoolSources) { + char *ret; + ret = conn->storageDriver->findPoolSources(conn, type, srcSpec, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -4533,19 +5366,30 @@ virStoragePoolLookupByName(virConnectPtr { DEBUG("conn=%p, name=%s", conn, name); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (name == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->storageDriver && conn->storageDriver->poolLookupByName) - return conn->storageDriver->poolLookupByName (conn, name); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolLookupByName) { + virStoragePoolPtr ret; + ret = conn->storageDriver->poolLookupByName (conn, name); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -4565,21 +5409,31 @@ virStoragePoolLookupByUUID(virConnectPtr { DEBUG("conn=%p, uuid=%s", conn, uuid); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (uuid == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->storageDriver && conn->storageDriver->poolLookupByUUID) - return conn->storageDriver->poolLookupByUUID (conn, uuid); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; - + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolLookupByUUID) { + virStoragePoolPtr ret; + ret = conn->storageDriver->poolLookupByUUID (conn, uuid); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return NULL; } @@ -4594,26 +5448,33 @@ virStoragePoolLookupByUUID(virConnectPtr */ virStoragePoolPtr virStoragePoolLookupByUUIDString(virConnectPtr conn, - const char *uuidstr) + const char *uuidstr) { unsigned char uuid[VIR_UUID_BUFLEN]; DEBUG("conn=%p, uuidstr=%s", conn, uuidstr); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (uuidstr == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } if (virUUIDParse(uuidstr, uuid) < 0) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } return virStoragePoolLookupByUUID(conn, uuid); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return NULL; } @@ -4630,17 +5491,27 @@ virStoragePoolLookupByVolume(virStorageV { DEBUG("vol=%p", vol); - if (!VIR_IS_STORAGE_VOL(vol)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - - if (vol->conn->storageDriver && vol->conn->storageDriver->poolLookupByVolume) - return vol->conn->storageDriver->poolLookupByVolume (vol); + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + + if (vol->conn->storageDriver && vol->conn->storageDriver->poolLookupByVolume) { + virStoragePoolPtr ret; + ret = vol->conn->storageDriver->poolLookupByVolume (vol); + if (!ret) + goto error; + return ret; + } virLibConnError (vol->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; - + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(vol->conn); + return NULL; } /** @@ -4662,23 +5533,34 @@ virStoragePoolCreateXML(virConnectPtr co { DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (xmlDesc == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); - } - - if (conn->storageDriver && conn->storageDriver->poolCreateXML) - return conn->storageDriver->poolCreateXML (conn, xmlDesc, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolCreateXML) { + virStoragePoolPtr ret; + ret = conn->storageDriver->poolCreateXML (conn, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -4700,25 +5582,35 @@ virStoragePoolDefineXML(virConnectPtr co { DEBUG("conn=%p, xml=%s", conn, xml); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) { - virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; } if (xml == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->storageDriver && conn->storageDriver->poolDefineXML) - return conn->storageDriver->poolDefineXML (conn, xml, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; - + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolDefineXML) { + virStoragePoolPtr ret; + ret = conn->storageDriver->poolDefineXML (conn, xml, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return NULL; } /** @@ -4737,6 +5629,8 @@ virStoragePoolBuild(virStoragePoolPtr po virConnectPtr conn; DEBUG("pool=%p, flags=%u", pool, flags); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); @@ -4744,15 +5638,23 @@ virStoragePoolBuild(virStoragePoolPtr po conn = pool->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->poolBuild) - return conn->storageDriver->poolBuild (pool, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; - + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolBuild) { + int ret; + ret = conn->storageDriver->poolBuild (pool, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return -1; } @@ -4770,6 +5672,8 @@ virStoragePoolUndefine(virStoragePoolPtr virConnectPtr conn; DEBUG("pool=%p", pool); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__); return (-1); @@ -4777,15 +5681,23 @@ virStoragePoolUndefine(virStoragePoolPtr conn = pool->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->poolUndefine) - return conn->storageDriver->poolUndefine (pool); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; - + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolUndefine) { + int ret; + ret = conn->storageDriver->poolUndefine (pool); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return -1; } @@ -4805,10 +5717,8 @@ virStoragePoolCreate(virStoragePoolPtr p virConnectPtr conn; DEBUG("pool=%p", pool); - if (pool == NULL) { - TODO; - return (-1); - } + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); @@ -4816,15 +5726,23 @@ virStoragePoolCreate(virStoragePoolPtr p conn = pool->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->poolCreate) - return conn->storageDriver->poolCreate (pool, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; - + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolCreate) { + int ret; + ret = conn->storageDriver->poolCreate (pool, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return -1; } @@ -4846,6 +5764,8 @@ virStoragePoolDestroy(virStoragePoolPtr virConnectPtr conn; DEBUG("pool=%p", pool); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); @@ -4854,13 +5774,22 @@ virStoragePoolDestroy(virStoragePoolPtr conn = pool->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->poolDestroy) - return conn->storageDriver->poolDestroy (pool); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolDestroy) { + int ret; + ret = conn->storageDriver->poolDestroy (pool); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -4882,6 +5811,8 @@ virStoragePoolDelete(virStoragePoolPtr p virConnectPtr conn; DEBUG("pool=%p, flags=%u", pool, flags); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); @@ -4890,13 +5821,22 @@ virStoragePoolDelete(virStoragePoolPtr p conn = pool->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->poolDelete) - return conn->storageDriver->poolDelete (pool, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolDelete) { + int ret; + ret = conn->storageDriver->poolDelete (pool, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -4915,7 +5855,9 @@ virStoragePoolFree(virStoragePoolPtr poo { DEBUG("pool=%p", pool); - if (!VIR_IS_STORAGE_POOL(pool)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); } @@ -4944,6 +5886,8 @@ virStoragePoolRefresh(virStoragePoolPtr virConnectPtr conn; DEBUG("pool=%p flags=%u", pool, flags); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); @@ -4952,13 +5896,22 @@ virStoragePoolRefresh(virStoragePoolPtr conn = pool->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->poolRefresh) - return conn->storageDriver->poolRefresh (pool, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->poolRefresh) { + int ret; + ret = conn->storageDriver->poolRefresh (pool, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -4976,12 +5929,13 @@ virStoragePoolGetName(virStoragePoolPtr { DEBUG("pool=%p", pool); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (NULL); } return (pool->name); - } @@ -5000,19 +5954,25 @@ virStoragePoolGetUUID(virStoragePoolPtr { DEBUG("pool=%p, uuid=%p", pool, uuid); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); } if (uuid == NULL) { virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } memcpy(uuid, &pool->uuid[0], VIR_UUID_BUFLEN); return (0); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return -1; } /** @@ -5031,21 +5991,27 @@ virStoragePoolGetUUIDString(virStoragePo unsigned char uuid[VIR_UUID_BUFLEN]; DEBUG("pool=%p, buf=%p", pool, buf); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); } if (buf == NULL) { virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } if (virStoragePoolGetUUID(pool, &uuid[0])) - return (-1); + goto error; virUUIDFormat(uuid, buf); return (0); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return -1; } @@ -5066,25 +6032,35 @@ virStoragePoolGetInfo(virStoragePoolPtr virConnectPtr conn; DEBUG("pool=%p, info=%p", pool, info); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); } if (info == NULL) { virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } memset(info, 0, sizeof(virStoragePoolInfo)); conn = pool->conn; - if (conn->storageDriver->poolGetInfo) - return conn->storageDriver->poolGetInfo (pool, info); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; - + if (conn->storageDriver->poolGetInfo) { + int ret; + ret = conn->storageDriver->poolGetInfo (pool, info); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return -1; } @@ -5106,23 +6082,33 @@ virStoragePoolGetXMLDesc(virStoragePoolP virConnectPtr conn; DEBUG("pool=%p, flags=%u", pool, flags); - if (!VIR_IS_STORAGE_POOL(pool)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (NULL); } if (flags != 0) { virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - conn = pool->conn; - - if (conn->storageDriver && conn->storageDriver->poolGetXMLDesc) - return conn->storageDriver->poolGetXMLDesc (pool, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; - + goto error; + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolGetXMLDesc) { + char *ret; + ret = conn->storageDriver->poolGetXMLDesc (pool, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return NULL; } @@ -5143,21 +6129,32 @@ virStoragePoolGetAutostart(virStoragePoo virConnectPtr conn; DEBUG("pool=%p, autostart=%p", pool, autostart); - if (!VIR_IS_STORAGE_POOL(pool)) { + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); } if (!autostart) { virLibStoragePoolError(pool, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - conn = pool->conn; - - if (conn->storageDriver && conn->storageDriver->poolGetAutostart) - return conn->storageDriver->poolGetAutostart (pool, autostart); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolGetAutostart) { + int ret; + ret = conn->storageDriver->poolGetAutostart (pool, autostart); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -5178,22 +6175,33 @@ virStoragePoolSetAutostart(virStoragePoo virConnectPtr conn; DEBUG("pool=%p, autostart=%d", pool, autostart); - if (!VIR_IS_STORAGE_POOL(pool)) { - virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); - return (-1); + virResetLastError(); + + if (!VIR_IS_CONNECTED_STORAGE_POOL(pool)) { + virLibStoragePoolError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + goto error; } if (pool->conn->flags & VIR_CONNECT_RO) { virLibStoragePoolError(pool, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - conn = pool->conn; - - if (conn->storageDriver && conn->storageDriver->poolSetAutostart) - return conn->storageDriver->poolSetAutostart (pool, autostart); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + conn = pool->conn; + + if (conn->storageDriver && conn->storageDriver->poolSetAutostart) { + int ret; + ret = conn->storageDriver->poolSetAutostart (pool, autostart); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -5211,15 +6219,26 @@ virStoragePoolNumOfVolumes(virStoragePoo { DEBUG("pool=%p", pool); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); } - if (pool->conn->storageDriver && pool->conn->storageDriver->poolNumOfVolumes) - return pool->conn->storageDriver->poolNumOfVolumes (pool); + if (pool->conn->storageDriver && pool->conn->storageDriver->poolNumOfVolumes) { + int ret; + ret = pool->conn->storageDriver->poolNumOfVolumes (pool); + if (ret < 0) + goto error; + return ret; + } virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -5242,6 +6261,8 @@ virStoragePoolListVolumes(virStoragePool { DEBUG("pool=%p, names=%p, maxnames=%d", pool, names, maxnames); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); return (-1); @@ -5249,13 +6270,22 @@ virStoragePoolListVolumes(virStoragePool if ((names == NULL) || (maxnames < 0)) { virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (pool->conn->storageDriver && pool->conn->storageDriver->poolListVolumes) - return pool->conn->storageDriver->poolListVolumes (pool, names, maxnames); + goto error; + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->poolListVolumes) { + int ret; + ret = pool->conn->storageDriver->poolListVolumes (pool, names, maxnames); + if (ret < 0) + goto error; + return ret; + } virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return -1; } @@ -5279,6 +6309,8 @@ virStorageVolGetConnect (virStorageVolPt { DEBUG("vol=%p", vol); + virResetLastError(); + if (!VIR_IS_STORAGE_VOL (vol)) { virLibStoragePoolError (NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return NULL; @@ -5303,19 +6335,30 @@ virStorageVolLookupByName(virStoragePool { DEBUG("pool=%p, name=%s", pool, name); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (name == NULL) { virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (pool->conn->storageDriver && pool->conn->storageDriver->volLookupByName) - return pool->conn->storageDriver->volLookupByName (pool, name); + goto error; + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->volLookupByName) { + virStorageVolPtr ret; + ret = pool->conn->storageDriver->volLookupByName (pool, name); + if (!ret) + goto error; + return ret; + } virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return NULL; } @@ -5337,19 +6380,30 @@ virStorageVolLookupByKey(virConnectPtr c { DEBUG("conn=%p, key=%s", conn, key); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (key == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->storageDriver && conn->storageDriver->volLookupByKey) - return conn->storageDriver->volLookupByKey (conn, key); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->volLookupByKey) { + virStorageVolPtr ret; + ret = conn->storageDriver->volLookupByKey (conn, key); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -5369,19 +6423,30 @@ virStorageVolLookupByPath(virConnectPtr { DEBUG("conn=%p, path=%s", conn, path); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (NULL); } if (path == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - if (conn->storageDriver && conn->storageDriver->volLookupByPath) - return conn->storageDriver->volLookupByPath (conn, path); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->volLookupByPath) { + virStorageVolPtr ret; + ret = conn->storageDriver->volLookupByPath (conn, path); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -5400,6 +6465,8 @@ virStorageVolGetName(virStorageVolPtr vo { DEBUG("vol=%p", vol); + virResetLastError(); + if (!VIR_IS_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (NULL); @@ -5423,6 +6490,8 @@ virStorageVolGetKey(virStorageVolPtr vol { DEBUG("vol=%p", vol); + virResetLastError(); + if (!VIR_IS_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (NULL); @@ -5450,6 +6519,8 @@ virStorageVolCreateXML(virStoragePoolPtr { DEBUG("pool=%p, flags=%u", pool, flags); + virResetLastError(); + if (!VIR_IS_STORAGE_POOL(pool)) { virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (NULL); @@ -5457,13 +6528,22 @@ virStorageVolCreateXML(virStoragePoolPtr if (pool->conn->flags & VIR_CONNECT_RO) { virLibConnError(pool->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (NULL); - } - - if (pool->conn->storageDriver && pool->conn->storageDriver->volCreateXML) - return pool->conn->storageDriver->volCreateXML (pool, xmldesc, flags); + goto error; + } + + if (pool->conn->storageDriver && pool->conn->storageDriver->volCreateXML) { + virStorageVolPtr ret; + ret = pool->conn->storageDriver->volCreateXML (pool, xmldesc, flags); + if (!ret) + goto error; + return ret; + } virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); return NULL; } @@ -5484,6 +6564,8 @@ virStorageVolDelete(virStorageVolPtr vol virConnectPtr conn; DEBUG("vol=%p, flags=%u", vol, flags); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (-1); @@ -5492,13 +6574,22 @@ virStorageVolDelete(virStorageVolPtr vol conn = vol->conn; if (conn->flags & VIR_CONNECT_RO) { virLibStorageVolError(vol, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - return (-1); - } - - if (conn->storageDriver && conn->storageDriver->volDelete) - return conn->storageDriver->volDelete (vol, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->volDelete) { + int ret; + ret = conn->storageDriver->volDelete (vol, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(vol->conn); return -1; } @@ -5517,6 +6608,8 @@ virStorageVolFree(virStorageVolPtr vol) { DEBUG("vol=%p", vol); + virResetLastError(); + if (!VIR_IS_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (-1); @@ -5544,23 +6637,34 @@ virStorageVolGetInfo(virStorageVolPtr vo virConnectPtr conn; DEBUG("vol=%p, info=%p", vol, info); + virResetLastError(); + if (!VIR_IS_CONNECTED_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (-1); } if (info == NULL) { virLibStorageVolError(vol, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + goto error; } memset(info, 0, sizeof(virStorageVolInfo)); conn = vol->conn; - if (conn->storageDriver->volGetInfo) - return conn->storageDriver->volGetInfo (vol, info); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->storageDriver->volGetInfo){ + int ret; + ret = conn->storageDriver->volGetInfo (vol, info); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(vol->conn); return -1; } @@ -5582,23 +6686,33 @@ virStorageVolGetXMLDesc(virStorageVolPtr virConnectPtr conn; DEBUG("vol=%p, flags=%u", vol, flags); + virResetLastError(); + if (!VIR_IS_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (NULL); } if (flags != 0) { virLibStorageVolError(vol, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); + goto error; } conn = vol->conn; - if (conn->storageDriver && conn->storageDriver->volGetXMLDesc) - return conn->storageDriver->volGetXMLDesc (vol, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; - + if (conn->storageDriver && conn->storageDriver->volGetXMLDesc) { + char *ret; + ret = conn->storageDriver->volGetXMLDesc (vol, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(vol->conn); + return NULL; } @@ -5620,6 +6734,8 @@ virStorageVolGetPath(virStorageVolPtr vo virConnectPtr conn; DEBUG("vol=%p", vol); + virResetLastError(); + if (!VIR_IS_STORAGE_VOL(vol)) { virLibStorageVolError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); return (NULL); @@ -5627,10 +6743,19 @@ virStorageVolGetPath(virStorageVolPtr vo conn = vol->conn; - if (conn->storageDriver && conn->storageDriver->volGetPath) - return conn->storageDriver->volGetPath (vol); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (conn->storageDriver && conn->storageDriver->volGetPath) { + char *ret; + ret = conn->storageDriver->volGetPath (vol); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(vol->conn); return NULL; } @@ -5655,19 +6780,30 @@ virNodeNumOfDevices(virConnectPtr conn, { DEBUG("conn=%p, cap=%s, flags=%d", conn, cap, flags); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); } if (flags != 0) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->deviceMonitor && conn->deviceMonitor->numOfDevices) - return conn->deviceMonitor->numOfDevices (conn, cap, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->deviceMonitor && conn->deviceMonitor->numOfDevices) { + int ret; + ret = conn->deviceMonitor->numOfDevices (conn, cap, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -5696,19 +6832,30 @@ virNodeListDevices(virConnectPtr conn, DEBUG("conn=%p, cap=%s, names=%p, maxnames=%d, flags=%d", conn, cap, names, maxnames, flags); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); } if ((flags != 0) || (names == NULL) || (maxnames < 0)) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if (conn->deviceMonitor && conn->deviceMonitor->listDevices) - return conn->deviceMonitor->listDevices (conn, cap, names, maxnames, flags); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->deviceMonitor && conn->deviceMonitor->listDevices) { + int ret; + ret = conn->deviceMonitor->listDevices (conn, cap, names, maxnames, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -5726,6 +6873,8 @@ virNodeDevicePtr virNodeDeviceLookupByNa { DEBUG("conn=%p, name=%p", conn, name); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return NULL; @@ -5733,13 +6882,22 @@ virNodeDevicePtr virNodeDeviceLookupByNa if (name == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return NULL; - } - - if (conn->deviceMonitor && conn->deviceMonitor->deviceLookupByName) - return conn->deviceMonitor->deviceLookupByName (conn, name); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (conn->deviceMonitor && conn->deviceMonitor->deviceLookupByName) { + virNodeDevicePtr ret; + ret = conn->deviceMonitor->deviceLookupByName (conn, name); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } @@ -5758,15 +6916,26 @@ char *virNodeDeviceGetXMLDesc(virNodeDev { DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL); + virResetLastError(); + if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) { virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__); return NULL; } - if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceDumpXML) - return dev->conn->deviceMonitor->deviceDumpXML (dev, flags); + if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceDumpXML) { + char *ret; + ret = dev->conn->deviceMonitor->deviceDumpXML (dev, flags); + if (!ret) + goto error; + return ret; + } virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dev->conn); return NULL; } @@ -5804,6 +6973,8 @@ const char *virNodeDeviceGetParent(virNo { DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL); + virResetLastError(); + if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) { virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__); return NULL; @@ -5814,6 +6985,7 @@ const char *virNodeDeviceGetParent(virNo dev->parent = dev->conn->deviceMonitor->deviceGetParent (dev); } else { virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virSetConnError(dev->conn); return NULL; } } @@ -5832,15 +7004,26 @@ int virNodeDeviceNumOfCaps(virNodeDevice { DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL); + virResetLastError(); + if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) { virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__); return -1; } - if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceNumOfCaps) - return dev->conn->deviceMonitor->deviceNumOfCaps (dev); + if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceNumOfCaps) { + int ret; + ret = dev->conn->deviceMonitor->deviceNumOfCaps (dev); + if (ret < 0) + goto error; + return ret; + } virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dev->conn); return -1; } @@ -5861,15 +7044,26 @@ int virNodeDeviceListCaps(virNodeDeviceP DEBUG("dev=%p, conn=%p, names=%p, maxnames=%d", dev, dev ? dev->conn : NULL, names, maxnames); + virResetLastError(); + if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) { virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__); return -1; } - if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceListCaps) - return dev->conn->deviceMonitor->deviceListCaps (dev, names, maxnames); + if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceListCaps) { + int ret; + ret = dev->conn->deviceMonitor->deviceListCaps (dev, names, maxnames); + if (ret < 0) + goto error; + return ret; + } virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(dev->conn); return -1; } @@ -5887,6 +7081,8 @@ int virNodeDeviceFree(virNodeDevicePtr d { DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL); + virResetLastError(); + if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) { virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__); return (-1); @@ -5919,6 +7115,8 @@ virConnectDomainEventRegister(virConnect void *opaque, virFreeCallback freecb) { + DEBUG("conn=%p, cb=%p, opaque=%p, freecb=%p", conn, cb, opaque, freecb); + virResetLastError(); if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -5926,11 +7124,21 @@ virConnectDomainEventRegister(virConnect } if (cb == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - if ((conn->driver) && (conn->driver->domainEventRegister)) - return conn->driver->domainEventRegister (conn, cb, opaque, freecb); + goto error; + } + + if ((conn->driver) && (conn->driver->domainEventRegister)) { + int ret; + ret = conn->driver->domainEventRegister (conn, cb, opaque, freecb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return -1; } @@ -5949,6 +7157,9 @@ int virConnectDomainEventDeregister(virConnectPtr conn, virConnectDomainEventCallback cb) { + DEBUG("conn=%p, cb=%p", conn, cb); + + virResetLastError(); if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -5956,12 +7167,21 @@ virConnectDomainEventDeregister(virConne } if (cb == NULL) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - if ((conn->driver) && (conn->driver->domainEventDeregister)) - return conn->driver->domainEventDeregister (conn, cb); - - return -1; -} - - + goto error; + } + if ((conn->driver) && (conn->driver->domainEventDeregister)) { + int ret; + ret = conn->driver->domainEventDeregister (conn, cb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return -1; +} + + diff --git a/src/virterror.c b/src/virterror.c --- a/src/virterror.c +++ b/src/virterror.c @@ -18,11 +18,11 @@ #include "virterror_internal.h" #include "datatypes.h" #include "logging.h" +#include "memory.h" +#include "threads.h" -virError virLastErr = /* the last error */ - { .code = 0, .domain = 0, .message = NULL, .level = VIR_ERR_NONE, - .conn = NULL, .dom = NULL, .str1 = NULL, .str2 = NULL, .str3 = NULL, - .int1 = 0, .int2 = 0, .net = NULL }; +virThreadLocal virLastErr; + virErrorFunc virErrorHandler = NULL; /* global error handler */ void *virUserData = NULL; /* associated data */ @@ -154,28 +154,118 @@ static const char *virErrorDomainName(vi return(dom); } + /* + * Internal helper that is called when a thread exits, to + * release the error object stored in the thread local + */ +static void +virLastErrFreeData(void *data) +{ + virErrorPtr err = data; + if (!err) + return; + virResetError(err); + VIR_FREE(err); +} + + +int +virErrorInitialize(void) +{ + return virThreadLocalInit(&virLastErr, virLastErrFreeData); +} + + +/* + * Internal helper to ensure a generic error code is stored + * in case where API returns failure, but forgot to set an + * error + */ +static void +virErrorGenericFailure(virErrorPtr err) +{ + err->code = VIR_ERR_INTERNAL_ERROR; + err->domain = VIR_FROM_NONE; + err->level = VIR_ERR_ERROR; + err->message = strdup(_("Unknown failure")); +} + + +/* + * Internal helper to perform a deep copy of the an error + */ +static int +virCopyError(virErrorPtr from, + virErrorPtr to) +{ + int ret = 0; + if (!to) + return 0; + virResetError(to); + if (!from) + return 0; + to->code = from->code; + to->domain = from->domain; + to->level = from->level; + if (from->message && !(to->message = strdup(from->message))) + ret = -1; + if (from->str1 && !(to->str1 = strdup(from->str1))) + ret = -1; + if (from->str2 && !(to->str2 = strdup(from->str2))) + ret = -1; + if (from->str3 && !(to->str3 = strdup(from->str3))) + ret = -1; + to->int1 = from->int1; + to->int2 = from->int2; + /* + * Delibrately not setting 'conn', 'dom', 'net' references + */ + return ret; +} + +static virErrorPtr +virLastErrorObject(void) +{ + virErrorPtr err; + err = virThreadLocalGet(&virLastErr); + if (!err) { + if (VIR_ALLOC(err) < 0) + return NULL; + virThreadLocalSet(&virLastErr, err); + } + return err; +} + + +/** * virGetLastError: * * Provide a pointer to the last error caught at the library level - * Simpler but may not be suitable for multithreaded accesses, in which - * case use virCopyLastError() + * + * The error object is kept in thread local storage, so separate + * threads can safely access this concurrently. * * Returns a pointer to the last error or NULL if none occurred. */ virErrorPtr virGetLastError(void) { - if (virLastErr.code == VIR_ERR_OK) - return (NULL); - return (&virLastErr); + virErrorPtr err = virLastErrorObject(); + if (!err || err->code == VIR_ERR_OK) + return NULL; + return err; } -/* +/** * virCopyLastError: * @to: target to receive the copy * * Copy the content of the last error caught at the library level + * + * The error object is kept in thread local storage, so separate + * threads can safely access this concurrently. + * * One will need to free the result with virResetError() * * Returns 0 if no error was found and the error code otherwise and -1 in case @@ -184,12 +274,14 @@ virGetLastError(void) int virCopyLastError(virErrorPtr to) { - if (to == NULL) - return (-1); - if (virLastErr.code == VIR_ERR_OK) - return (0); - memcpy(to, &virLastErr, sizeof(virError)); - return (virLastErr.code); + virErrorPtr err = virLastErrorObject(); + /* We can't guarentee caller has initialized it to zero */ + memset(to, 0, sizeof(*to)); + if (err) + virCopyError(err, to); + else + virResetError(to); + return to->code; } /** @@ -210,15 +302,22 @@ virResetError(virErrorPtr err) memset(err, 0, sizeof(virError)); } + /** * virResetLastError: * * Reset the last error caught at the library level. + * + * The error object is kept in thread local storage, so separate + * threads can safely access this concurrently, only resetting + * their own error object. */ void virResetLastError(void) { - virResetError(&virLastErr); + virErrorPtr err = virLastErrorObject(); + if (err) + virResetError(err); } /** @@ -226,8 +325,20 @@ virResetLastError(void) * @conn: pointer to the hypervisor connection * * Provide a pointer to the last error caught on that connection - * Simpler but may not be suitable for multithreaded accesses, in which - * case use virConnCopyLastError() + * + * This method is not protected against access from multiple + * threads. In a multi-threaded application, always use the + * global virGetLastError() API which is backed by thread + * local storage. + * + * If the connection object was discovered to be invalid by + * an API call, then the error will be reported against the + * global error object. + * + * Since 0.6.0, all errors reported in the per-connection object + * are also duplicated in the global error object. As such an + * application can always use virGetLastError(). This method + * remains for backwards compatability. * * Returns a pointer to the last error or NULL if none occurred. */ @@ -235,8 +346,8 @@ virErrorPtr virConnGetLastError(virConnectPtr conn) { if (conn == NULL) - return (NULL); - return (&conn->err); + return NULL; + return &conn->err; } /** @@ -245,6 +356,21 @@ virConnGetLastError(virConnectPtr conn) * @to: target to receive the copy * * Copy the content of the last error caught on that connection + * + * This method is not protected against access from multiple + * threads. In a multi-threaded application, always use the + * global virGetLastError() API which is backed by thread + * local storage. + * + * If the connection object was discovered to be invalid by + * an API call, then the error will be reported against the + * global error object. + * + * Since 0.6.0, all errors reported in the per-connection object + * are also duplicated in the global error object. As such an + * application can always use virGetLastError(). This method + * remains for backwards compatability. + * * One will need to free the result with virResetError() * * Returns 0 if no error was found and the error code otherwise and -1 in case @@ -253,20 +379,27 @@ virConnGetLastError(virConnectPtr conn) int virConnCopyLastError(virConnectPtr conn, virErrorPtr to) { + /* We can't guarentee caller has initialized it to zero */ + memset(to, 0, sizeof(*to)); + if (conn == NULL) - return (-1); - if (to == NULL) - return (-1); + return -1; + virMutexLock(&conn->lock); if (conn->err.code == VIR_ERR_OK) - return (0); - memcpy(to, &conn->err, sizeof(virError)); - return (conn->err.code); + virResetError(to); + else + virCopyError(&conn->err, to); + virMutexUnlock(&conn->lock); + return to->code; } /** * virConnResetLastError: * @conn: pointer to the hypervisor connection * + * The error object is kept in thread local storage, so separate + * threads can safely access this concurrently. + * * Reset the last error caught on that connection */ void @@ -274,7 +407,9 @@ virConnResetLastError(virConnectPtr conn { if (conn == NULL) return; + virMutexLock(&conn->lock); virResetError(&conn->err); + virMutexUnlock(&conn->lock); } /** @@ -309,8 +444,10 @@ virConnSetErrorFunc(virConnectPtr conn, { if (conn == NULL) return; + virMutexLock(&conn->lock); conn->handler = handler; conn->userData = userData; + virMutexUnlock(&conn->lock); } /** @@ -357,6 +494,44 @@ virDefaultErrorFunc(virErrorPtr err) dom, lvl, domain, network, err->message); } +/* + * Internal helper to ensure the global error object + * is initialized with a generic message if not already + * set. + */ +void +virSetGlobalError(void) +{ + virErrorPtr err = virLastErrorObject(); + + if (err && err->code == VIR_ERR_OK) + virErrorGenericFailure(err); +} + +/* + * Internal helper to ensure the connection error object + * is initialized from the global object. + */ +void +virSetConnError(virConnectPtr conn) +{ + virErrorPtr err = virLastErrorObject(); + + if (err && err->code == VIR_ERR_OK) + virErrorGenericFailure(err); + + if (conn) { + virMutexLock(&conn->lock); + if (err) + virCopyError(err, &conn->err); + else + virErrorGenericFailure(&conn->err); + virMutexUnlock(&conn->lock); + } +} + + + /** * virRaiseError: * @conn: the connection to the hypervisor if available @@ -377,16 +552,29 @@ virDefaultErrorFunc(virErrorPtr err) * immediately if a callback is found and store it for later handling. */ void -virRaiseError(virConnectPtr conn, virDomainPtr dom, virNetworkPtr net, +virRaiseError(virConnectPtr conn, + virDomainPtr dom ATTRIBUTE_UNUSED, + virNetworkPtr net ATTRIBUTE_UNUSED, int domain, int code, virErrorLevel level, const char *str1, const char *str2, const char *str3, int int1, int int2, const char *msg, ...) { - virErrorPtr to = &virLastErr; + virErrorPtr to; void *userData = virUserData; virErrorFunc handler = virErrorHandler; char *str; + /* + * All errors are recorded in thread local storage + * For compatability, public API calls will copy them + * to the per-connection error object when neccessary + */ + to = virLastErrorObject(); + if (!to) + return; /* Hit OOM allocating thread error object, sod all we can do now */ + + virResetError(to); + if (code == VIR_ERR_OK) return; @@ -394,11 +582,12 @@ virRaiseError(virConnectPtr conn, virDom * try to find the best place to save and report the error */ if (conn != NULL) { - to = &conn->err; + virMutexLock(&conn->lock); if (conn->handler != NULL) { handler = conn->handler; userData = conn->userData; } + virMutexUnlock(&conn->lock); } /* @@ -421,9 +610,10 @@ virRaiseError(virConnectPtr conn, virDom * Save the information about the error */ virResetError(to); - to->conn = conn; - to->dom = dom; - to->net = net; + /* + * Delibrately not setting conn, dom & net fields since + * they're utterly unsafe + */ to->domain = domain; to->code = code; to->message = str; @@ -813,3 +1003,5 @@ void virReportErrorHelper(virConnectPtr virerr, errorMessage, NULL, -1, -1, virerr, errorMessage); } + + diff --git a/src/virterror_internal.h b/src/virterror_internal.h --- a/src/virterror_internal.h +++ b/src/virterror_internal.h @@ -24,7 +24,6 @@ #include "internal.h" -extern virError virLastErr; extern virErrorFunc virErrorHandler; extern void *virUserData; @@ -33,6 +32,7 @@ extern void *virUserData; * API for error handling * * * ************************************************************************/ +int virErrorInitialize(void); void virRaiseError(virConnectPtr conn, virDomainPtr dom, virNetworkPtr net, @@ -53,4 +53,7 @@ void virReportErrorHelper(virConnectPtr ATTRIBUTE_FORMAT(printf, 7, 8); +void virSetGlobalError(void); +void virSetConnError(virConnectPtr conn); + #endif diff --git a/tests/cpuset b/tests/cpuset --- a/tests/cpuset +++ b/tests/cpuset @@ -40,6 +40,6 @@ libvir: Domain Config error : failed Xen error: Failed to define domain from xml-invalid EOF -compare out exp || fail=1 +compare exp out || fail=1 (exit $fail); exit $fail diff --git a/tests/read-bufsiz b/tests/read-bufsiz --- a/tests/read-bufsiz +++ b/tests/read-bufsiz @@ -37,7 +37,7 @@ for i in before after; do virsh --connect test:///default define $in > out || fail=1 printf "Domain test defined from $in\n\n" > exp || fail=1 - compare out exp || fail=1 + compare exp out || fail=1 done (exit $fail); exit $fail diff --git a/tests/start b/tests/start --- a/tests/start +++ b/tests/start @@ -34,9 +34,9 @@ virsh -c $test_url start test > out 2> e # stdout gets a newline echo > exp || fail=1 -compare out exp || fail=1 +compare exp out || fail=1 echo 'error: Domain is already active' > exp || fail=1 -compare err exp || fail=1 +compare exp err || fail=1 (exit $fail); exit $fail diff --git a/tests/undefine b/tests/undefine --- a/tests/undefine +++ b/tests/undefine @@ -9,7 +9,7 @@ # (at your option) any later version. # This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of +# but WITHEXP ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. @@ -32,7 +32,7 @@ cat <<\EOF > exp || fail=1 libvir: Test error : internal error Domain 'test' is still running error: Failed to undefine domain test EOF -compare out exp || fail=1 +compare exp out || fail=1 # A different diagnostic when specifying a domain ID virsh -q -c test:///default undefine 1 > out 2>&1 @@ -41,7 +41,7 @@ cat <<\EOF > exp || fail=1 error: a running domain like 1 cannot be undefined; to undefine, first shutdown then undefine using its name or UUID EOF -compare out exp || fail=1 +compare exp out || fail=1 # Succeed, now: first shut down, then undefine, both via name. virsh -q -c test:///default 'shutdown test; undefine test' > out 2>&1 @@ -50,6 +50,6 @@ cat <<\EOF > exp || fail=1 Domain test is being shutdown Domain test has been undefined EOF -compare out exp || fail=1 +compare exp out || fail=1 (exit $fail); exit $fail diff --git a/tests/vcpupin b/tests/vcpupin --- a/tests/vcpupin +++ b/tests/vcpupin @@ -32,7 +32,7 @@ cat <<\EOF > exp || fail=1 error: vcpupin: Invalid or missing vCPU number. EOF -compare out exp || fail=1 +compare exp out || fail=1 # An out-of-range vCPU number deserves a diagnostic, too. virsh --connect test:///default vcpupin test 100 0,1 > out 2>&1 @@ -41,6 +41,6 @@ cat <<\EOF > exp || fail=1 error: vcpupin: Invalid vCPU number. EOF -compare out exp || fail=1 +compare exp out || fail=1 (exit $fail); exit $fail -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:42:15PM +0000, Daniel P. Berrange wrote:
The virGetLastError() and virConnGetLastError() methods are not even remotely thread safe, and the virCopyLastError/virConnCopyLastError methods don't help in this goal, being open to a race condition.
This patch changes the internal impl of the global error object to store its virError instance in a thread local variable. All errors are now reported against the global error object. In the public API entry points, we explicitly reset the global error object to ensure no stale errors are hanging around. In all error paths we also set a generic error, if the internal driver forget to set an explicit error. Finally we also copy the global error to the per connection error object for back-compatability, though the global object remains non-threadsafe for application access.
src/datatypes.c | 31 src/datatypes.h | 15 src/libvirt.c | 3276 ++++++++++++++++++++++++++++++++--------------- src/virterror.c | 258 +++ src/virterror_internal.h | 5 tests/cpuset | 2 tests/read-bufsiz | 2 tests/start | 4 tests/undefine | 8 tests/vcpupin | 4 10 files changed, 2507 insertions(+), 1098 deletions(-)
Yup, looks good, and also fixes lots of potential bugs where the connection object might have been corrupted. +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The virGetLastError() and virConnGetLastError() methods are not
Dan, I suppose you're using mercurial to produce these diffs. If so, you may want to file a bug, since the diffs are misleadingly suboptimal. Here's one example: - - DEBUG("conn=%p", conn); + DEBUG("conn=%p", conn); + + virResetLastError(); That's not so bad: since they're short and right next to each other, it's obvious they're identical. However, I saw the following one first, and searched visually for a while before using the editor to confirm that the added/removed virLibConnError (conn, VIR_ERR_NO_SUPPORT... lines are identical: @@ -1217,15 +1235,25 @@ virConnectGetHostname (virConnectPtr con { DEBUG("conn=%p", conn); - if (!VIR_IS_CONNECT(conn)) { - virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return NULL; - } - - if (conn->driver->getHostname) - return conn->driver->getHostname (conn); - - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return NULL; + } + + if (conn->driver->getHostname) { + char *ret = conn->driver->getHostname (conn); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; } ---------------------------------- It's hard enough to read some of these changes without such duplication. For reference, here are the git-generated diffs: @@ -1217,15 +1235,25 @@ virConnectGetHostname (virConnectPtr conn) { DEBUG("conn=%p", conn); + virResetLastError(); + if (!VIR_IS_CONNECT(conn)) { virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); return NULL; } - if (conn->driver->getHostname) - return conn->driver->getHostname (conn); + if (conn->driver->getHostname) { + char *ret = conn->driver->getHostname (conn); + if (!ret) + goto error; + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); return NULL; }

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The virGetLastError() and virConnGetLastError() methods are not even remotely thread safe, and the virCopyLastError/virConnCopyLastError methods don't help in this goal, being open to a race condition.
This patch changes the internal impl of the global error object to store its virError instance in a thread local variable. All errors are now reported against the global error object. In the public API entry points, we explicitly reset the global error object to ensure no stale errors are hanging around. In all error paths we also set a generic error, if the internal driver forget to set an explicit error. Finally we also copy the global error to the per connection error object for back-compatability, though the global object remains non-threadsafe for application access.
ACK. I looked through all of it, though confess I paid less attention to detail as the scope of the repetition began to set in. The code of many of those function bodies should be automatically generated. Unfortunately, it's just a little too complicated to do with cpp... What I mean, precisely is that there are 50-60 blocks that fit this general mold: + if (conn->driver->domainSetMaxMemory) { + int ret; + ret = conn->driver->domainSetMaxMemory (domain, memory); + if (ret < 0) + goto error; + return ret; + } Where all that varies is the method name (domainSetMaxMemory, here), its argument list, and the type of "ret", which matches the type of the containing function. Oh, and of course the condition changes with the type, and sometimes with the semantics of the method. ----------------------------------------- However, I did notice one tiny problem: There are many uses of TODO, like this: - if (domain == NULL) { - TODO - return (-1); - } + virResetLastError(); Obviously not a compile-time problem, since it's defined to nothing, but with no following semicolon, it would cause automatic indenting tools to do the wrong thing. Four uses remain after your patch series.

On Fri, Jan 16, 2009 at 10:01:01PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The virGetLastError() and virConnGetLastError() methods are not even remotely thread safe, and the virCopyLastError/virConnCopyLastError methods don't help in this goal, being open to a race condition.
This patch changes the internal impl of the global error object to store its virError instance in a thread local variable. All errors are now reported against the global error object. In the public API entry points, we explicitly reset the global error object to ensure no stale errors are hanging around. In all error paths we also set a generic error, if the internal driver forget to set an explicit error. Finally we also copy the global error to the per connection error object for back-compatability, though the global object remains non-threadsafe for application access.
ACK.
This is applied now.
----------------------------------------- However, I did notice one tiny problem: There are many uses of TODO, like this:
- if (domain == NULL) { - TODO - return (-1); - } + virResetLastError();
Obviously not a compile-time problem, since it's defined to nothing, but with no following semicolon, it would cause automatic indenting tools to do the wrong thing.
We should probably just remove the remaining TODO blocks - they're not really doing much useful at this point. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

The strerror() method is not guarenteed to be re-entrant, which is rather a pain because the strerror_r() method is rather unpleasant to use. In addition our code is quite inconsistent about using VIR_ERR_SYSTEM_ERROR vs VIR_ERR_INTERNAL_ERROR for problems which have an 'errno' avilable. Likewise we're not very consistent about OOM reporting error codes This patch thus introduces two convenient functions for reporting a system error and OOM error. virReportSystemError(conn, theerrno, fmt,...) virReportOOMError(conn) The OOM error funtion doesn't take any args because 50% of the time we don't currently give any message, and where we do its just the name of the variable, which is pretty useless for a user. The useful stuff would be filename/line number/method name, which are automatically included in all error calls now. As a short example.. Before: virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, _("cannot remove config %s: %s"), configFile, strerror(errno)); After: virReportSystemError(conn, errno, _("cannot remove config %s"), configFile); NB, we actually still use strerror() on MinGW because it lacks strerror_r(), and by code examination I've verified its strerror() is threadsafe, just returning static strings. .x-sc_avoid_write | 1 autobuild.sh | 1 configure.in | 3 po/POTFILES.in | 1 src/domain_conf.c | 41 ++++++----- src/libvirt_private.syms | 2 src/lxc_container.c | 142 ++++++++++++++++++++++------------------ src/lxc_controller.c | 97 ++++++++++++++------------- src/lxc_driver.c | 73 +++++++++------------ src/network_conf.c | 46 ++++++------- src/network_driver.c | 125 ++++++++++++++++++----------------- src/nodeinfo.c | 16 ++-- src/qemu_driver.c | 79 ++++++++++------------ src/remote_internal.c | 73 +++++++++------------ src/storage_backend.c | 81 +++++++++++------------ src/storage_backend_disk.c | 23 +++--- src/storage_backend_fs.c | 129 ++++++++++++++++++------------------- src/storage_backend_iscsi.c | 34 +++++---- src/storage_backend_logical.c | 57 ++++++++-------- src/storage_conf.c | 26 ++++--- src/storage_driver.c | 34 ++++----- src/test.c | 146 +++++++++++++++++++++--------------------- src/uml_driver.c | 111 +++++++++++++++---------------- src/util.c | 94 ++++++++++++--------------- src/virterror.c | 73 ++++++++++++++++++++- src/virterror_internal.h | 28 +++++++- src/xen_inotify.c | 9 +- src/xen_internal.c | 13 ++- src/xen_unified.c | 5 + src/xend_internal.c | 21 +++--- src/xm_internal.c | 59 +++++++++------- 31 files changed, 881 insertions(+), 762 deletions(-) Daniel diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -1,4 +1,5 @@ ^src/util\.c$ ^src/xend_internal\.c$ ^src/util-lib\.c$ +^qemud/qemud.c$ ^gnulib/ diff --git a/autobuild.sh b/autobuild.sh --- a/autobuild.sh +++ b/autobuild.sh @@ -65,6 +65,7 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; --build=$(uname -m)-pc-linux \ --host=i686-pc-mingw32 \ --prefix="$AUTOBUILD_INSTALL_ROOT/i686-pc-mingw32/sys-root/mingw" \ + --enable-compile-warnings=error \ --without-sasl \ --without-avahi \ --without-polkit \ diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -74,6 +74,9 @@ AC_SYS_LARGEFILE dnl Availability of various common functions (non-fatal if missing). AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity getuid getgid]) +dnl Availability of various not common threadsafe functions +AC_CHECK_FUNCS([strerror_r]) + dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h sys/poll.h syslog.h]) diff --git a/po/POTFILES.in b/po/POTFILES.in --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -38,6 +38,7 @@ src/virsh.c src/virterror.c src/xen_inotify.c src/xen_internal.c +src/xen_unified.c src/xend_internal.c src/xm_internal.c src/xml.c diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -40,6 +40,8 @@ #include "buf.h" #include "c-ctype.h" +#define VIR_FROM_THIS VIR_FROM_DOMAIN + VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "qemu", "kqemu", @@ -1904,8 +1906,7 @@ static virDomainDefPtr virDomainDefParse int err; if ((err = virUUIDGenerate(def->uuid))) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to generate UUID: %s"), - strerror(err)); + "%s", _("Failed to generate UUID")); goto error; } } else { @@ -3394,33 +3395,33 @@ int virDomainSaveXML(virConnectPtr conn, goto cleanup; if ((err = virFileMakePath(configDir))) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - configDir, strerror(err)); + virReportSystemError(conn, errno, + _("cannot create config directory '%s'"), + configDir); goto cleanup; } if ((fd = open(configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config file %s: %s"), - configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create config file '%s'"), + configFile); goto cleanup; } towrite = strlen(xml); if (safewrite(fd, xml, towrite) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot write config file %s: %s"), - configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot write config file '%s'"), + configFile); goto cleanup; } if (close(fd) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot save config file %s: %s"), - configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot save config file '%s'"), + configFile); goto cleanup; } @@ -3519,9 +3520,9 @@ int virDomainLoadAllConfigs(virConnectPt if (!(dir = opendir(configDir))) { if (errno == ENOENT) return 0; - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to open dir '%s': %s"), - configDir, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to open dir '%s'"), + configDir); return -1; } @@ -3573,9 +3574,9 @@ int virDomainDeleteConfig(virConnectPtr if (unlink(configFile) < 0 && errno != ENOENT) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot remove config for %s: %s"), - dom->def->name, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot remove config %s"), + configFile); goto cleanup; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -319,6 +319,8 @@ virUUIDParse; virReportErrorHelper; virErrorMsg; virRaiseError; +virReportSystemErrorFull; +virReportOOMErrorFull; # xml.h diff --git a/src/lxc_container.c b/src/lxc_container.c --- a/src/lxc_container.c +++ b/src/lxc_container.c @@ -48,6 +48,8 @@ #include "memory.h" #include "veth.h" +#define VIR_FROM_THIS VIR_FROM_LXC + /* * GLibc headers are behind the kernel, so we define these * constants if they're not present already. @@ -118,14 +120,14 @@ static int lxcContainerSetStdio(int cont int open_max, i; if (setsid() < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("setsid failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("setsid failed")); goto cleanup; } if (ioctl(ttyfd, TIOCSCTTY, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("ioctl(TIOCSTTY) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("ioctl(TIOCSTTY) failed")); goto cleanup; } @@ -137,20 +139,20 @@ static int lxcContainerSetStdio(int cont close(i); if (dup2(ttyfd, 0) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("dup2(stdin) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("dup2(stdin) failed")); goto cleanup; } if (dup2(ttyfd, 1) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("dup2(stdout) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("dup2(stdout) failed")); goto cleanup; } if (dup2(ttyfd, 2) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("dup2(stderr) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("dup2(stderr) failed")); goto cleanup; } @@ -177,9 +179,8 @@ int lxcContainerSendContinue(int control writeCount = safewrite(control, &msg, sizeof(msg)); if (writeCount != sizeof(msg)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("unable to send container continue message: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("unable to send container continue message")); goto error_out; } @@ -207,9 +208,8 @@ static int lxcContainerWaitForContinue(i readLen = saferead(control, &msg, sizeof(msg)); if (readLen != sizeof(msg) || msg != LXC_CONTINUE_MSG) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to read the container continue message: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Failed to read the container continue message")); return -1; } close(control); @@ -266,27 +266,28 @@ static int lxcContainerChildMountSort(co static int lxcContainerPivotRoot(virDomainFSDefPtr root) { + int rc; char *oldroot; /* First step is to ensure the new root itself is a mount point */ if (mount(root->src, root->src, NULL, MS_BIND, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to bind new root %s: %s"), - root->src, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to bind new root %s"), + root->src); return -1; } if (virAsprintf(&oldroot, "%s/.oldroot", root->src) < 0) { - lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(NULL); return -1; } - if (virFileMakePath(oldroot) < 0) { + if ((rc = virFileMakePath(oldroot)) < 0) { VIR_FREE(oldroot); - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create %s: %s"), - oldroot, strerror(errno)); + virReportSystemError(NULL, rc, + _("failed to create %s"), + oldroot); return -1; } @@ -294,9 +295,9 @@ static int lxcContainerPivotRoot(virDoma * this and will soon be unmounted completely */ if (pivot_root(root->src, oldroot) < 0) { VIR_FREE(oldroot); - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to pivot root %s to %s: %s"), - oldroot, root->src, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to pivot root %s to %s"), + oldroot, root->src); return -1; } VIR_FREE(oldroot); @@ -312,6 +313,7 @@ static int lxcContainerPivotRoot(virDoma static int lxcContainerPopulateDevices(void) { int i; + int rc; const struct { int maj; int min; @@ -326,11 +328,14 @@ static int lxcContainerPopulateDevices(v { LXC_DEV_MAJ_MEMORY, LXC_DEV_MIN_URANDOM, 0666, "/dev/urandom" }, }; - if (virFileMakePath("/dev") < 0 || - mount("none", "/dev", "tmpfs", 0, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount /dev tmpfs for container: %s"), - strerror(errno)); + if ((rc = virFileMakePath("/dev")) < 0) { + virReportSystemError(NULL, rc, "%s", + _("cannot create /dev/")); + return -1; + } + if (mount("none", "/dev", "tmpfs", 0, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("failed to mount /dev tmpfs")); return -1; } /* Move old devpts into container, since we have to @@ -339,12 +344,15 @@ static int lxcContainerPopulateDevices(v XXX This sucks, we need to figure out how to get our own private devpts for isolation */ - if (virFileMakePath("/dev/pts") < 0 || - mount("/.oldroot/dev/pts", "/dev/pts", NULL, + if ((rc = virFileMakePath("/dev/pts") < 0)) { + virReportSystemError(NULL, rc, "%s", + _("cannot create /dev/pts")); + return -1; + } + if (mount("/.oldroot/dev/pts", "/dev/pts", NULL, MS_MOVE, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to move /dev/pts into container: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to move /dev/pts into container")); return -1; } @@ -353,9 +361,9 @@ static int lxcContainerPopulateDevices(v dev_t dev = makedev(devs[i].maj, devs[i].min); if (mknod(devs[i].path, 0, dev) < 0 || chmod(devs[i].path, devs[i].mode)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to make device %s: %s"), - devs[i].path, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to make device %s"), + devs[i].path); return -1; } } @@ -382,12 +390,19 @@ static int lxcContainerMountNewFS(virDom return -1; } - if (virFileMakePath(vmDef->fss[i]->dst) < 0 || - mount(src, vmDef->fss[i]->dst, NULL, MS_BIND, NULL) < 0) { + if (virFileMakePath(vmDef->fss[i]->dst) < 0) { + virReportSystemError(NULL, errno, + _("failed to create %s"), + vmDef->fss[i]->dst); VIR_FREE(src); - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount %s at %s for container: %s"), - vmDef->fss[i]->src, vmDef->fss[i]->dst, strerror(errno)); + return -1; + } + if (mount(src, vmDef->fss[i]->dst, NULL, MS_BIND, NULL) < 0) { + VIR_FREE(src); + virReportSystemError(NULL, errno, + _("failed to mount %s at %s"), + vmDef->fss[i]->src, + vmDef->fss[i]->dst); return -1; } VIR_FREE(src); @@ -406,9 +421,8 @@ static int lxcContainerUnmountOldFS(void int i; if (!(procmnt = setmntent("/proc/mounts", "r"))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to read /proc/mounts: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to read /proc/mounts")); return -1; } while ((mntent = getmntent(procmnt)) != NULL) { @@ -433,9 +447,9 @@ static int lxcContainerUnmountOldFS(void for (i = 0 ; i < nmounts ; i++) { if (umount(mounts[i]) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to unmount %s: %s"), - mounts[i], strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to unmount '%s'"), + mounts[i]); return -1; } VIR_FREE(mounts[i]); @@ -458,9 +472,8 @@ static int lxcContainerSetupPivotRoot(vi if (virFileMakePath("/proc") < 0 || mount("none", "/proc", "proc", 0, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount /proc for container: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to mount /proc")); return -1; } @@ -492,18 +505,18 @@ static int lxcContainerSetupExtraMounts( NULL, MS_BIND, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount %s at %s for container: %s"), - vmDef->fss[i]->src, vmDef->fss[i]->dst, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to mount %s at %s"), + vmDef->fss[i]->src, + vmDef->fss[i]->dst); return -1; } } /* mount /proc */ if (mount("lxcproc", "/proc", "proc", 0, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount /proc for container: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to mount /proc")); return -1; } @@ -557,8 +570,9 @@ static int lxcContainerChild( void *data ttyfd = open(argv->ttyPath, O_RDWR|O_NOCTTY); if (ttyfd < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("open(%s) failed: %s"), argv->ttyPath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to open %s"), + argv->ttyPath); return -1; } @@ -618,8 +632,8 @@ int lxcContainerStart(virDomainDefPtr de DEBUG("clone() returned, %d", pid); if (pid < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("clone() failed, %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to run clone container")); return -1; } diff --git a/src/lxc_controller.c b/src/lxc_controller.c --- a/src/lxc_controller.c +++ b/src/lxc_controller.c @@ -45,6 +45,8 @@ #include "util.h" #include "cgroup.h" +#define VIR_FROM_THIS VIR_FROM_LXC + struct cgroup_device_policy { char type; int major; @@ -109,8 +111,8 @@ static int lxcSetContainerResources(virD rc = virCgroupAddTask(cgroup, getpid()); out: if (rc != 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to set lxc resources: %s\n"), strerror(-rc)); + virReportSystemError(NULL, -rc, "%s", + _("Failed to set lxc resources")); virCgroupRemove(cgroup); } @@ -135,9 +137,9 @@ static int lxcMonitorServer(const char * struct sockaddr_un addr; if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create server socket %s: %s"), - sockpath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to create server socket '%s'"), + sockpath); goto error; } @@ -147,15 +149,15 @@ static int lxcMonitorServer(const char * strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to bind server socket %s: %s"), - sockpath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to bind server socket '%s'"), + sockpath); goto error; } if (listen(fd, 30 /* backlog */ ) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to listen server socket %s: %s"), - sockpath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to listen server socket %s"), + sockpath); goto error; } @@ -187,14 +189,16 @@ static int lxcFdForward(int readFd, int goto cleanup; } - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("read of fd %d failed: %s"), readFd, strerror(errno)); + virReportSystemError(NULL, errno, + _("read of fd %d failed"), + readFd); goto cleanup; } if (1 != (safewrite(writeFd, buf, 1))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("write to fd %d failed: %s"), writeFd, strerror(errno)); + virReportSystemError(NULL, errno, + _("write to fd %d failed"), + writeFd); goto cleanup; } @@ -244,8 +248,8 @@ static int lxcControllerMain(int monitor /* create the epoll fild descriptor */ epollFd = epoll_create(2); if (0 > epollFd) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_create(2) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_create(2) failed")); goto cleanup; } @@ -254,30 +258,30 @@ static int lxcControllerMain(int monitor epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */ epollEvent.data.fd = appPty; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, appPty, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(appPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(appPty) failed")); goto cleanup; } epollEvent.data.fd = contPty; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, contPty, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } epollEvent.events = EPOLLIN; epollEvent.data.fd = monitor; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, monitor, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } epollEvent.events = EPOLLHUP; epollEvent.data.fd = client; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } @@ -296,14 +300,14 @@ static int lxcControllerMain(int monitor epollEvent.events = EPOLLHUP; epollEvent.data.fd = client; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } } else if (client != -1 && epollEvent.data.fd == client) { if (0 > epoll_ctl(epollFd, EPOLL_CTL_DEL, client, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } close(client); @@ -340,8 +344,8 @@ static int lxcControllerMain(int monitor } /* error */ - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_wait() failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_wait() failed")); goto cleanup; } @@ -438,16 +442,16 @@ lxcControllerRun(virDomainDefPtr def, pid_t container = -1; if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("sockpair failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("sockpair failed")); goto cleanup; } if (virFileOpenTty(&containerPty, &containerPtyPath, 0) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to allocate tty: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to allocate tty")); goto cleanup; } @@ -528,18 +532,18 @@ int main(int argc, char *argv[]) case 'n': if ((name = strdup(optarg)) == NULL) { - fprintf(stderr, "%s", strerror(errno)); + virReportOOMError(NULL); goto cleanup; } break; case 'v': if (VIR_REALLOC_N(veths, nveths+1) < 0) { - fprintf(stderr, "cannot allocate veths %s", strerror(errno)); + virReportOOMError(NULL); goto cleanup; } if ((veths[nveths++] = strdup(optarg)) == NULL) { - fprintf(stderr, "cannot allocate veth name %s", strerror(errno)); + virReportOOMError(NULL); goto cleanup; } break; @@ -614,8 +618,9 @@ int main(int argc, char *argv[]) if (pid > 0) { if ((rc = virFileWritePid(LXC_STATE_DIR, name, pid)) != 0) { - fprintf(stderr, _("Unable to write pid file: %s\n"), - strerror(rc)); + virReportSystemError(NULL, errno, + _("Unable to write pid file '%s/%s.pid'"), + LXC_STATE_DIR, name); _exit(1); } @@ -627,22 +632,22 @@ int main(int argc, char *argv[]) /* Don't hold onto any cwd we inherit from libvirtd either */ if (chdir("/") < 0) { - fprintf(stderr, _("Unable to change to root dir: %s\n"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Unable to change to root dir")); goto cleanup; } if (setsid() < 0) { - fprintf(stderr, _("Unable to become session leader: %s\n"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Unable to become session leader")); goto cleanup; } } /* Accept initial client which is the libvirtd daemon */ if ((client = accept(monitor, NULL, 0)) < 0) { - fprintf(stderr, _("Failed connection from LXC driver: %s\n"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Failed connection from LXC driver")); goto cleanup; } diff --git a/src/lxc_driver.c b/src/lxc_driver.c --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -49,6 +49,8 @@ #include "cgroup.h" +#define VIR_FROM_THIS VIR_FROM_LXC + static int lxcStartup(void); static int lxcShutdown(void); static lxc_driver_t *lxc_driver = NULL; @@ -467,9 +469,9 @@ static int lxcVMCleanup(virConnectPtr co ; /* empty */ if ((waitRc != vm->pid) && (errno != ECHILD)) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("waitpid failed to wait for container %d: %d %s"), - vm->pid, waitRc, strerror(errno)); + virReportSystemError(conn, errno, + _("waitpid failed to wait for container %d: %d"), + vm->pid, waitRc); } rc = 0; @@ -579,17 +581,15 @@ static int lxcSetupInterfaces(virConnect } if (0 != (rc = brAddInterface(brctl, bridge, parentVeth))) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add %s device to %s: %s"), - parentVeth, - bridge, - strerror(rc)); + virReportSystemError(conn, rc, + _("failed to add %s device to %s"), + parentVeth, bridge); goto error_exit; } if (0 != (rc = vethInterfaceUpOrDown(parentVeth, 1))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to enable parent ns veth device: %d"), rc); + virReportSystemError(conn, rc, "%s", + _("failed to enable parent ns veth device")); goto error_exit; } @@ -618,9 +618,8 @@ static int lxcMonitorClient(virConnectPt } if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create client socket: %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to create client socket")); goto error; } @@ -629,9 +628,8 @@ static int lxcMonitorClient(virConnectPt strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to connect to client socket: %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to connect to client socket")); goto error; } @@ -662,9 +660,9 @@ static int lxcVmTerminate(virConnectPtr if (kill(vm->pid, signum) < 0) { if (errno != ESRCH) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to kill pid %d: %s"), - vm->pid, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to kill pid %d"), + vm->pid); return -1; } } @@ -794,9 +792,9 @@ static int lxcControllerStart(virConnect */ while ((rc = waitpid(child, &status, 0) == -1) && errno == EINTR); if (rc == -1) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot wait for '%s': %s"), - largv[0], strerror(errno)); + virReportSystemError(conn, errno, + _("cannot wait for '%s'"), + largv[0]); goto cleanup; } @@ -848,10 +846,10 @@ static int lxcVmStart(virConnectPtr conn unsigned int nveths = 0; char **veths = NULL; - if (virFileMakePath(driver->logDir) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create log directory %s: %s"), - driver->logDir, strerror(rc)); + if ((rc = virFileMakePath(driver->logDir)) < 0) { + virReportSystemError(conn, rc, + _("cannot create log directory '%s'"), + driver->logDir); return -1; } @@ -863,9 +861,8 @@ static int lxcVmStart(virConnectPtr conn /* open parent tty */ if (virFileOpenTty(&parentTty, &parentTtyPath, 1) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to allocate tty: %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to allocate tty")); goto cleanup; } if (vm->def->console && @@ -887,9 +884,9 @@ static int lxcVmStart(virConnectPtr conn if ((logfd = open(logfile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR|S_IWUSR)) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to open %s: %s"), logfile, - strerror(errno)); + virReportSystemError(conn, errno, + _("failed to open '%s'"), + logfile); goto cleanup; } @@ -907,9 +904,9 @@ static int lxcVmStart(virConnectPtr conn /* And get its pid */ if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) != 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to read pid file %s/%s.pid: %s"), - driver->stateDir, vm->def->name, strerror(rc)); + virReportSystemError(conn, rc, + _("Failed to read pid file %s/%s.pid"), + driver->stateDir, vm->def->name); rc = -1; goto cleanup; } @@ -1272,11 +1269,7 @@ static int lxcVersion(virConnectPtr conn int min; int rev; - if (uname(&ver) != 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("uname(): %s"), strerror(errno)); - return -1; - } + uname(&ver); if (sscanf(ver.release, "%i.%i.%i", &maj, &min, &rev) != 3) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, diff --git a/src/network_conf.c b/src/network_conf.c --- a/src/network_conf.c +++ b/src/network_conf.c @@ -43,6 +43,8 @@ #include "buf.h" #include "c-ctype.h" +#define VIR_FROM_THIS VIR_FROM_NETWORK + VIR_ENUM_DECL(virNetworkForward) VIR_ENUM_IMPL(virNetworkForward, @@ -332,7 +334,7 @@ virNetworkDefParseXML(virConnectPtr conn int err; if ((err = virUUIDGenerate(def->uuid))) { virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to generate UUID: %s"), strerror(err)); + "%s", _("Failed to generate UUID")); goto error; } } else { @@ -667,40 +669,40 @@ int virNetworkSaveConfig(virConnectPtr c goto cleanup; if ((err = virFileMakePath(configDir))) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - configDir, strerror(err)); + virReportSystemError(conn, err, + _("cannot create config directory '%s'"), + configDir); goto cleanup; } if ((err = virFileMakePath(autostartDir))) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - autostartDir, strerror(err)); + virReportSystemError(conn, err, + _("cannot create autostart directory '%s'"), + autostartDir); goto cleanup; } if ((fd = open(net->configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config file %s: %s"), - net->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create config file '%s'"), + net->configFile); goto cleanup; } towrite = strlen(xml); if (safewrite(fd, xml, towrite) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot write config file %s: %s"), - net->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot write config file '%s'"), + net->configFile); goto cleanup; } if (close(fd) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot save config file %s: %s"), - net->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot save config file '%s'"), + net->configFile); goto cleanup; } @@ -777,9 +779,9 @@ int virNetworkLoadAllConfigs(virConnectP if (!(dir = opendir(configDir))) { if (errno == ENOENT) return 0; - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to open dir '%s': %s"), - configDir, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to open dir '%s'"), + configDir); return -1; } @@ -821,9 +823,9 @@ int virNetworkDeleteConfig(virConnectPtr unlink(net->autostartLink); if (unlink(net->configFile) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot remove config for %s: %s"), - net->def->name, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot remove config file '%s'"), + net->configFile); return -1; } diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -57,6 +57,9 @@ #include "iptables.h" #include "bridge.h" + +#define VIR_FROM_THIS VIR_FROM_NETWORK + /* Main driver state */ struct network_driver { virMutex lock; @@ -460,9 +463,9 @@ networkAddMasqueradingIptablesRules(virC network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow forwarding from '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow forwarding from '%s'"), + network->def->bridge); goto masqerr1; } @@ -471,9 +474,9 @@ networkAddMasqueradingIptablesRules(virC network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow forwarding to '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow forwarding to '%s'"), + network->def->bridge); goto masqerr2; } @@ -481,9 +484,9 @@ networkAddMasqueradingIptablesRules(virC if ((err = iptablesAddForwardMasquerade(driver->iptables, network->def->network, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to enable masquerading : %s\n"), - strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to enable masquerading to '%s'\n"), + network->def->forwardDev ? network->def->forwardDev : NULL); goto masqerr3; } @@ -513,9 +516,9 @@ networkAddRoutingIptablesRules(virConnec network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow routing from '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow routing from '%s'"), + network->def->bridge); goto routeerr1; } @@ -524,9 +527,9 @@ networkAddRoutingIptablesRules(virConnec network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow routing to '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow routing to '%s'"), + network->def->bridge); goto routeerr2; } @@ -557,31 +560,31 @@ networkAddIptablesRules(virConnectPtr co /* allow DHCP requests through to dnsmasq */ if ((err = iptablesAddTcpInput(driver->iptables, network->def->bridge, 67))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DHCP requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DHCP requests from '%s'"), + network->def->bridge); goto err1; } if ((err = iptablesAddUdpInput(driver->iptables, network->def->bridge, 67))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DHCP requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DHCP requests from '%s'"), + network->def->bridge); goto err2; } /* allow DNS requests through to dnsmasq */ if ((err = iptablesAddTcpInput(driver->iptables, network->def->bridge, 53))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DNS requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DNS requests from '%s'"), + network->def->bridge); goto err3; } if ((err = iptablesAddUdpInput(driver->iptables, network->def->bridge, 53))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DNS requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DNS requests from '%s'"), + network->def->bridge); goto err4; } @@ -589,24 +592,24 @@ networkAddIptablesRules(virConnectPtr co /* Catch all rules to block forwarding to/from bridges */ if ((err = iptablesAddForwardRejectOut(driver->iptables, network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to block outbound traffic from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to block outbound traffic from '%s'"), + network->def->bridge); goto err5; } if ((err = iptablesAddForwardRejectIn(driver->iptables, network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to block inbound traffic to '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to block inbound traffic to '%s'"), + network->def->bridge); goto err6; } /* Allow traffic between guests on the same bridge */ if ((err = iptablesAddForwardAllowCross(driver->iptables, network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow cross bridge traffic on '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow cross bridge traffic on '%s'"), + network->def->bridge); goto err7; } @@ -711,15 +714,15 @@ static int networkStartNetworkDaemon(vir } if (!driver->brctl && (err = brInit(&driver->brctl))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot initialize bridge support: %s"), strerror(err)); + virReportSystemError(conn, err, "%s", + _("cannot initialize bridge support")); return -1; } if ((err = brAddBridge(driver->brctl, &network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create bridge '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("cannot create bridge '%s'"), + network->def->bridge); return -1; } @@ -732,25 +735,25 @@ static int networkStartNetworkDaemon(vir if (network->def->ipAddress && (err = brSetInetAddress(driver->brctl, network->def->bridge, network->def->ipAddress))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot set IP address on bridge '%s' to '%s' : %s"), - network->def->bridge, network->def->ipAddress, strerror(err)); + virReportSystemError(conn, err, + _("cannot set IP address on bridge '%s' to '%s'"), + network->def->bridge, network->def->ipAddress); goto err_delbr; } if (network->def->netmask && (err = brSetInetNetmask(driver->brctl, network->def->bridge, network->def->netmask))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot set netmask on bridge '%s' to '%s' : %s"), - network->def->bridge, network->def->netmask, strerror(err)); + virReportSystemError(conn, err, + _("cannot set netmask on bridge '%s' to '%s'"), + network->def->bridge, network->def->netmask); goto err_delbr; } if (network->def->ipAddress && (err = brSetInterfaceUp(driver->brctl, network->def->bridge, 1))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to bring the bridge '%s' up : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to bring the bridge '%s' up"), + network->def->bridge); goto err_delbr; } @@ -759,8 +762,8 @@ static int networkStartNetworkDaemon(vir if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE && !networkEnableIpForwarding()) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to enable IP forwarding : %s"), strerror(err)); + virReportSystemError(conn, errno, "%s", + _("failed to enable IP forwarding")); goto err_delbr2; } @@ -1249,23 +1252,23 @@ static int networkSetAutostart(virNetwor int err; if ((err = virFileMakePath(driver->networkAutostartDir))) { - networkReportError(net->conn, NULL, net, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->networkAutostartDir, strerror(err)); + virReportSystemError(net->conn, errno, + _("cannot create autostart directory '%s'"), + driver->networkAutostartDir); goto cleanup; } if (symlink(network->configFile, network->autostartLink) < 0) { - networkReportError(net->conn, NULL, net, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s' to '%s': %s"), - network->autostartLink, network->configFile, strerror(errno)); + virReportSystemError(net->conn, errno, + _("Failed to create symlink '%s' to '%s'"), + network->autostartLink, network->configFile); goto cleanup; } } else { if (unlink(network->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - networkReportError(net->conn, NULL, net, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - network->autostartLink, strerror(errno)); + virReportSystemError(net->conn, errno, + _("Failed to delete symlink '%s'"), + network->autostartLink); goto cleanup; } } diff --git a/src/nodeinfo.c b/src/nodeinfo.c --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -45,6 +45,9 @@ #include "util.h" #include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + #ifdef __linux__ #define CPUINFO_PATH "/proc/cpuinfo" @@ -135,12 +138,8 @@ int virNodeInfoPopulate(virConnectPtr co #ifdef HAVE_UNAME struct utsname info; - if (uname(&info) < 0) { - virRaiseError(conn, NULL, NULL, 0, VIR_ERR_INTERNAL_ERROR, - VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - "cannot extract machine type %s", strerror(errno)); - return -1; - } + uname(&info); + strncpy(nodeinfo->model, info.machine, sizeof(nodeinfo->model)-1); nodeinfo->model[sizeof(nodeinfo->model)-1] = '\0'; @@ -155,9 +154,8 @@ int virNodeInfoPopulate(virConnectPtr co int ret; FILE *cpuinfo = fopen(CPUINFO_PATH, "r"); if (!cpuinfo) { - virRaiseError(conn, NULL, NULL, 0, VIR_ERR_INTERNAL_ERROR, - VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - "cannot open %s %s", CPUINFO_PATH, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open %s"), CPUINFO_PATH); return -1; } ret = linuxNodeInfoCPUPopulate(conn, cpuinfo, nodeinfo); diff --git a/src/qemu_driver.c b/src/qemu_driver.c --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -69,6 +69,8 @@ #include "uuid.h" #include "domain_conf.h" +#define VIR_FROM_THIS VIR_FROM_QEMU + /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" @@ -153,9 +155,7 @@ qemudLogFD(virConnectPtr conn, const cha if ((ret = snprintf(logfile, sizeof(logfile), "%s/%s.log", logDir, name)) < 0 || ret >= sizeof(logfile)) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to build logfile name %s/%s.log"), - logDir, name); + virReportOOMError(conn); return -1; } @@ -165,15 +165,14 @@ qemudLogFD(virConnectPtr conn, const cha else logmode |= O_APPEND; if ((fd = open(logfile, logmode, S_IRUSR | S_IWUSR)) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create logfile %s: %s"), - logfile, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to create logfile %s"), + logfile); return -1; } if (qemudSetCloseExec(fd) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Unable to set VM logfile close-on-exec flag %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("Unable to set VM logfile close-on-exec flag")); close(fd); return -1; } @@ -527,9 +526,9 @@ qemudReadMonitorOutput(virConnectPtr con continue; if (errno != EAGAIN) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failure while reading %s startup output: %s"), - what, strerror(errno)); + virReportSystemError(conn, errno, + _("Failure while reading %s startup output"), + what); return -1; } @@ -540,9 +539,9 @@ qemudReadMonitorOutput(virConnectPtr con return -1; } else if (ret == -1) { if (errno != EINTR) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failure while reading %s startup output: %s"), - what, strerror(errno)); + virReportSystemError(conn, errno, + _("Failure while reading %s startup output"), + what); return -1; } } else { @@ -867,9 +866,8 @@ qemudInitCpus(virConnectPtr conn, for (i = 0 ; i < vm->nvcpupids ; i++) { if (sched_setaffinity(vm->vcpupids[i], sizeof(mask), &mask) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to set CPU affinity %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to set CPU affinity")); return -1; } } @@ -963,9 +961,9 @@ static int qemudStartVMDaemon(virConnect } if (virFileMakePath(driver->logDir) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create log directory %s: %s"), - driver->logDir, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create log directory %s"), + driver->logDir); return -1; } @@ -983,10 +981,9 @@ static int qemudStartVMDaemon(virConnect * in a sub-process so its hard to feed back a useful error */ if (stat(emulator, &sb) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Cannot find QEMU binary %s: %s"), - emulator, - strerror(errno)); + virReportSystemError(conn, errno, + _("Cannot find QEMU binary %s"), + emulator); return -1; } @@ -2239,9 +2236,9 @@ static int qemudDomainSave(virDomainPtr } if (close(fd) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, - _("unable to save file %s %s"), - path, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("unable to save file %s"), + path); goto cleanup; } fd = -1; @@ -2402,8 +2399,8 @@ qemudDomainPinVcpu(virDomainPtr dom, if (vm->vcpupids != NULL) { if (sched_setaffinity(vm->vcpupids[vcpu], sizeof(mask), &mask) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, - _("cannot set affinity: %s"), strerror(errno)); + virReportSystemError(dom->conn, errno, "%s", + _("cannot set affinity")); goto cleanup; } } else { @@ -2471,8 +2468,8 @@ qemudDomainGetVcpus(virDomainPtr dom, CPU_ZERO(&mask); if (sched_getaffinity(vm->vcpupids[v], sizeof(mask), &mask) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, - _("cannot get affinity: %s"), strerror(errno)); + virReportSystemError(dom->conn, errno, "%s", + _("cannot get affinity")); goto cleanup; } @@ -3455,23 +3452,23 @@ static int qemudDomainSetAutostart(virDo int err; if ((err = virFileMakePath(driver->autostartDir))) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(dom->conn, errno, + _("cannot create autostart directory %s"), + driver->autostartDir); goto cleanup; } if (symlink(configFile, autostartLink) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s to '%s': %s"), - autostartLink, configFile, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to create symlink '%s to '%s'"), + autostartLink, configFile); goto cleanup; } } else { if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - autostartLink, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to delete symlink '%s'"), + autostartLink); goto cleanup; } } diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -88,6 +88,8 @@ #include "util.h" #include "event.h" +#define VIR_FROM_THIS VIR_FROM_REMOTE + #ifdef WIN32 #define pipe(fds) _pipe(fds,4096, _O_BINARY) #endif @@ -584,9 +586,9 @@ doRemoteOpen (virConnectPtr conn, } freeaddrinfo (res); - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to connect to '%s': %s"), - priv->hostname, strerror (saved_errno)); + virReportSystemError(conn, saved_errno, + _("unable to connect to '%s'"), + priv->hostname); goto failed; tcp_connected: @@ -605,9 +607,9 @@ doRemoteOpen (virConnectPtr conn, uid_t uid = getuid(); if (!(pw = getpwuid(uid))) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to lookup user '%d': %s"), - uid, strerror (errno)); + virReportSystemError(conn, errno, + _("unable to lookup user '%d'"), + uid); goto failed; } @@ -639,9 +641,8 @@ doRemoteOpen (virConnectPtr conn, autostart_retry: priv->sock = socket (AF_UNIX, SOCK_STREAM, 0); if (priv->sock == -1) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to create socket %s"), - strerror (errno)); + virReportSystemError(conn, errno, "%s", + _("unable to create socket")); goto failed; } if (connect (priv->sock, (struct sockaddr *) &addr, sizeof addr) == -1) { @@ -663,9 +664,9 @@ doRemoteOpen (virConnectPtr conn, goto autostart_retry; } } - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to connect to '%s': %s"), - sockname, strerror (errno)); + virReportSystemError(conn, errno, + _("unable to connect to '%s'"), + sockname); goto failed; } @@ -723,9 +724,8 @@ doRemoteOpen (virConnectPtr conn, * to faff around with two file descriptors (a la 'pipe(2)'). */ if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to create socket pair %s"), - strerror (errno)); + virReportSystemError(conn, errno, "%s", + _("unable to create socket pair")); goto failed; } @@ -752,16 +752,14 @@ doRemoteOpen (virConnectPtr conn, } /* switch (transport) */ if (virSetNonBlock(priv->sock) < 0) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to make socket non-blocking %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("unable to make socket non-blocking")); goto failed; } if (pipe(wakeup) < 0) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to make pipe %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("unable to make pipe")); goto failed; } priv->wakeupRead = wakeup[0]; @@ -1002,9 +1000,9 @@ check_cert_file (virConnectPtr conn, con { struct stat sb; if (stat(file, &sb) < 0) { - errorf(conn, VIR_ERR_RPC, - _("Cannot access %s '%s': %s (%d)"), - type, file, strerror(errno), errno); + virReportSystemError(conn, errno, + _("Cannot access %s '%s'"), + type, file); return -1; } return 0; @@ -1191,9 +1189,8 @@ verify_certificate (virConnectPtr conn A } if ((now = time(NULL)) == ((time_t)-1)) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("cannot get current time: %s"), - strerror (errno)); + virReportSystemError(conn, errno, "%s", + _("cannot get current time")); return -1; } @@ -5183,10 +5180,8 @@ remoteAuthSASL (virConnectPtr conn, stru /* Get local address in form IPADDR:PORT */ salen = sizeof(sa); if (getsockname(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { - virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("failed to get sock address %d (%s)"), - errno, strerror(errno)); + virReportSystemError(in_open ? NULL : conn, errno, "%s", + _("failed to get sock address")); goto cleanup; } if ((localAddr = addrToString(&sa, salen)) == NULL) @@ -5195,10 +5190,8 @@ remoteAuthSASL (virConnectPtr conn, stru /* Get remote address in form IPADDR:PORT */ salen = sizeof(sa); if (getpeername(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { - virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("failed to get peer address %d (%s)"), - errno, strerror(errno)); + virReportSystemError(in_open ? NULL : conn, errno, "%s", + _("failed to get peer address")); goto cleanup; } if ((remoteAddr = addrToString(&sa, salen)) == NULL) @@ -5723,8 +5716,8 @@ processCallWrite(virConnectPtr conn, if (errno == EWOULDBLOCK) return 0; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); + virReportSystemError(in_open ? NULL : conn, errno, + "%s", _("cannot send data")); return -1; } @@ -5773,10 +5766,8 @@ processCallRead(virConnectPtr conn, if (errno == EWOULDBLOCK) return 0; - errorf (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, - _("failed to read from socket %s"), - strerror (errno)); + virReportSystemError(in_open ? NULL : conn, errno, + "%s", _("cannot recv data")); } else { errorf (in_open ? NULL : conn, VIR_ERR_SYSTEM_ERROR, diff --git a/src/storage_backend.c b/src/storage_backend.c --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -62,6 +62,8 @@ #endif +#define VIR_FROM_THIS VIR_FROM_STORAGE + static virStorageBackendPtr backends[] = { #if WITH_STORAGE_DIR &virStorageBackendDirectory, @@ -104,9 +106,9 @@ virStorageBackendUpdateVolInfo(virConnec int ret, fd; if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open volume '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open volume '%s'"), + vol->target.path); return -1; } @@ -163,9 +165,9 @@ virStorageBackendUpdateVolInfoFD(virConn #endif if (fstat(fd, &sb) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot stat file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot stat file '%s'"), + vol->target.path); return -1; } @@ -195,9 +197,9 @@ virStorageBackendUpdateVolInfoFD(virConn */ end = lseek(fd, 0, SEEK_END); if (end == (off_t)-1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot seek to end of file '%s':%s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot seek to end of file '%s'"), + vol->target.path); return -1; } vol->allocation = end; @@ -215,16 +217,16 @@ virStorageBackendUpdateVolInfoFD(virConn start = lseek(fd, 0, SEEK_SET); if (start < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot seek to beginning of file '%s':%s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot seek to beginning of file '%s'"), + vol->target.path); return -1; } bytes = saferead(fd, buffer, sizeof(buffer)); if (bytes < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read beginning of file '%s':%s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read beginning of file '%s'"), + vol->target.path); return -1; } @@ -248,9 +250,9 @@ virStorageBackendUpdateVolInfoFD(virConn #if HAVE_SELINUX if (fgetfilecon(fd, &filecon) == -1) { if (errno != ENODATA && errno != ENOTSUP) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot get file context of %s: %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot get file context of '%s'"), + vol->target.path); return -1; } else { vol->target.perms.label = NULL; @@ -258,7 +260,7 @@ virStorageBackendUpdateVolInfoFD(virConn } else { vol->target.perms.label = strdup(filecon); if (vol->target.perms.label == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("context")); + virReportOOMError(conn); return -1; } freecon(filecon); @@ -341,10 +343,9 @@ virStorageBackendStablePath(virConnectPt usleep(100 * 1000); goto reopen; } - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read dir %s: %s"), - pool->def->target.path, - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read dir '%s'"), + pool->def->target.path); return NULL; } @@ -359,7 +360,7 @@ virStorageBackendStablePath(virConnectPt if (VIR_ALLOC_N(stablepath, strlen(pool->def->target.path) + 1 + strlen(dent->d_name) + 1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("path")); + virReportOOMError(conn); closedir(dh); return NULL; } @@ -386,7 +387,7 @@ virStorageBackendStablePath(virConnectPt stablepath = strdup(devpath); if (stablepath == NULL) - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("dup path")); + virReportOOMError(conn); return stablepath; } @@ -423,7 +424,7 @@ virStorageBackendRunProgRegex(virConnect /* Compile all regular expressions */ if (VIR_ALLOC_N(reg, nregex) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("regex")); + virReportOOMError(conn); return -1; } @@ -448,13 +449,11 @@ virStorageBackendRunProgRegex(virConnect /* Storage for matched variables */ if (VIR_ALLOC_N(groups, totgroups) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("regex groups")); + virReportOOMError(conn); goto cleanup; } if (VIR_ALLOC_N(vars, maxvars+1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("regex groups")); + virReportOOMError(conn); goto cleanup; } @@ -490,8 +489,7 @@ virStorageBackendRunProgRegex(virConnect line[vars[j+1].rm_eo] = '\0'; if ((groups[ngroup++] = strdup(line + vars[j+1].rm_so)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("regex groups")); + virReportOOMError(conn); goto cleanup; } } @@ -538,9 +536,9 @@ virStorageBackendRunProgRegex(virConnect return -1; if (err == -1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to wait for command: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("failed to wait for command '%s'"), + prog[0]); return -1; } else { if (WIFEXITED(exitstatus)) { @@ -588,8 +586,7 @@ virStorageBackendRunProgNul(virConnectPt return -1; if (VIR_ALLOC_N(v, n_columns) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("n_columns too large")); + virReportOOMError(conn); return -1; } for (i = 0; i < n_columns; i++) @@ -636,8 +633,8 @@ virStorageBackendRunProgNul(virConnectPt if (feof (fp)) err = 0; else - virStorageReportError (conn, VIR_ERR_INTERNAL_ERROR, - _("read error: %s"), strerror (errno)); + virReportSystemError(conn, errno, + _("read error on pipe to '%s'"), prog[0]); cleanup: for (i = 0; i < n_columns; i++) @@ -657,9 +654,9 @@ virStorageBackendRunProgNul(virConnectPt return -1; if (w_err == -1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to wait for command: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("failed to wait for command '%s'"), + prog[0]); return -1; } else { if (WIFEXITED(exitstatus)) { diff --git a/src/storage_backend_disk.c b/src/storage_backend_disk.c --- a/src/storage_backend_disk.c +++ b/src/storage_backend_disk.c @@ -32,6 +32,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + #define PARTHELPER BINDIR "/libvirt_parthelper" static int @@ -44,13 +46,13 @@ virStorageBackendDiskMakeDataVol(virConn if (vol == NULL) { if (VIR_ALLOC(vol) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); virStorageVolDefFree(vol); return -1; } @@ -61,14 +63,14 @@ virStorageBackendDiskMakeDataVol(virConn */ tmp = strrchr(groups[0], '/'); if ((vol->name = strdup(tmp ? tmp + 1 : groups[0])) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } } if (vol->target.path == NULL) { if ((devpath = strdup(groups[0])) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } @@ -89,15 +91,14 @@ virStorageBackendDiskMakeDataVol(virConn if (vol->key == NULL) { /* XXX base off a unique key of the underlying disk */ if ((vol->key = strdup(vol->target.path)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } } if (vol->source.extents == NULL) { if (VIR_ALLOC(vol->source.extents) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("volume extents")); + virReportOOMError(conn); return -1; } vol->source.nextent = 1; @@ -118,7 +119,7 @@ virStorageBackendDiskMakeDataVol(virConn if ((vol->source.extents[0].path = strdup(pool->def->source.devices[0].path)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("extents")); + virReportOOMError(conn); return -1; } } @@ -367,9 +368,9 @@ virStorageBackendDiskDeleteVol(virConnec if ((n = readlink(vol->target.path, devpath, sizeof(devpath))) < 0 && errno != EINVAL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Couldn't read volume target path '%s'. %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("Couldn't read volume target path '%s'"), + vol->target.path); return -1; } else if (n <= 0) { strncpy(devpath, vol->target.path, PATH_MAX); diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -122,6 +122,7 @@ const struct FileTypeInfo const fileType */ }; +#define VIR_FROM_THIS VIR_FROM_STORAGE @@ -136,9 +137,9 @@ static int virStorageBackendProbeFile(vi int len, i, ret; if ((fd = open(def->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open volume '%s': %s"), - def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open volume '%s'"), + def->target.path); return -1; } @@ -148,9 +149,9 @@ static int virStorageBackendProbeFile(vi } if ((len = read(fd, head, sizeof(head))) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read header '%s': %s"), - def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read header '%s'"), + def->target.path); close(fd); return -1; } @@ -277,7 +278,7 @@ virStorageBackendFileSystemNetFindPoolSo } if (VIR_REALLOC_N(state->list.sources, state->list.nsources+1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } memset(state->list.sources + state->list.nsources, 0, sizeof(*state->list.sources)); @@ -335,7 +336,7 @@ virStorageBackendFileSystemNetFindPoolSo xpath_ctxt = xmlXPathNewContext(doc); if (xpath_ctxt == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xpath_ctxt")); + virReportOOMError(conn); goto cleanup; } @@ -354,7 +355,7 @@ virStorageBackendFileSystemNetFindPoolSo retval = virStoragePoolSourceListFormat(conn, &state.list); if (retval == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("retval")); + virReportOOMError(conn); goto cleanup; } @@ -387,9 +388,9 @@ virStorageBackendFileSystemIsMounted(vir struct mntent *ent; if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read %s: %s"), - _PATH_MOUNTED, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read mount list '%s'"), + _PATH_MOUNTED); return -1; } @@ -485,7 +486,7 @@ virStorageBackendFileSystemMount(virConn if (pool->def->type == VIR_STORAGE_POOL_NETFS) { if (VIR_ALLOC_N(src, strlen(pool->def->source.host.name) + 1 + strlen(pool->def->source.dir) + 1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("source")); + virReportOOMError(conn); return -1; } strcpy(src, pool->def->source.host.name); @@ -493,7 +494,7 @@ virStorageBackendFileSystemMount(virConn strcat(src, pool->def->source.dir); } else { if ((src = strdup(pool->def->source.devices[0].path)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("source")); + virReportOOMError(conn); return -1; } } @@ -600,10 +601,11 @@ virStorageBackendFileSystemBuild(virConn virStoragePoolObjPtr pool, unsigned int flags ATTRIBUTE_UNUSED) { - if (virFileMakePath(pool->def->target.path) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create path '%s': %s"), - pool->def->target.path, strerror(errno)); + int err; + if ((err = virFileMakePath(pool->def->target.path)) < 0) { + virReportSystemError(conn, err, + _("cannot create path '%s'"), + pool->def->target.path); return -1; } @@ -625,9 +627,9 @@ virStorageBackendFileSystemRefresh(virCo virStorageVolDefPtr vol = NULL; if (!(dir = opendir(pool->def->target.path))) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open path '%s': %s"), - pool->def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open path '%s'"), + pool->def->target.path); goto cleanup; } @@ -674,9 +676,9 @@ virStorageBackendFileSystemRefresh(virCo if (statvfs(pool->def->target.path, &sb) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot statvfs path '%s': %s"), - pool->def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot statvfs path '%s'"), + pool->def->target.path); return -1; } pool->def->capacity = ((unsigned long long)sb.f_frsize * @@ -688,7 +690,7 @@ virStorageBackendFileSystemRefresh(virCo return 0; no_memory: - virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); /* fallthrough */ cleanup: @@ -740,9 +742,9 @@ virStorageBackendFileSystemDelete(virCon /* XXX delete all vols first ? */ if (unlink(pool->def->target.path) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unlink path '%s': %s"), - pool->def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot unlink path '%s'"), + pool->def->target.path); return -1; } @@ -764,7 +766,7 @@ virStorageBackendFileSystemVolCreate(vir if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("target")); + virReportOOMError(conn); return -1; } vol->type = VIR_STORAGE_VOL_FILE; @@ -773,17 +775,16 @@ virStorageBackendFileSystemVolCreate(vir strcat(vol->target.path, vol->name); vol->key = strdup(vol->target.path); if (vol->key == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("storage vol key")); + virReportOOMError(conn); return -1; } if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, vol->target.perms.mode)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); return -1; } @@ -798,9 +799,9 @@ virStorageBackendFileSystemVolCreate(vir if (bytes > remain) bytes = remain; if ((bytes = safewrite(fd, zeros, bytes)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot fill file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot fill file '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; @@ -811,25 +812,25 @@ virStorageBackendFileSystemVolCreate(vir /* Now seek to final size, possibly making the file sparse */ if (ftruncate(fd, vol->capacity) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot extend file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot extend file '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; } } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); return -1; } if ((fd = open(vol->target.path, O_RDWR)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); return -1; } } else { @@ -862,9 +863,9 @@ virStorageBackendFileSystemVolCreate(vir } if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); unlink(vol->target.path); return -1; } @@ -897,9 +898,9 @@ virStorageBackendFileSystemVolCreate(vir } if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); unlink(vol->target.path); return -1; } @@ -914,18 +915,18 @@ virStorageBackendFileSystemVolCreate(vir /* We can only chown/grp if root */ if (getuid() == 0) { if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file owner '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file owner '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; } } if (fchmod(fd, vol->target.perms.mode) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file mode '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file mode '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; @@ -939,9 +940,9 @@ virStorageBackendFileSystemVolCreate(vir } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot close file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + vol->target.path); unlink(vol->target.path); return -1; } @@ -962,9 +963,9 @@ virStorageBackendFileSystemVolDelete(vir if (unlink(vol->target.path) < 0) { /* Silently ignore failures where the vol has already gone away */ if (errno != ENOENT) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unlink file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot unlink file '%s'"), + vol->target.path); return -1; } } diff --git a/src/storage_backend_iscsi.c b/src/storage_backend_iscsi.c --- a/src/storage_backend_iscsi.c +++ b/src/storage_backend_iscsi.c @@ -39,6 +39,9 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + + static int virStorageBackendISCSITargetIP(virConnectPtr conn, const char *hostname, @@ -204,9 +207,9 @@ virStorageBackendISCSINewLun(virConnectP usleep(100 * 1000); goto reopen; } - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open %s: %s"), - devpath, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open '%s'"), + devpath); goto cleanup; } @@ -322,9 +325,9 @@ virStorageBackendISCSIFindLUNs(virConnec sysdir = opendir(sysfs_path); if (sysdir == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to opendir sysfs path %s: %s"), - sysfs_path, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); return -1; } while ((sys_dirent = readdir(sysdir))) { @@ -354,10 +357,9 @@ virStorageBackendISCSIFindLUNs(virConnec n = scandir(sysfs_path, &namelist, notdotdir, versionsort); if (n <= 0) { /* we didn't find any reasonable entries; return failure */ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to find any LUNs for session %s: %s"), - session, strerror(errno)); - + virReportSystemError(conn, errno, + _("Failed to find any LUNs for session '%s'"), + session); return -1; } @@ -407,9 +409,9 @@ virStorageBackendISCSIFindLUNs(virConnec sysdir = opendir(sysfs_path); if (sysdir == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to opendir sysfs path %s: %s"), - sysfs_path, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); retval = -1; goto namelist_cleanup; } @@ -443,9 +445,9 @@ virStorageBackendISCSIFindLUNs(virConnec host, bus, target, lun); sysdir = opendir(sysfs_path); if (sysdir == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to opendir sysfs path %s: %s"), - sysfs_path, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); retval = -1; goto namelist_cleanup; } diff --git a/src/storage_backend_logical.c b/src/storage_backend_logical.c --- a/src/storage_backend_logical.c +++ b/src/storage_backend_logical.c @@ -37,6 +37,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + #define PV_BLANK_SECTOR_SIZE 512 @@ -400,22 +402,22 @@ virStorageBackendLogicalBuildPool(virCon * rather than trying to figure out if we're a disk or partition */ if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open device %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open device '%s'"), + pool->def->source.devices[i].path); goto cleanup; } if (safewrite(fd, zeros, sizeof(zeros)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot clear device header %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot clear device header of '%s'"), + pool->def->source.devices[i].path); close(fd); goto cleanup; } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot close device %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot close device '%s'"), + pool->def->source.devices[i].path); goto cleanup; } @@ -538,10 +540,9 @@ virStorageBackendLogicalDeletePool(virCo pvargv[1] = pool->def->source.devices[i].path; if (virRun(conn, pvargv, NULL) < 0) { error = -1; - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot remove PV device %s: %s"), - pool->def->source.devices[i].path, - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot remove PV device '%s'"), + pool->def->source.devices[i].path); break; } } @@ -591,41 +592,41 @@ virStorageBackendLogicalCreateVol(virCon return -1; if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); goto cleanup; } /* We can only chown/grp if root */ if (getuid() == 0) { if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file owner '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file owner '%s'"), + vol->target.path); goto cleanup; } } if (fchmod(fd, vol->target.perms.mode) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file mode '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file mode '%s'"), + vol->target.path); goto cleanup; } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot close file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + vol->target.path); goto cleanup; } fd = -1; /* Fill in data about this new vol */ if (virStorageBackendLogicalFindLVs(conn, pool, vol) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot find newly created volume '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot find newly created volume '%s'"), + vol->target.path); goto cleanup; } diff --git a/src/storage_conf.c b/src/storage_conf.c --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -43,6 +43,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + /* Work around broken limits.h on debian etch */ #if defined __GNUC__ && defined _GCC_LIMITS_H_ && ! defined ULLONG_MAX # define ULLONG_MAX ULONG_LONG_MAX @@ -1405,9 +1407,9 @@ virStoragePoolObjSaveDef(virConnectPtr c char path[PATH_MAX]; if ((err = virFileMakePath(driver->configDir))) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - driver->configDir, strerror(err)); + virStorageReportError(conn, errno, + _("cannot create config directory %s"), + driver->configDir); return -1; } @@ -1448,24 +1450,24 @@ virStoragePoolObjSaveDef(virConnectPtr c if ((fd = open(pool->configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config file %s: %s"), - pool->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create config file %s"), + pool->configFile); goto cleanup; } towrite = strlen(xml); if (safewrite(fd, xml, towrite) != towrite) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot write config file %s: %s"), - pool->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot write config file %s"), + pool->configFile); goto cleanup; } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot save config file %s: %s"), - pool->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot save config file %s"), + pool->configFile); goto cleanup; } diff --git a/src/storage_driver.c b/src/storage_driver.c --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -41,6 +41,8 @@ #include "memory.h" #include "storage_backend.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + #define storageLog(msg...) fprintf(stderr, msg) static virStorageDriverStatePtr driverState; @@ -379,8 +381,7 @@ storageListPools(virConnectPtr conn, if (virStoragePoolObjIsActive(driver->pools.objs[i])) { if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) { virStoragePoolObjUnlock(driver->pools.objs[i]); - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("names")); + virReportOOMError(conn); goto cleanup; } got++; @@ -428,8 +429,7 @@ storageListDefinedPools(virConnectPtr co if (!virStoragePoolObjIsActive(driver->pools.objs[i])) { if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) { virStoragePoolObjUnlock(driver->pools.objs[i]); - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("names")); + virReportOOMError(conn); goto cleanup; } got++; @@ -952,25 +952,24 @@ storagePoolSetAutostart(virStoragePoolPt int err; if ((err = virFileMakePath(driver->autostartDir))) { - virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(obj->conn, err, + _("cannot create autostart directory %s"), + driver->autostartDir); goto cleanup; } if (symlink(pool->configFile, pool->autostartLink) < 0) { - virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s' to '%s': %s"), - pool->autostartLink, pool->configFile, - strerror(errno)); + virReportSystemError(obj->conn, errno, + _("Failed to create symlink '%s' to '%s'"), + pool->autostartLink, pool->configFile); goto cleanup; } } else { if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - pool->autostartLink, strerror(errno)); + virReportSystemError(obj->conn, errno, + _("Failed to delete symlink '%s'"), + pool->autostartLink); goto cleanup; } } @@ -1042,8 +1041,7 @@ storagePoolListVolumes(virStoragePoolPtr for (i = 0 ; i < pool->volumes.count && n < maxnames ; i++) { if ((names[n++] = strdup(pool->volumes.objs[i]->name)) == NULL) { - virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, - "%s", _("name")); + virReportOOMError(obj->conn); goto cleanup; } } @@ -1224,7 +1222,7 @@ storageVolumeCreateXML(virStoragePoolPtr if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { - virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(obj->conn); goto cleanup; } @@ -1453,7 +1451,7 @@ storageVolumeGetPath(virStorageVolPtr ob ret = strdup(vol->target.path); if (ret == NULL) - virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, "%s", _("path")); + virReportOOMError(obj->conn); cleanup: if (pool) diff --git a/src/test.c b/src/test.c --- a/src/test.c +++ b/src/test.c @@ -46,6 +46,8 @@ #include "xml.h" #include "threads.h" +#define VIR_FROM_THIS VIR_FROM_TEST + #define MAX_CPUS 128 struct _testCell { @@ -154,7 +156,7 @@ testBuildCapabilities(virConnectPtr conn return caps; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); virCapabilitiesFree(caps); return NULL; } @@ -195,7 +197,7 @@ static const char *defaultPoolXML = static const unsigned long long defaultPoolCap = (100 * 1024 * 1024 * 1024ull); static const unsigned long long defaultPoolAlloc = 0; -static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr pool); +static int testStoragePoolObjSetDefaults(virConnectPtr conn, virStoragePoolObjPtr pool); static int testOpenDefault(virConnectPtr conn) { int u; @@ -209,7 +211,7 @@ static int testOpenDefault(virConnectPtr virStoragePoolObjPtr poolobj = NULL; if (VIR_ALLOC(privconn) < 0) { - testError(conn, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn); return VIR_DRV_OPEN_ERROR; } if (virMutexInit(&privconn->lock) < 0) { @@ -223,7 +225,8 @@ static int testOpenDefault(virConnectPtr conn->privateData = privconn; if (gettimeofday(&tv, NULL) < 0) { - testError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("getting time of day")); + virReportSystemError(conn, errno, + "%s", _("getting time of day")); goto error; } @@ -276,7 +279,7 @@ static int testOpenDefault(virConnectPtr goto error; } - if (testStoragePoolObjSetDefaults(poolobj) == -1) { + if (testStoragePoolObjSetDefaults(conn, poolobj) == -1) { virStoragePoolObjUnlock(poolobj); goto error; } @@ -336,7 +339,7 @@ static int testOpenFromFile(virConnectPt virDomainObjPtr dom; testConnPtr privconn; if (VIR_ALLOC(privconn) < 0) { - testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn); return VIR_DRV_OPEN_ERROR; } if (virMutexInit(&privconn->lock) < 0) { @@ -353,9 +356,9 @@ static int testOpenFromFile(virConnectPt goto error; if ((fd = open(file, O_RDONLY)) < 0) { - testError(NULL, VIR_ERR_INTERNAL_ERROR, - _("loading host definition file '%s': %s"), - file, strerror(errno)); + virReportSystemError(NULL, errno, + _("loading host definition file '%s'"), + file); goto error; } @@ -573,7 +576,7 @@ static int testOpenFromFile(virConnectPt goto error; } - if (testStoragePoolObjSetDefaults(pool) == -1) { + if (testStoragePoolObjSetDefaults(conn, pool) == -1) { virStoragePoolObjUnlock(pool); goto error; } @@ -673,8 +676,8 @@ static char *testGetHostname (virConnect result = virGetHostname(); if (result == NULL) { - testError (conn, VIR_ERR_SYSTEM_ERROR, "%s", - strerror (errno)); + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); return NULL; } /* Caller frees this string. */ @@ -703,7 +706,7 @@ static char *testGetCapabilities (virCon char *xml; testDriverLock(privconn); if ((xml = virCapabilitiesFormatXML(privconn->caps)) == NULL) - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); testDriverUnlock(privconn); return xml; } @@ -1111,42 +1114,42 @@ static int testDomainSave(virDomainPtr d xml = testDomainDumpXML(domain, 0); if (xml == NULL) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' failed to allocate space for metadata: %s"), - domain->name, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' failed to allocate space for metadata"), + domain->name); goto cleanup; } if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': open failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': open failed"), + domain->name, path); goto cleanup; } len = strlen(xml); if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } if (safewrite(fd, (char*)&len, sizeof(len)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } if (safewrite(fd, xml, len) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } if (close(fd) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } fd = -1; @@ -1189,13 +1192,15 @@ static int testDomainRestore(virConnectP int ret = -1; if ((fd = open(path, O_RDONLY)) < 0) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot read domain image")); + virReportSystemError(conn, errno, + _("cannot read domain image '%s'"), + path); goto cleanup; } - if (read(fd, magic, sizeof(magic)) != sizeof(magic)) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("incomplete save header")); + if (saferead(fd, magic, sizeof(magic)) != sizeof(magic)) { + virReportSystemError(conn, errno, + _("incomplete save header in '%s'"), + path); goto cleanup; } if (memcmp(magic, TEST_SAVE_MAGIC, sizeof(magic))) { @@ -1203,9 +1208,10 @@ static int testDomainRestore(virConnectP "%s", _("mismatched header magic")); goto cleanup; } - if (read(fd, (char*)&len, sizeof(len)) != sizeof(len)) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to read metadata length")); + if (saferead(fd, (char*)&len, sizeof(len)) != sizeof(len)) { + virReportSystemError(conn, errno, + _("failed to read metadata length in '%s'"), + path); goto cleanup; } if (len < 1 || len > 8192) { @@ -1214,12 +1220,12 @@ static int testDomainRestore(virConnectP goto cleanup; } if (VIR_ALLOC_N(xml, len+1) < 0) { - testError(conn, VIR_ERR_NO_MEMORY, "xml"); + virReportOOMError(conn); goto cleanup; } - if (read(fd, xml, len) != len) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("incomplete metdata")); + if (saferead(fd, xml, len) != len) { + virReportSystemError(conn, errno, + _("incomplete metdata in '%s'"), path); goto cleanup; } xml[len] = '\0'; @@ -1269,21 +1275,21 @@ static int testDomainCoreDump(virDomainP } if ((fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("domain '%s' coredump: failed to open %s: %s"), - domain->name, to, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("domain '%s' coredump: failed to open %s"), + domain->name, to); goto cleanup; } if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("domain '%s' coredump: failed to write header to %s: %s"), - domain->name, to, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("domain '%s' coredump: failed to write header to %s"), + domain->name, to); goto cleanup; } if (close(fd) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("domain '%s' coredump: write failed: %s: %s"), - domain->name, to, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("domain '%s' coredump: write failed: %s"), + domain->name, to); goto cleanup; } privdom->state = VIR_DOMAIN_SHUTOFF; @@ -1306,7 +1312,7 @@ cleanup: static char *testGetOSType(virDomainPtr dom) { char *ret = strdup("linux"); if (!ret) - testError(dom->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(dom->conn); return ret; } @@ -1491,7 +1497,7 @@ static int testListDefinedDomains(virCon return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < maxnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -1682,7 +1688,7 @@ static char *testDomainGetSchedulerType( *nparams = 1; type = strdup("fair"); if (!type) - testError(domain->conn, VIR_ERR_NO_MEMORY, "schedular"); + virReportOOMError(domain->conn); return type; } @@ -1864,7 +1870,7 @@ static int testListNetworks(virConnectPt return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -1907,7 +1913,7 @@ static int testListDefinedNetworks(virCo return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -2099,7 +2105,7 @@ static char *testNetworkGetBridgeName(vi if (privnet->def->bridge && !(bridge = strdup(privnet->def->bridge))) { - testError(network->conn, VIR_ERR_NO_MEMORY, "network"); + virReportOOMError(network->conn); goto cleanup; } @@ -2164,7 +2170,8 @@ cleanup: * Storage Driver routines */ -static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr pool) { +static int testStoragePoolObjSetDefaults(virConnectPtr conn, + virStoragePoolObjPtr pool) { pool->def->capacity = defaultPoolCap; pool->def->allocation = defaultPoolAlloc; @@ -2172,7 +2179,7 @@ static int testStoragePoolObjSetDefaults pool->configFile = strdup("\0"); if (!pool->configFile) { - testError(NULL, VIR_ERR_NO_MEMORY, "configFile"); + virReportOOMError(conn); return -1; } @@ -2284,7 +2291,7 @@ testStorageListPools(virConnectPtr conn, return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -2331,7 +2338,7 @@ testStorageListDefinedPools(virConnectPt return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -2408,7 +2415,7 @@ testStoragePoolCreate(virConnectPtr conn } def = NULL; - if (testStoragePoolObjSetDefaults(pool) == -1) { + if (testStoragePoolObjSetDefaults(conn, pool) == -1) { virStoragePoolObjRemove(&privconn->pools, pool); pool = NULL; goto cleanup; @@ -2447,7 +2454,7 @@ testStoragePoolDefine(virConnectPtr conn } def = NULL; - if (testStoragePoolObjSetDefaults(pool) == -1) { + if (testStoragePoolObjSetDefaults(conn, pool) == -1) { virStoragePoolObjRemove(&privconn->pools, pool); pool = NULL; goto cleanup; @@ -2806,7 +2813,7 @@ testStoragePoolListVolumes(virStoragePoo for (i = 0 ; i < privpool->volumes.count && n < maxnames ; i++) { if ((names[n++] = strdup(privpool->volumes.objs[i]->name)) == NULL) { - testError(pool->conn, VIR_ERR_NO_MEMORY, "%s", _("name")); + virReportOOMError(pool->conn); goto cleanup; } } @@ -2986,14 +2993,14 @@ testStorageVolumeCreateXML(virStoragePoo if (VIR_REALLOC_N(privpool->volumes.objs, privpool->volumes.count+1) < 0) { - testError(pool->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(pool->conn); goto cleanup; } if (VIR_ALLOC_N(privvol->target.path, strlen(privpool->def->target.path) + 1 + strlen(privvol->name) + 1) < 0) { - testError(pool->conn, VIR_ERR_NO_MEMORY, "%s", _("target")); + virReportOOMError(pool->conn); goto cleanup; } @@ -3002,8 +3009,7 @@ testStorageVolumeCreateXML(virStoragePoo strcat(privvol->target.path, privvol->name); privvol->key = strdup(privvol->target.path); if (privvol->key == NULL) { - testError(pool->conn, VIR_ERR_INTERNAL_ERROR, "%s", - _("storage vol key")); + virReportOOMError(pool->conn); goto cleanup; } @@ -3224,7 +3230,7 @@ testStorageVolumeGetPath(virStorageVolPt ret = strdup(privvol->target.path); if (ret == NULL) - testError(vol->conn, VIR_ERR_NO_MEMORY, "%s", _("path")); + virReportOOMError(vol->conn); cleanup: if (privpool) diff --git a/src/uml_driver.c b/src/uml_driver.c --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -64,6 +64,8 @@ #include "datatypes.h" #include "logging.h" +#define VIR_FROM_THIS VIR_FROM_UML + /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" @@ -159,7 +161,7 @@ umlIdentifyOneChrPTY(virConnectPtr conn, char *res = NULL; int retries = 0; if (virAsprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } requery: @@ -168,7 +170,7 @@ requery: if (STRPREFIX(res, "pts:")) { VIR_FREE(def->data.file.path); if ((def->data.file.path = strdup(res + 4)) == NULL) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); VIR_FREE(res); VIR_FREE(cmd); return -1; @@ -523,7 +525,7 @@ static int umlReadPidFile(virConnectPtr vm->pid = -1; if (virAsprintf(&pidfile, "%s/%s/pid", driver->monitorDir, vm->def->name) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } @@ -549,9 +551,9 @@ reopen: cleanup: if (rc != 0) - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to read pid: %s: %s"), - pidfile, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to read pid: %s"), + pidfile); VIR_FREE(pidfile); return rc; } @@ -564,7 +566,7 @@ static int umlMonitorAddress(virConnectP if (virAsprintf(&sockname, "%s/%s/mconsole", driver->monitorDir, vm->def->name) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } @@ -597,16 +599,16 @@ restat: } if ((vm->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot open socket %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot open socket")); return -1; } memset(addr.sun_path, 0, sizeof addr.sun_path); sprintf(addr.sun_path + 1, "%u", getpid()); if (bind(vm->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot bind socket %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot bind socket")); close(vm->monitor); vm->monitor = -1; return -1; @@ -658,9 +660,9 @@ static int umlMonitorCommand(virConnectP req.version = MONITOR_VERSION; req.length = strlen(cmd); if (req.length > (MONITOR_BUFLEN-1)) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot send too long command %s: %s"), - cmd, strerror(EINVAL)); + virReportSystemError(conn, EINVAL, + _("cannot send too long command %s (%d bytes)"), + cmd, req.length); return -1; } strncpy(req.data, cmd, req.length); @@ -668,9 +670,9 @@ static int umlMonitorCommand(virConnectP if (sendto(vm->monitor, &req, sizeof req, 0, (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot send command %s: %s"), - cmd, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot send command %s"), + cmd); return -1; } @@ -678,15 +680,14 @@ static int umlMonitorCommand(virConnectP addrlen = sizeof(addr); if (recvfrom(vm->monitor, &res, sizeof res, 0, (struct sockaddr *)&addr, &addrlen) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot read reply %s: %s"), - cmd, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read reply %s"), + cmd); goto error; } if (VIR_REALLOC_N(retdata, retlen + res.length) < 0) { - umlReportError(conn, NULL, NULL, - VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); goto error; } memcpy(retdata + retlen, res.data, res.length); @@ -740,39 +741,38 @@ static int umlStartVMDaemon(virConnectPt * in a sub-process so its hard to feed back a useful error */ if (stat(vm->def->os.kernel, &sb) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Cannot find UML kernel %s: %s"), - vm->def->os.kernel, strerror(errno)); + virReportSystemError(conn, errno, + _("Cannot find UML kernel %s"), + vm->def->os.kernel); return -1; } if (virFileMakePath(driver->logDir) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create log directory %s"), - driver->logDir); + virReportSystemError(conn, errno, + _("cannot create log directory %s"), + driver->logDir); return -1; } if (virAsprintf(&logfile, "%s/%s.log", driver->logDir, vm->def->name) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } if ((logfd = open(logfile, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create logfile %s: %s"), - logfile, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to create logfile %s"), + logfile); VIR_FREE(logfile); return -1; } VIR_FREE(logfile); if (umlSetCloseExec(logfd) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Unable to set VM logfile close-on-exec flag %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("Unable to set VM logfile close-on-exec flag")); close(logfd); return -1; } @@ -909,7 +909,7 @@ static virDrvOpenStatus umlOpen(virConne } else { conn->uri = xmlParseURI(uid ? "uml:///session" : "uml:///system"); if (!conn->uri) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,NULL); + virReportOOMError(conn); return VIR_DRV_OPEN_ERROR; } } @@ -946,8 +946,7 @@ static char *umlGetCapabilities(virConne umlDriverLock(driver); if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, - "%s", _("failed to allocate space for capabilities support")); + virReportOOMError(conn); umlDriverUnlock(driver); return xml; @@ -1157,8 +1156,8 @@ umlGetHostname (virConnectPtr conn) result = virGetHostname(); if (result == NULL) { - umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, - "%s", strerror (errno)); + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); return NULL; } /* Caller frees this string. */ @@ -1326,8 +1325,7 @@ static char *umlDomainGetOSType(virDomai } if (!(type = strdup(vm->def->os.type))) - umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_MEMORY, - "%s", _("failed to allocate space for ostype")); + virReportOOMError(dom->conn); cleanup: if (vm) @@ -1511,8 +1509,7 @@ static int umlListDefinedDomains(virConn virDomainObjLock(driver->domains.objs[i]); if (!virDomainIsActive(driver->domains.objs[i])) { if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, - "%s", _("failed to allocate space for VM name string")); + virReportOOMError(conn); virDomainObjUnlock(driver->domains.objs[i]); goto cleanup; } @@ -1711,23 +1708,23 @@ static int umlDomainSetAutostart(virDoma int err; if ((err = virFileMakePath(driver->autostartDir))) { - umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(dom->conn, err, + _("cannot create autostart directory %s"), + driver->autostartDir); goto cleanup; } if (symlink(configFile, autostartLink) < 0) { - umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s to '%s': %s"), - autostartLink, configFile, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to create symlink '%s to '%s'"), + autostartLink, configFile); goto cleanup; } } else { if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - autostartLink, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to delete symlink '%s'"), + autostartLink); goto cleanup; } } @@ -1786,8 +1783,8 @@ umlDomainBlockPeek (virDomainPtr dom, /* The path is correct, now try to open it and get its size. */ fd = open (path, O_RDONLY); if (fd == -1) { - umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, - "%s", strerror (errno)); + virReportSystemError(dom->conn, errno, + _("cannot open %s"), path); goto cleanup; } @@ -1797,8 +1794,8 @@ umlDomainBlockPeek (virDomainPtr dom, */ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || saferead (fd, buffer, size) == (ssize_t) -1) { - umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, - "%s", strerror (errno)); + virReportSystemError(dom->conn, errno, + _("cannot read %s"), path); goto cleanup; } diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -67,6 +67,7 @@ #define virLog(msg...) fprintf(stderr, msg) +#define VIR_FROM_THIS VIR_FROM_NONE #define ReportError(conn, code, fmt...) \ virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ @@ -212,37 +213,36 @@ __virExec(virConnectPtr conn, */ sigfillset(&newmask); if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot block signals: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot block signals")); return -1; } if ((null = open("/dev/null", O_RDONLY)) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open %s: %s"), - "/dev/null", strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open %s"), + "/dev/null"); goto cleanup; } if (outfd != NULL) { if (*outfd == -1) { if (pipe(pipeout) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create pipe: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot create pipe")); goto cleanup; } if ((flags & VIR_EXEC_NONBLOCK) && virSetNonBlock(pipeout[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set non-blocking file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set non-blocking file descriptor flag")); goto cleanup; } if (virSetCloseExec(pipeout[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set close-on-exec file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set close-on-exec file descriptor flag")); goto cleanup; } @@ -259,21 +259,21 @@ __virExec(virConnectPtr conn, if (errfd != NULL) { if (*errfd == -1) { if (pipe(pipeerr) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to create pipe: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("Failed to create pipe")); goto cleanup; } if ((flags & VIR_EXEC_NONBLOCK) && virSetNonBlock(pipeerr[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set non-blocking file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set non-blocking file descriptor flag")); goto cleanup; } if (virSetCloseExec(pipeerr[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set close-on-exec file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set close-on-exec file descriptor flag")); goto cleanup; } @@ -288,8 +288,8 @@ __virExec(virConnectPtr conn, } if ((pid = fork()) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot fork child process: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot fork child process")); goto cleanup; } @@ -307,9 +307,8 @@ __virExec(virConnectPtr conn, /* Restore our original signal mask now child is safely running */ if (pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unblock signals: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot unblock signals")); return -1; } @@ -345,9 +344,8 @@ __virExec(virConnectPtr conn, and don't want to propagate that to children */ sigemptyset(&newmask); if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unblock signals: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot unblock signals")); return -1; } @@ -363,24 +361,21 @@ __virExec(virConnectPtr conn, if (flags & VIR_EXEC_DAEMON) { if (setsid() < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot become session leader: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot become session leader")); _exit(1); } if (chdir("/") < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot change to root directory: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot change to root directory: %s")); _exit(1); } pid = fork(); if (pid < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot fork child process: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot fork child process")); _exit(1); } @@ -390,20 +385,20 @@ __virExec(virConnectPtr conn, if (dup2(infd >= 0 ? infd : null, STDIN_FILENO) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to setup stdin file handle: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("failed to setup stdin file handle")); _exit(1); } if (childout > 0 && dup2(childout, STDOUT_FILENO) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to setup stdout file handle: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("failed to setup stdout file handle")); _exit(1); } if (childerr > 0 && dup2(childerr, STDERR_FILENO) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to setup stderr file handle: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("failed to setup stderr file handle")); _exit(1); } @@ -419,9 +414,9 @@ __virExec(virConnectPtr conn, else execvp(argv[0], (char **) argv); - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot execute binary '%s': %s"), - argv[0], strerror(errno)); + virReportSystemError(conn, errno, + _("cannot execute binary %s"), + argv[0]); _exit(1); @@ -535,8 +530,8 @@ virPipeReadUntilEOF(virConnectPtr conn, continue; pollerr: - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("poll error: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("poll error")); goto error; } @@ -599,16 +594,15 @@ virRun(virConnectPtr conn, while ((waitret = waitpid(childpid, &exitstatus, 0) == -1) && errno == EINTR); if (waitret == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot wait for '%s': %s"), - argv[0], strerror(errno)); + virReportSystemError(conn, errno, + _("cannot wait for '%s'"), + argv[0]); goto error; } if (status == NULL) { errno = EINVAL; if (WIFEXITED(exitstatus) && WEXITSTATUS(exitstatus) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, _("'%s' exited with non-zero status %d and " "signal %d: %s"), argv_str, diff --git a/src/virterror.c b/src/virterror.c --- a/src/virterror.c +++ b/src/virterror.c @@ -20,6 +20,7 @@ #include "logging.h" #include "memory.h" #include "threads.h" +#include "util.h" virThreadLocal virLastErr; @@ -332,7 +333,7 @@ virResetLastError(void) * If the connection object was discovered to be invalid by * an API call, then the error will be reported against the * global error object. - * + * * Since 0.6.0, all errors reported in the per-connection object * are also duplicated in the global error object. As such an * application can always use virGetLastError(). This method @@ -363,7 +364,7 @@ virConnGetLastError(virConnectPtr conn) * If the connection object was discovered to be invalid by * an API call, then the error will be reported against the * global error object. - * + * * Since 0.6.0, all errors reported in the per-connection object * are also duplicated in the global error object. As such an * application can always use virGetLastError(). This method @@ -978,7 +979,7 @@ virErrorMsg(virErrorNumber error, const void virReportErrorHelper(virConnectPtr conn, int domcode, int errcode, const char *filename ATTRIBUTE_UNUSED, const char *funcname ATTRIBUTE_UNUSED, - long long linenr ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED, const char *fmt, ...) { va_list args; @@ -1000,3 +1001,69 @@ void virReportErrorHelper(virConnectPtr } + + + + +void virReportSystemErrorFull(virConnectPtr conn, + int domcode, + int theerrno, + const char *filename ATTRIBUTE_UNUSED, + const char *funcname ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED, + const char *fmt, ...) +{ + va_list args; + char errorMessage[1024]; + char systemError[1024]; + char *theerrnostr; + const char *virerr; + char *combined = NULL; + +#ifdef HAVE_STRERROR_R +#ifdef __USE_GNU + /* Annoying linux specific API contract */ + theerrnostr = strerror_r(theerrno, systemError, sizeof(systemError)); +#else + strerror_r(theerrno, systemError, sizeof(systemError)); + theerrnostr = systemError; +#endif +#else + /* Mingw lacks strerror_r() and its strerror() is definitely not + * threadsafe, so safest option is to just print the raw errno + * value - we can at least reliably & safely look it up in the + * header files for debug purposes + */ + snprintf(systemError, sizeof(systemError), "errno=%d", theerrno); + theerrnostr = systemError; +#endif + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args); + va_end(args); + } else { + errorMessage[0] = '\0'; + } + + if (virAsprintf(&combined, "%s: %s", errorMessage, theerrnostr) < 0) + combined = theerrnostr; /* OOM, so lets just pass the strerror info as best effort */ + + virerr = virErrorMsg(VIR_ERR_SYSTEM_ERROR, (errorMessage[0] ? errorMessage : NULL)); + virRaiseError(conn, NULL, NULL, domcode, VIR_ERR_SYSTEM_ERROR, VIR_ERR_ERROR, + virerr, errorMessage, NULL, -1, -1, virerr, errorMessage); +} + + +void virReportOOMErrorFull(virConnectPtr conn, + int domcode, + const char *filename ATTRIBUTE_UNUSED, + const char *funcname ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED) +{ + const char *virerr; + + virerr = virErrorMsg(VIR_ERR_NO_MEMORY, NULL); + virRaiseError(conn, NULL, NULL, domcode, VIR_ERR_NO_MEMORY, VIR_ERR_ERROR, + virerr, NULL, NULL, -1, -1, virerr, NULL); +} diff --git a/src/virterror_internal.h b/src/virterror_internal.h --- a/src/virterror_internal.h +++ b/src/virterror_internal.h @@ -48,10 +48,36 @@ const char *virErrorMsg(virErrorNumber e void virReportErrorHelper(virConnectPtr conn, int domcode, int errcode, const char *filename ATTRIBUTE_UNUSED, const char *funcname ATTRIBUTE_UNUSED, - long long linenr ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 7, 8); +void virReportSystemErrorFull(virConnectPtr conn, + int domcode, + int theerrno, + const char *filename, + const char *funcname, + size_t linenr, + const char *fmt, ...) + ATTRIBUTE_FORMAT(printf, 7, 8); + +#define virReportSystemError(conn, theerrno, fmt,...) \ + virReportSystemErrorFull((conn), \ + VIR_FROM_THIS, \ + (theerrno), \ + __FILE__, __FUNCTION__, __LINE__, \ + (fmt), __VA_ARGS__) + +void virReportOOMErrorFull(virConnectPtr conn, + int domcode, + const char *filename, + const char *funcname, + size_t linenr); + +#define virReportOOMError(conn) \ + virReportOOMErrorFull((conn), VIR_FROM_THIS, \ + __FILE__, __FUNCTION__, __LINE__) + void virSetGlobalError(void); void virSetConnError(virConnectPtr conn); diff --git a/src/xen_inotify.c b/src/xen_inotify.c --- a/src/xen_inotify.c +++ b/src/xen_inotify.c @@ -41,6 +41,8 @@ #include "xm_internal.h" /* for xenXMDomainConfigParse */ +#define VIR_FROM_THIS VIR_FROM_XEN_INOTIFY + #define virXenInotifyError(conn, code, fmt...) \ virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) @@ -390,9 +392,10 @@ xenInotifyOpen(virConnectPtr conn ATTRIB } /* populate initial list */ - if (!(dh = opendir(configDir))) { - virXenInotifyError (NULL, VIR_ERR_INTERNAL_ERROR, - "%s", strerror(errno)); + if (!(dh = opendir(configDir))) { + virReportSystemError(NULL, errno, + _("cannot open directory: %s"), + configDir); return -1; } while ((ent = readdir(dh))) { diff --git a/src/xen_internal.c b/src/xen_internal.c --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -54,6 +54,8 @@ #include "capabilities.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_XEN + /* * so far there is 2 versions of the structures usable for doing * hypervisor calls. @@ -2404,8 +2406,9 @@ xenHypervisorMakeCapabilities(virConnect cpuinfo = fopen ("/proc/cpuinfo", "r"); if (cpuinfo == NULL) { if (errno != ENOENT) { - virXenError (conn, VIR_ERR_SYSTEM_ERROR, - "/proc/cpuinfo: %s", strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read file %s"), + "/proc/cpuinfo"); return NULL; } } @@ -2414,9 +2417,9 @@ xenHypervisorMakeCapabilities(virConnect if (capabilities == NULL) { if (errno != ENOENT) { fclose(cpuinfo); - virXenError (conn, VIR_ERR_SYSTEM_ERROR, - "/sys/hypervisor/properties/capabilities: %s", - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read file %s"), + "/sys/hypervisor/properties/capabilities"); return NULL; } } diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -44,6 +44,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_XEN + static int xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); static int @@ -451,7 +453,8 @@ xenUnifiedGetHostname (virConnectPtr con result = virGetHostname(); if (result == NULL) { - xenUnifiedError (conn, VIR_ERR_SYSTEM_ERROR, "%s", strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); return NULL; } /* Caller frees this string. */ diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -49,6 +49,8 @@ /* required for cpumap_t */ #include <xen/dom0_ops.h> +#define VIR_FROM_THIS VIR_FROM_XEND + #ifndef PROXY /* @@ -4093,14 +4095,13 @@ xenDaemonDomainMigratePrepare (virConnec if (uri_in == NULL) { r = gethostname (hostname, HOST_NAME_MAX+1); if (r == -1) { - virXendError (dconn, VIR_ERR_SYSTEM_ERROR, - _("gethostname failed: %s"), strerror (errno)); + virReportSystemError(dconn, errno, + _("unable to resolve name %s"), hostname); return -1; } *uri_out = strdup (hostname); if (*uri_out == NULL) { - virXendError (dconn, VIR_ERR_SYSTEM_ERROR, - _("failed to strdup hostname: %s"), strerror (errno)); + virReportOOMError(dconn); return -1; } } @@ -4734,9 +4735,9 @@ xenDaemonDomainBlockPeek (virDomainPtr d /* The path is correct, now try to open it and get its size. */ fd = open (path, O_RDONLY); if (fd == -1) { - virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, - _("failed to open for reading: %s: %s"), - path, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("failed to open for reading: %s"), + path); goto cleanup; } @@ -4746,9 +4747,9 @@ xenDaemonDomainBlockPeek (virDomainPtr d */ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || saferead (fd, buffer, size) == (ssize_t) -1) { - virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, - _("failed to lseek or read from file: %s: %s"), - path, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("failed to lseek or read from file: %s"), + path); goto cleanup; } diff --git a/src/xm_internal.c b/src/xm_internal.c --- a/src/xm_internal.c +++ b/src/xm_internal.c @@ -47,6 +47,7 @@ #include "memory.h" #include "logging.h" +#define VIR_FROM_THIS VIR_FROM_XENXM /* The true Xen limit varies but so far is always way less than 1024, which is the Linux kernel limit according @@ -276,7 +277,7 @@ static int xenXMConfigCopyStringInternal } if (!(*value = strdup(val->str))) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } @@ -405,8 +406,9 @@ xenXMConfigCacheAddFile(virConnectPtr co /* Get modified time */ if ((stat(filename, &st) < 0)) { - xenXMError (conn, VIR_ERR_INTERNAL_ERROR, - _("cannot stat %s: %s"), filename, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot stat: %s"), + filename); return -1; } @@ -441,7 +443,7 @@ xenXMConfigCacheAddFile(virConnectPtr co } else { /* Completely new entry */ newborn = 1; if (VIR_ALLOC(entry) < 0) { - xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno)); + virReportOOMError(conn); return -1; } memcpy(entry->filename, filename, PATH_MAX); @@ -495,7 +497,8 @@ int xenXMConfigCacheRefresh (virConnectP int ret = -1; if (now == ((time_t)-1)) { - xenXMError (conn, VIR_ERR_SYSTEM_ERROR, "%s", strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot get time of day")); return (-1); } @@ -507,7 +510,9 @@ int xenXMConfigCacheRefresh (virConnectP /* Process the files in the config dir */ if (!(dh = opendir(configDir))) { - xenXMError (conn, VIR_ERR_SYSTEM_ERROR, "%s", strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read directory %s"), + configDir); return (-1); } @@ -1289,7 +1294,7 @@ xenXMDomainConfigParse(virConnectPtr con return def; no_memory: - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); /* fallthrough */ cleanup: virDomainGraphicsDefFree(graphics); @@ -1523,14 +1528,14 @@ int xenXMDomainPinVcpu(virDomainPtr doma } if (virBufferError(&mapbuf)) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, "%s", _("allocate buffer")); + virReportOOMError(domain->conn); return -1; } mapstr = virBufferContentAndReset(&mapbuf); if (VIR_ALLOC_N(cpuset, maxcpu) < 0) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, "%s", _("allocate buffer")); + virReportOOMError(domain->conn); goto cleanup; } if (virDomainCpuSetParse(domain->conn, @@ -1773,12 +1778,12 @@ static int xenXMDomainConfigFormatDisk(v virBufferAddLit(&buf, ",w"); if (virBufferError(&buf)) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } if (VIR_ALLOC(val) < 0) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); goto cleanup; } @@ -1844,7 +1849,7 @@ static int xenXMDomainConfigFormatNet(vi net->model); if (VIR_ALLOC(val) < 0) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); goto cleanup; } @@ -2245,7 +2250,7 @@ virConfPtr xenXMDomainConfigFormat(virCo return conf; no_memory: - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); cleanup: virConfFreeValue(diskVal); @@ -2339,7 +2344,7 @@ virDomainPtr xenXMDomainDefineXML(virCon goto error; if (VIR_ALLOC(entry) < 0) { - xenXMError(conn, VIR_ERR_NO_MEMORY, "%s", _("config")); + virReportOOMError(conn); goto error; } @@ -2535,7 +2540,7 @@ xenXMDomainAttachDevice(virDomainPtr dom case VIR_DOMAIN_DEVICE_DISK: { if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(domain->conn); goto cleanup; } def->disks[def->ndisks++] = dev->data.disk; @@ -2548,7 +2553,7 @@ xenXMDomainAttachDevice(virDomainPtr dom case VIR_DOMAIN_DEVICE_NET: { if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(domain->conn); goto cleanup; } def->nets[def->nnets++] = dev->data.net; @@ -2706,15 +2711,15 @@ int xenXMDomainGetAutostart(virDomainPtr int ret = -1; if (!linkname || !config) { - xenXMError(dom->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(dom->conn); goto cleanup; } *autostart = virFileLinkPointsTo(linkname, config); if (*autostart < 0) { - xenXMError(dom->conn, VIR_ERR_INTERNAL_ERROR, - _("failed to check autostart link %s: %s"), - linkname, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("cannot check link %s points to config %s"), + linkname, config); goto cleanup; } @@ -2734,24 +2739,24 @@ int xenXMDomainSetAutostart(virDomainPtr int ret = -1; if (!linkname || !config) { - xenXMError(dom->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(dom->conn); goto cleanup; } if (autostart) { if (symlink(config, linkname) < 0 && errno != EEXIST) { - xenXMError(dom->conn, VIR_ERR_INTERNAL_ERROR, - _("failed to create link %s: %s"), - linkname, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("failed to create link %s to %s"), + config, linkname); goto cleanup; } } else { if (unlink(linkname) < 0 && errno != ENOENT) { - xenXMError(dom->conn, VIR_ERR_INTERNAL_ERROR, - _("failed to remove link %s: %s"), - linkname, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("failed to remove link %s"), + linkname); goto cleanup; } } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:42:41PM +0000, Daniel P. Berrange wrote:
The strerror() method is not guarenteed to be re-entrant, which is rather a pain because the strerror_r() method is rather unpleasant to use. In addition our code is quite inconsistent about using VIR_ERR_SYSTEM_ERROR vs VIR_ERR_INTERNAL_ERROR for problems which have an 'errno' avilable. Likewise we're not very consistent about OOM reporting error codes
This patch thus introduces two convenient functions for reporting a system error and OOM error.
virReportSystemError(conn, theerrno, fmt,...) virReportOOMError(conn)
Yes, a very welcome change. I've looked through the patch briefly and it looks good, and hoping gcc would pick up any gross argument mismatches. So +1.
diff --git a/autobuild.sh b/autobuild.sh --- a/autobuild.sh +++ b/autobuild.sh @@ -65,6 +65,7 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; --build=$(uname -m)-pc-linux \ --host=i686-pc-mingw32 \ --prefix="$AUTOBUILD_INSTALL_ROOT/i686-pc-mingw32/sys-root/mingw" \ + --enable-compile-warnings=error \ --without-sasl \ --without-avahi \ --without-polkit \
Intended? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Thu, Jan 15, 2009 at 05:16:34PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:42:41PM +0000, Daniel P. Berrange wrote:
The strerror() method is not guarenteed to be re-entrant, which is rather a pain because the strerror_r() method is rather unpleasant to use. In addition our code is quite inconsistent about using VIR_ERR_SYSTEM_ERROR vs VIR_ERR_INTERNAL_ERROR for problems which have an 'errno' avilable. Likewise we're not very consistent about OOM reporting error codes
This patch thus introduces two convenient functions for reporting a system error and OOM error.
virReportSystemError(conn, theerrno, fmt,...) virReportOOMError(conn)
Yes, a very welcome change. I've looked through the patch briefly and it looks good, and hoping gcc would pick up any gross argument mismatches. So +1.
diff --git a/autobuild.sh b/autobuild.sh --- a/autobuild.sh +++ b/autobuild.sh @@ -65,6 +65,7 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; --build=$(uname -m)-pc-linux \ --host=i686-pc-mingw32 \ --prefix="$AUTOBUILD_INSTALL_ROOT/i686-pc-mingw32/sys-root/mingw" \ + --enable-compile-warnings=error \ --without-sasl \ --without-avahi \ --without-polkit \
Intended?
Yes, I noticed the MinGW build is now completely free of warnings so figured I'd add that to stop us ever re-introducing warnings on MinGW :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 15, 2009 at 05:23:46PM +0000, Daniel P. Berrange wrote:
Yes, I noticed the MinGW build is now completely free of warnings so figured I'd add that to stop us ever re-introducing warnings on MinGW :-)
Cool, +1! Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The strerror() method is not guarenteed to be re-entrant, which is rather a pain because the strerror_r() method is rather unpleasant ...
Good work. A couple of small problems and a question: ...
@@ -614,8 +618,9 @@ int main(int argc, char *argv[])
if (pid > 0) { if ((rc = virFileWritePid(LXC_STATE_DIR, name, pid)) != 0) { - fprintf(stderr, _("Unable to write pid file: %s\n"), - strerror(rc)); + virReportSystemError(NULL, errno, + _("Unable to write pid file '%s/%s.pid'"), + LXC_STATE_DIR, name); _exit(1); }
Looks like that should be "rc", not "errno". ...
@@ -471,9 +474,9 @@ networkAddMasqueradingIptablesRules(virC network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow forwarding to '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow forwarding to '%s'"), + network->def->bridge); goto masqerr2; }
There's a stray trailing newline, above. Obviously it was there before your changes, too. ...
@@ -3455,23 +3452,23 @@ static int qemudDomainSetAutostart(virDo int err;
if ((err = virFileMakePath(driver->autostartDir))) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(dom->conn, errno, + _("cannot create autostart directory %s"), + driver->autostartDir);
I think you want to retain the use of "err", above, since virFileMakePath maps a missing / to EINVAL.
diff --git a/src/remote_internal.c b/src/remote_internal.c diff --git a/src/storage_conf.c b/src/storage_conf.c --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -43,6 +43,8 @@ #include "util.h" #include "memory.h"
+#define VIR_FROM_THIS VIR_FROM_STORAGE + /* Work around broken limits.h on debian etch */ #if defined __GNUC__ && defined _GCC_LIMITS_H_ && ! defined ULLONG_MAX # define ULLONG_MAX ULONG_LONG_MAX @@ -1405,9 +1407,9 @@ virStoragePoolObjSaveDef(virConnectPtr c char path[PATH_MAX];
if ((err = virFileMakePath(driver->configDir))) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - driver->configDir, strerror(err)); + virStorageReportError(conn, errno, + _("cannot create config directory %s"), + driver->configDir);
Same here. Incidentally, virFileMakePath will have to go, eventually. Not only is it recursive in the number of components of the name being created, but allocating PATH_MAX for each stack frame is very wasteful, and might make it easy to blow the stack/DoS -- if there's a way to make libvirtd call it with an abusive argument. Worst still, it creates directories with mkdir(path, 0777). No virFileMakePath caller is careful to chmod _all_ of the possibly-just-created directories, and even if they all were, there'd still be a race. ...
diff --git a/src/test.c b/src/test.c ... @@ -336,7 +339,7 @@ static int testOpenFromFile(virConnectPt virDomainObjPtr dom; testConnPtr privconn; if (VIR_ALLOC(privconn) < 0) { - testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn);
I don't remember the rules for NULL vs non-NULL conn. Is this s/NULL/conn/ transform valid? I think there's one more like it.

On Mon, Jan 19, 2009 at 11:40:56AM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The strerror() method is not guarenteed to be re-entrant, which is rather a pain because the strerror_r() method is rather unpleasant ...
Good work. A couple of small problems and a question:
...
@@ -614,8 +618,9 @@ int main(int argc, char *argv[])
if (pid > 0) { if ((rc = virFileWritePid(LXC_STATE_DIR, name, pid)) != 0) { - fprintf(stderr, _("Unable to write pid file: %s\n"), - strerror(rc)); + virReportSystemError(NULL, errno, + _("Unable to write pid file '%s/%s.pid'"), + LXC_STATE_DIR, name); _exit(1); }
Looks like that should be "rc", not "errno".
...
@@ -471,9 +474,9 @@ networkAddMasqueradingIptablesRules(virC network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow forwarding to '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow forwarding to '%s'"), + network->def->bridge); goto masqerr2; }
There's a stray trailing newline, above. Obviously it was there before your changes, too.
...
@@ -3455,23 +3452,23 @@ static int qemudDomainSetAutostart(virDo int err;
if ((err = virFileMakePath(driver->autostartDir))) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(dom->conn, errno, + _("cannot create autostart directory %s"), + driver->autostartDir);
I think you want to retain the use of "err", above, since virFileMakePath maps a missing / to EINVAL.
diff --git a/src/remote_internal.c b/src/remote_internal.c diff --git a/src/storage_conf.c b/src/storage_conf.c --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -43,6 +43,8 @@ #include "util.h" #include "memory.h"
+#define VIR_FROM_THIS VIR_FROM_STORAGE + /* Work around broken limits.h on debian etch */ #if defined __GNUC__ && defined _GCC_LIMITS_H_ && ! defined ULLONG_MAX # define ULLONG_MAX ULONG_LONG_MAX @@ -1405,9 +1407,9 @@ virStoragePoolObjSaveDef(virConnectPtr c char path[PATH_MAX];
if ((err = virFileMakePath(driver->configDir))) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - driver->configDir, strerror(err)); + virStorageReportError(conn, errno, + _("cannot create config directory %s"), + driver->configDir);
Same here.
Incidentally, virFileMakePath will have to go, eventually. Not only is it recursive in the number of components of the name being created, but allocating PATH_MAX for each stack frame is very wasteful, and might make it easy to blow the stack/DoS -- if there's a way to make libvirtd call it with an abusive argument. Worst still, it creates directories with mkdir(path, 0777). No virFileMakePath caller is careful to chmod _all_ of the possibly-just-created directories, and even if they all were, there'd still be a race.
I'm not sure about the mkdir 0777 mode - it will be modulated by the process umask which will typically be 002 (regular user) or 022 (root). It may be worth checking the callers of this to see if this will be a problem or not. As for the stack/recursion issue, the impl is fairly naive. It can be trivially re-written to do one single strdup() of the incoming path, and then implement the whole thing using iteration instead of recursion. Finally, the malicious user supplied data issue - reasonably sure we only use directories based off getpwuid / /etc/libvirt/libvirtd.conf and don't allow user to supply arbitrary paths, but again worth checking the callers.
diff --git a/src/test.c b/src/test.c ... @@ -336,7 +339,7 @@ static int testOpenFromFile(virConnectPt virDomainObjPtr dom; testConnPtr privconn; if (VIR_ALLOC(privconn) < 0) { - testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn);
I don't remember the rules for NULL vs non-NULL conn. Is this s/NULL/conn/ transform valid? I think there's one more like it.
Here is an update with the fixes you mention in this mail, and re-diffed based on latest CVS. Daniel diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -1,4 +1,5 @@ ^src/util\.c$ ^src/xend_internal\.c$ ^src/util-lib\.c$ +^qemud/qemud.c$ ^gnulib/ diff --git a/autobuild.sh b/autobuild.sh --- a/autobuild.sh +++ b/autobuild.sh @@ -65,6 +65,7 @@ if [ -x /usr/bin/i686-pc-mingw32-gcc ]; --build=$(uname -m)-pc-linux \ --host=i686-pc-mingw32 \ --prefix="$AUTOBUILD_INSTALL_ROOT/i686-pc-mingw32/sys-root/mingw" \ + --enable-compile-warnings=error \ --without-sasl \ --without-avahi \ --without-polkit \ diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -74,6 +74,9 @@ AC_SYS_LARGEFILE dnl Availability of various common functions (non-fatal if missing). AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity getuid getgid]) +dnl Availability of various not common threadsafe functions +AC_CHECK_FUNCS([strerror_r]) + dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h sys/poll.h syslog.h]) diff --git a/po/POTFILES.in b/po/POTFILES.in --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -38,6 +38,7 @@ src/virsh.c src/virterror.c src/xen_inotify.c src/xen_internal.c +src/xen_unified.c src/xend_internal.c src/xm_internal.c src/xml.c diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -40,6 +40,8 @@ #include "buf.h" #include "c-ctype.h" +#define VIR_FROM_THIS VIR_FROM_DOMAIN + VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "qemu", "kqemu", @@ -1913,8 +1915,7 @@ static virDomainDefPtr virDomainDefParse int err; if ((err = virUUIDGenerate(def->uuid))) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to generate UUID: %s"), - strerror(err)); + "%s", _("Failed to generate UUID")); goto error; } } else { @@ -3404,33 +3405,33 @@ int virDomainSaveXML(virConnectPtr conn, goto cleanup; if ((err = virFileMakePath(configDir))) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - configDir, strerror(err)); + virReportSystemError(conn, errno, + _("cannot create config directory '%s'"), + configDir); goto cleanup; } if ((fd = open(configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config file %s: %s"), - configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create config file '%s'"), + configFile); goto cleanup; } towrite = strlen(xml); if (safewrite(fd, xml, towrite) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot write config file %s: %s"), - configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot write config file '%s'"), + configFile); goto cleanup; } if (close(fd) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot save config file %s: %s"), - configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot save config file '%s'"), + configFile); goto cleanup; } @@ -3529,9 +3530,9 @@ int virDomainLoadAllConfigs(virConnectPt if (!(dir = opendir(configDir))) { if (errno == ENOENT) return 0; - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to open dir '%s': %s"), - configDir, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to open dir '%s'"), + configDir); return -1; } @@ -3583,9 +3584,9 @@ int virDomainDeleteConfig(virConnectPtr if (unlink(configFile) < 0 && errno != ENOENT) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot remove config for %s: %s"), - dom->def->name, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot remove config %s"), + configFile); goto cleanup; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -321,6 +321,8 @@ virUUIDParse; virReportErrorHelper; virErrorMsg; virRaiseError; +virReportSystemErrorFull; +virReportOOMErrorFull; # xml.h diff --git a/src/lxc_container.c b/src/lxc_container.c --- a/src/lxc_container.c +++ b/src/lxc_container.c @@ -48,6 +48,8 @@ #include "memory.h" #include "veth.h" +#define VIR_FROM_THIS VIR_FROM_LXC + /* * GLibc headers are behind the kernel, so we define these * constants if they're not present already. @@ -118,14 +120,14 @@ static int lxcContainerSetStdio(int cont int open_max, i; if (setsid() < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("setsid failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("setsid failed")); goto cleanup; } if (ioctl(ttyfd, TIOCSCTTY, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("ioctl(TIOCSTTY) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("ioctl(TIOCSTTY) failed")); goto cleanup; } @@ -137,20 +139,20 @@ static int lxcContainerSetStdio(int cont close(i); if (dup2(ttyfd, 0) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("dup2(stdin) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("dup2(stdin) failed")); goto cleanup; } if (dup2(ttyfd, 1) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("dup2(stdout) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("dup2(stdout) failed")); goto cleanup; } if (dup2(ttyfd, 2) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("dup2(stderr) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("dup2(stderr) failed")); goto cleanup; } @@ -177,9 +179,8 @@ int lxcContainerSendContinue(int control writeCount = safewrite(control, &msg, sizeof(msg)); if (writeCount != sizeof(msg)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("unable to send container continue message: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("unable to send container continue message")); goto error_out; } @@ -207,9 +208,8 @@ static int lxcContainerWaitForContinue(i readLen = saferead(control, &msg, sizeof(msg)); if (readLen != sizeof(msg) || msg != LXC_CONTINUE_MSG) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to read the container continue message: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Failed to read the container continue message")); return -1; } close(control); @@ -266,27 +266,28 @@ static int lxcContainerChildMountSort(co static int lxcContainerPivotRoot(virDomainFSDefPtr root) { + int rc; char *oldroot; /* First step is to ensure the new root itself is a mount point */ if (mount(root->src, root->src, NULL, MS_BIND, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to bind new root %s: %s"), - root->src, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to bind new root %s"), + root->src); return -1; } if (virAsprintf(&oldroot, "%s/.oldroot", root->src) < 0) { - lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(NULL); return -1; } - if (virFileMakePath(oldroot) < 0) { + if ((rc = virFileMakePath(oldroot)) < 0) { VIR_FREE(oldroot); - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create %s: %s"), - oldroot, strerror(errno)); + virReportSystemError(NULL, rc, + _("failed to create %s"), + oldroot); return -1; } @@ -294,9 +295,9 @@ static int lxcContainerPivotRoot(virDoma * this and will soon be unmounted completely */ if (pivot_root(root->src, oldroot) < 0) { VIR_FREE(oldroot); - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to pivot root %s to %s: %s"), - oldroot, root->src, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to pivot root %s to %s"), + oldroot, root->src); return -1; } VIR_FREE(oldroot); @@ -312,6 +313,7 @@ static int lxcContainerPivotRoot(virDoma static int lxcContainerPopulateDevices(void) { int i; + int rc; const struct { int maj; int min; @@ -326,11 +328,14 @@ static int lxcContainerPopulateDevices(v { LXC_DEV_MAJ_MEMORY, LXC_DEV_MIN_URANDOM, 0666, "/dev/urandom" }, }; - if (virFileMakePath("/dev") < 0 || - mount("none", "/dev", "tmpfs", 0, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount /dev tmpfs for container: %s"), - strerror(errno)); + if ((rc = virFileMakePath("/dev")) < 0) { + virReportSystemError(NULL, rc, "%s", + _("cannot create /dev/")); + return -1; + } + if (mount("none", "/dev", "tmpfs", 0, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("failed to mount /dev tmpfs")); return -1; } /* Move old devpts into container, since we have to @@ -339,12 +344,15 @@ static int lxcContainerPopulateDevices(v XXX This sucks, we need to figure out how to get our own private devpts for isolation */ - if (virFileMakePath("/dev/pts") < 0 || - mount("/.oldroot/dev/pts", "/dev/pts", NULL, + if ((rc = virFileMakePath("/dev/pts") < 0)) { + virReportSystemError(NULL, rc, "%s", + _("cannot create /dev/pts")); + return -1; + } + if (mount("/.oldroot/dev/pts", "/dev/pts", NULL, MS_MOVE, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to move /dev/pts into container: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to move /dev/pts into container")); return -1; } @@ -353,9 +361,9 @@ static int lxcContainerPopulateDevices(v dev_t dev = makedev(devs[i].maj, devs[i].min); if (mknod(devs[i].path, 0, dev) < 0 || chmod(devs[i].path, devs[i].mode)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to make device %s: %s"), - devs[i].path, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to make device %s"), + devs[i].path); return -1; } } @@ -382,12 +390,19 @@ static int lxcContainerMountNewFS(virDom return -1; } - if (virFileMakePath(vmDef->fss[i]->dst) < 0 || - mount(src, vmDef->fss[i]->dst, NULL, MS_BIND, NULL) < 0) { + if (virFileMakePath(vmDef->fss[i]->dst) < 0) { + virReportSystemError(NULL, errno, + _("failed to create %s"), + vmDef->fss[i]->dst); VIR_FREE(src); - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount %s at %s for container: %s"), - vmDef->fss[i]->src, vmDef->fss[i]->dst, strerror(errno)); + return -1; + } + if (mount(src, vmDef->fss[i]->dst, NULL, MS_BIND, NULL) < 0) { + VIR_FREE(src); + virReportSystemError(NULL, errno, + _("failed to mount %s at %s"), + vmDef->fss[i]->src, + vmDef->fss[i]->dst); return -1; } VIR_FREE(src); @@ -406,9 +421,8 @@ static int lxcContainerUnmountOldFS(void int i; if (!(procmnt = setmntent("/proc/mounts", "r"))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to read /proc/mounts: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to read /proc/mounts")); return -1; } while ((mntent = getmntent(procmnt)) != NULL) { @@ -433,9 +447,9 @@ static int lxcContainerUnmountOldFS(void for (i = 0 ; i < nmounts ; i++) { if (umount(mounts[i]) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to unmount %s: %s"), - mounts[i], strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to unmount '%s'"), + mounts[i]); return -1; } VIR_FREE(mounts[i]); @@ -458,9 +472,8 @@ static int lxcContainerSetupPivotRoot(vi if (virFileMakePath("/proc") < 0 || mount("none", "/proc", "proc", 0, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount /proc for container: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to mount /proc")); return -1; } @@ -492,18 +505,18 @@ static int lxcContainerSetupExtraMounts( NULL, MS_BIND, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount %s at %s for container: %s"), - vmDef->fss[i]->src, vmDef->fss[i]->dst, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to mount %s at %s"), + vmDef->fss[i]->src, + vmDef->fss[i]->dst); return -1; } } /* mount /proc */ if (mount("lxcproc", "/proc", "proc", 0, NULL) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to mount /proc for container: %s"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to mount /proc")); return -1; } @@ -557,8 +570,9 @@ static int lxcContainerChild( void *data ttyfd = open(argv->ttyPath, O_RDWR|O_NOCTTY); if (ttyfd < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("open(%s) failed: %s"), argv->ttyPath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to open %s"), + argv->ttyPath); return -1; } @@ -618,8 +632,8 @@ int lxcContainerStart(virDomainDefPtr de DEBUG("clone() returned, %d", pid); if (pid < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("clone() failed, %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to run clone container")); return -1; } diff --git a/src/lxc_controller.c b/src/lxc_controller.c --- a/src/lxc_controller.c +++ b/src/lxc_controller.c @@ -45,6 +45,8 @@ #include "util.h" #include "cgroup.h" +#define VIR_FROM_THIS VIR_FROM_LXC + struct cgroup_device_policy { char type; int major; @@ -109,8 +111,8 @@ static int lxcSetContainerResources(virD rc = virCgroupAddTask(cgroup, getpid()); out: if (rc != 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to set lxc resources: %s\n"), strerror(-rc)); + virReportSystemError(NULL, -rc, "%s", + _("Failed to set lxc resources")); virCgroupRemove(cgroup); } @@ -135,9 +137,9 @@ static int lxcMonitorServer(const char * struct sockaddr_un addr; if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create server socket %s: %s"), - sockpath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to create server socket '%s'"), + sockpath); goto error; } @@ -147,15 +149,15 @@ static int lxcMonitorServer(const char * strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to bind server socket %s: %s"), - sockpath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to bind server socket '%s'"), + sockpath); goto error; } if (listen(fd, 30 /* backlog */ ) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to listen server socket %s: %s"), - sockpath, strerror(errno)); + virReportSystemError(NULL, errno, + _("failed to listen server socket %s"), + sockpath); goto error; } @@ -187,14 +189,16 @@ static int lxcFdForward(int readFd, int goto cleanup; } - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("read of fd %d failed: %s"), readFd, strerror(errno)); + virReportSystemError(NULL, errno, + _("read of fd %d failed"), + readFd); goto cleanup; } if (1 != (safewrite(writeFd, buf, 1))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("write to fd %d failed: %s"), writeFd, strerror(errno)); + virReportSystemError(NULL, errno, + _("write to fd %d failed"), + writeFd); goto cleanup; } @@ -244,8 +248,8 @@ static int lxcControllerMain(int monitor /* create the epoll fild descriptor */ epollFd = epoll_create(2); if (0 > epollFd) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_create(2) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_create(2) failed")); goto cleanup; } @@ -254,30 +258,30 @@ static int lxcControllerMain(int monitor epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */ epollEvent.data.fd = appPty; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, appPty, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(appPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(appPty) failed")); goto cleanup; } epollEvent.data.fd = contPty; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, contPty, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } epollEvent.events = EPOLLIN; epollEvent.data.fd = monitor; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, monitor, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } epollEvent.events = EPOLLHUP; epollEvent.data.fd = client; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } @@ -296,14 +300,14 @@ static int lxcControllerMain(int monitor epollEvent.events = EPOLLHUP; epollEvent.data.fd = client; if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } } else if (client != -1 && epollEvent.data.fd == client) { if (0 > epoll_ctl(epollFd, EPOLL_CTL_DEL, client, &epollEvent)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_ctl(contPty) failed")); goto cleanup; } close(client); @@ -340,8 +344,8 @@ static int lxcControllerMain(int monitor } /* error */ - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("epoll_wait() failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("epoll_wait() failed")); goto cleanup; } @@ -438,16 +442,16 @@ lxcControllerRun(virDomainDefPtr def, pid_t container = -1; if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("sockpair failed: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("sockpair failed")); goto cleanup; } if (virFileOpenTty(&containerPty, &containerPtyPath, 0) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to allocate tty: %s"), strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("failed to allocate tty")); goto cleanup; } @@ -528,18 +532,18 @@ int main(int argc, char *argv[]) case 'n': if ((name = strdup(optarg)) == NULL) { - fprintf(stderr, "%s", strerror(errno)); + virReportOOMError(NULL); goto cleanup; } break; case 'v': if (VIR_REALLOC_N(veths, nveths+1) < 0) { - fprintf(stderr, "cannot allocate veths %s", strerror(errno)); + virReportOOMError(NULL); goto cleanup; } if ((veths[nveths++] = strdup(optarg)) == NULL) { - fprintf(stderr, "cannot allocate veth name %s", strerror(errno)); + virReportOOMError(NULL); goto cleanup; } break; @@ -614,8 +618,9 @@ int main(int argc, char *argv[]) if (pid > 0) { if ((rc = virFileWritePid(LXC_STATE_DIR, name, pid)) != 0) { - fprintf(stderr, _("Unable to write pid file: %s\n"), - strerror(rc)); + virReportSystemError(NULL, rc, + _("Unable to write pid file '%s/%s.pid'"), + LXC_STATE_DIR, name); _exit(1); } @@ -627,22 +632,22 @@ int main(int argc, char *argv[]) /* Don't hold onto any cwd we inherit from libvirtd either */ if (chdir("/") < 0) { - fprintf(stderr, _("Unable to change to root dir: %s\n"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Unable to change to root dir")); goto cleanup; } if (setsid() < 0) { - fprintf(stderr, _("Unable to become session leader: %s\n"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Unable to become session leader")); goto cleanup; } } /* Accept initial client which is the libvirtd daemon */ if ((client = accept(monitor, NULL, 0)) < 0) { - fprintf(stderr, _("Failed connection from LXC driver: %s\n"), - strerror(errno)); + virReportSystemError(NULL, errno, "%s", + _("Failed connection from LXC driver")); goto cleanup; } diff --git a/src/lxc_driver.c b/src/lxc_driver.c --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -49,6 +49,8 @@ #include "cgroup.h" +#define VIR_FROM_THIS VIR_FROM_LXC + static int lxcStartup(void); static int lxcShutdown(void); static lxc_driver_t *lxc_driver = NULL; @@ -467,9 +469,9 @@ static int lxcVMCleanup(virConnectPtr co ; /* empty */ if ((waitRc != vm->pid) && (errno != ECHILD)) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("waitpid failed to wait for container %d: %d %s"), - vm->pid, waitRc, strerror(errno)); + virReportSystemError(conn, errno, + _("waitpid failed to wait for container %d: %d"), + vm->pid, waitRc); } rc = 0; @@ -579,17 +581,15 @@ static int lxcSetupInterfaces(virConnect } if (0 != (rc = brAddInterface(brctl, bridge, parentVeth))) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add %s device to %s: %s"), - parentVeth, - bridge, - strerror(rc)); + virReportSystemError(conn, rc, + _("failed to add %s device to %s"), + parentVeth, bridge); goto error_exit; } if (0 != (rc = vethInterfaceUpOrDown(parentVeth, 1))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to enable parent ns veth device: %d"), rc); + virReportSystemError(conn, rc, "%s", + _("failed to enable parent ns veth device")); goto error_exit; } @@ -618,9 +618,8 @@ static int lxcMonitorClient(virConnectPt } if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create client socket: %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to create client socket")); goto error; } @@ -629,9 +628,8 @@ static int lxcMonitorClient(virConnectPt strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to connect to client socket: %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to connect to client socket")); goto error; } @@ -662,9 +660,9 @@ static int lxcVmTerminate(virConnectPtr if (kill(vm->pid, signum) < 0) { if (errno != ESRCH) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to kill pid %d: %s"), - vm->pid, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to kill pid %d"), + vm->pid); return -1; } } @@ -794,9 +792,9 @@ static int lxcControllerStart(virConnect */ while ((rc = waitpid(child, &status, 0) == -1) && errno == EINTR); if (rc == -1) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot wait for '%s': %s"), - largv[0], strerror(errno)); + virReportSystemError(conn, errno, + _("cannot wait for '%s'"), + largv[0]); goto cleanup; } @@ -848,10 +846,10 @@ static int lxcVmStart(virConnectPtr conn unsigned int nveths = 0; char **veths = NULL; - if (virFileMakePath(driver->logDir) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create log directory %s: %s"), - driver->logDir, strerror(rc)); + if ((rc = virFileMakePath(driver->logDir)) < 0) { + virReportSystemError(conn, rc, + _("cannot create log directory '%s'"), + driver->logDir); return -1; } @@ -863,9 +861,8 @@ static int lxcVmStart(virConnectPtr conn /* open parent tty */ if (virFileOpenTty(&parentTty, &parentTtyPath, 1) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to allocate tty: %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to allocate tty")); goto cleanup; } if (vm->def->console && @@ -887,9 +884,9 @@ static int lxcVmStart(virConnectPtr conn if ((logfd = open(logfile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR|S_IWUSR)) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to open %s: %s"), logfile, - strerror(errno)); + virReportSystemError(conn, errno, + _("failed to open '%s'"), + logfile); goto cleanup; } @@ -907,9 +904,9 @@ static int lxcVmStart(virConnectPtr conn /* And get its pid */ if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) != 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to read pid file %s/%s.pid: %s"), - driver->stateDir, vm->def->name, strerror(rc)); + virReportSystemError(conn, rc, + _("Failed to read pid file %s/%s.pid"), + driver->stateDir, vm->def->name); rc = -1; goto cleanup; } @@ -1272,11 +1269,7 @@ static int lxcVersion(virConnectPtr conn int min; int rev; - if (uname(&ver) != 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("uname(): %s"), strerror(errno)); - return -1; - } + uname(&ver); if (sscanf(ver.release, "%i.%i.%i", &maj, &min, &rev) != 3) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, diff --git a/src/network_conf.c b/src/network_conf.c --- a/src/network_conf.c +++ b/src/network_conf.c @@ -43,6 +43,8 @@ #include "buf.h" #include "c-ctype.h" +#define VIR_FROM_THIS VIR_FROM_NETWORK + VIR_ENUM_DECL(virNetworkForward) VIR_ENUM_IMPL(virNetworkForward, @@ -332,7 +334,7 @@ virNetworkDefParseXML(virConnectPtr conn int err; if ((err = virUUIDGenerate(def->uuid))) { virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to generate UUID: %s"), strerror(err)); + "%s", _("Failed to generate UUID")); goto error; } } else { @@ -667,40 +669,40 @@ int virNetworkSaveConfig(virConnectPtr c goto cleanup; if ((err = virFileMakePath(configDir))) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - configDir, strerror(err)); + virReportSystemError(conn, err, + _("cannot create config directory '%s'"), + configDir); goto cleanup; } if ((err = virFileMakePath(autostartDir))) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - autostartDir, strerror(err)); + virReportSystemError(conn, err, + _("cannot create autostart directory '%s'"), + autostartDir); goto cleanup; } if ((fd = open(net->configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config file %s: %s"), - net->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create config file '%s'"), + net->configFile); goto cleanup; } towrite = strlen(xml); if (safewrite(fd, xml, towrite) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot write config file %s: %s"), - net->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot write config file '%s'"), + net->configFile); goto cleanup; } if (close(fd) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot save config file %s: %s"), - net->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot save config file '%s'"), + net->configFile); goto cleanup; } @@ -777,9 +779,9 @@ int virNetworkLoadAllConfigs(virConnectP if (!(dir = opendir(configDir))) { if (errno == ENOENT) return 0; - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to open dir '%s': %s"), - configDir, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to open dir '%s'"), + configDir); return -1; } @@ -821,9 +823,9 @@ int virNetworkDeleteConfig(virConnectPtr unlink(net->autostartLink); if (unlink(net->configFile) < 0) { - virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot remove config for %s: %s"), - net->def->name, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot remove config file '%s'"), + net->configFile); return -1; } diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -57,6 +57,9 @@ #include "iptables.h" #include "bridge.h" + +#define VIR_FROM_THIS VIR_FROM_NETWORK + /* Main driver state */ struct network_driver { virMutex lock; @@ -460,9 +463,9 @@ networkAddMasqueradingIptablesRules(virC network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow forwarding from '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow forwarding from '%s'"), + network->def->bridge); goto masqerr1; } @@ -471,9 +474,9 @@ networkAddMasqueradingIptablesRules(virC network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow forwarding to '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow forwarding to '%s'"), + network->def->bridge); goto masqerr2; } @@ -481,9 +484,9 @@ networkAddMasqueradingIptablesRules(virC if ((err = iptablesAddForwardMasquerade(driver->iptables, network->def->network, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to enable masquerading : %s\n"), - strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to enable masquerading to '%s'\n"), + network->def->forwardDev ? network->def->forwardDev : NULL); goto masqerr3; } @@ -513,9 +516,9 @@ networkAddRoutingIptablesRules(virConnec network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow routing from '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow routing from '%s'"), + network->def->bridge); goto routeerr1; } @@ -524,9 +527,9 @@ networkAddRoutingIptablesRules(virConnec network->def->network, network->def->bridge, network->def->forwardDev))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow routing to '%s' : %s\n"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow routing to '%s'"), + network->def->bridge); goto routeerr2; } @@ -557,31 +560,31 @@ networkAddIptablesRules(virConnectPtr co /* allow DHCP requests through to dnsmasq */ if ((err = iptablesAddTcpInput(driver->iptables, network->def->bridge, 67))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DHCP requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DHCP requests from '%s'"), + network->def->bridge); goto err1; } if ((err = iptablesAddUdpInput(driver->iptables, network->def->bridge, 67))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DHCP requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DHCP requests from '%s'"), + network->def->bridge); goto err2; } /* allow DNS requests through to dnsmasq */ if ((err = iptablesAddTcpInput(driver->iptables, network->def->bridge, 53))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DNS requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DNS requests from '%s'"), + network->def->bridge); goto err3; } if ((err = iptablesAddUdpInput(driver->iptables, network->def->bridge, 53))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow DNS requests from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow DNS requests from '%s'"), + network->def->bridge); goto err4; } @@ -589,24 +592,24 @@ networkAddIptablesRules(virConnectPtr co /* Catch all rules to block forwarding to/from bridges */ if ((err = iptablesAddForwardRejectOut(driver->iptables, network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to block outbound traffic from '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to block outbound traffic from '%s'"), + network->def->bridge); goto err5; } if ((err = iptablesAddForwardRejectIn(driver->iptables, network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to block inbound traffic to '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to block inbound traffic to '%s'"), + network->def->bridge); goto err6; } /* Allow traffic between guests on the same bridge */ if ((err = iptablesAddForwardAllowCross(driver->iptables, network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to add iptables rule to allow cross bridge traffic on '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to add iptables rule to allow cross bridge traffic on '%s'"), + network->def->bridge); goto err7; } @@ -711,15 +714,15 @@ static int networkStartNetworkDaemon(vir } if (!driver->brctl && (err = brInit(&driver->brctl))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot initialize bridge support: %s"), strerror(err)); + virReportSystemError(conn, err, "%s", + _("cannot initialize bridge support")); return -1; } if ((err = brAddBridge(driver->brctl, &network->def->bridge))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create bridge '%s' : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("cannot create bridge '%s'"), + network->def->bridge); return -1; } @@ -732,25 +735,25 @@ static int networkStartNetworkDaemon(vir if (network->def->ipAddress && (err = brSetInetAddress(driver->brctl, network->def->bridge, network->def->ipAddress))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot set IP address on bridge '%s' to '%s' : %s"), - network->def->bridge, network->def->ipAddress, strerror(err)); + virReportSystemError(conn, err, + _("cannot set IP address on bridge '%s' to '%s'"), + network->def->bridge, network->def->ipAddress); goto err_delbr; } if (network->def->netmask && (err = brSetInetNetmask(driver->brctl, network->def->bridge, network->def->netmask))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot set netmask on bridge '%s' to '%s' : %s"), - network->def->bridge, network->def->netmask, strerror(err)); + virReportSystemError(conn, err, + _("cannot set netmask on bridge '%s' to '%s'"), + network->def->bridge, network->def->netmask); goto err_delbr; } if (network->def->ipAddress && (err = brSetInterfaceUp(driver->brctl, network->def->bridge, 1))) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to bring the bridge '%s' up : %s"), - network->def->bridge, strerror(err)); + virReportSystemError(conn, err, + _("failed to bring the bridge '%s' up"), + network->def->bridge); goto err_delbr; } @@ -759,8 +762,8 @@ static int networkStartNetworkDaemon(vir if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE && !networkEnableIpForwarding()) { - networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to enable IP forwarding : %s"), strerror(err)); + virReportSystemError(conn, errno, "%s", + _("failed to enable IP forwarding")); goto err_delbr2; } @@ -1249,23 +1252,23 @@ static int networkSetAutostart(virNetwor int err; if ((err = virFileMakePath(driver->networkAutostartDir))) { - networkReportError(net->conn, NULL, net, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->networkAutostartDir, strerror(err)); + virReportSystemError(net->conn, errno, + _("cannot create autostart directory '%s'"), + driver->networkAutostartDir); goto cleanup; } if (symlink(network->configFile, network->autostartLink) < 0) { - networkReportError(net->conn, NULL, net, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s' to '%s': %s"), - network->autostartLink, network->configFile, strerror(errno)); + virReportSystemError(net->conn, errno, + _("Failed to create symlink '%s' to '%s'"), + network->autostartLink, network->configFile); goto cleanup; } } else { if (unlink(network->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - networkReportError(net->conn, NULL, net, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - network->autostartLink, strerror(errno)); + virReportSystemError(net->conn, errno, + _("Failed to delete symlink '%s'"), + network->autostartLink); goto cleanup; } } diff --git a/src/nodeinfo.c b/src/nodeinfo.c --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -45,6 +45,9 @@ #include "util.h" #include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + #ifdef __linux__ #define CPUINFO_PATH "/proc/cpuinfo" @@ -135,12 +138,8 @@ int virNodeInfoPopulate(virConnectPtr co #ifdef HAVE_UNAME struct utsname info; - if (uname(&info) < 0) { - virRaiseError(conn, NULL, NULL, 0, VIR_ERR_INTERNAL_ERROR, - VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - "cannot extract machine type %s", strerror(errno)); - return -1; - } + uname(&info); + strncpy(nodeinfo->model, info.machine, sizeof(nodeinfo->model)-1); nodeinfo->model[sizeof(nodeinfo->model)-1] = '\0'; @@ -155,9 +154,8 @@ int virNodeInfoPopulate(virConnectPtr co int ret; FILE *cpuinfo = fopen(CPUINFO_PATH, "r"); if (!cpuinfo) { - virRaiseError(conn, NULL, NULL, 0, VIR_ERR_INTERNAL_ERROR, - VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - "cannot open %s %s", CPUINFO_PATH, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open %s"), CPUINFO_PATH); return -1; } ret = linuxNodeInfoCPUPopulate(conn, cpuinfo, nodeinfo); diff --git a/src/qemu_driver.c b/src/qemu_driver.c --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -69,6 +69,8 @@ #include "uuid.h" #include "domain_conf.h" +#define VIR_FROM_THIS VIR_FROM_QEMU + /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" @@ -153,9 +155,7 @@ qemudLogFD(virConnectPtr conn, const cha if ((ret = snprintf(logfile, sizeof(logfile), "%s/%s.log", logDir, name)) < 0 || ret >= sizeof(logfile)) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to build logfile name %s/%s.log"), - logDir, name); + virReportOOMError(conn); return -1; } @@ -165,15 +165,14 @@ qemudLogFD(virConnectPtr conn, const cha else logmode |= O_APPEND; if ((fd = open(logfile, logmode, S_IRUSR | S_IWUSR)) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create logfile %s: %s"), - logfile, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to create logfile %s"), + logfile); return -1; } if (qemudSetCloseExec(fd) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Unable to set VM logfile close-on-exec flag %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("Unable to set VM logfile close-on-exec flag")); close(fd); return -1; } @@ -527,9 +526,9 @@ qemudReadMonitorOutput(virConnectPtr con continue; if (errno != EAGAIN) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failure while reading %s startup output: %s"), - what, strerror(errno)); + virReportSystemError(conn, errno, + _("Failure while reading %s startup output"), + what); return -1; } @@ -540,9 +539,9 @@ qemudReadMonitorOutput(virConnectPtr con return -1; } else if (ret == -1) { if (errno != EINTR) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failure while reading %s startup output: %s"), - what, strerror(errno)); + virReportSystemError(conn, errno, + _("Failure while reading %s startup output"), + what); return -1; } } else { @@ -867,9 +866,8 @@ qemudInitCpus(virConnectPtr conn, for (i = 0 ; i < vm->nvcpupids ; i++) { if (sched_setaffinity(vm->vcpupids[i], sizeof(mask), &mask) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to set CPU affinity %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("failed to set CPU affinity")); return -1; } } @@ -964,9 +962,9 @@ static int qemudStartVMDaemon(virConnect } if (virFileMakePath(driver->logDir) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create log directory %s: %s"), - driver->logDir, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create log directory %s"), + driver->logDir); return -1; } @@ -984,10 +982,9 @@ static int qemudStartVMDaemon(virConnect * in a sub-process so its hard to feed back a useful error */ if (stat(emulator, &sb) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Cannot find QEMU binary %s: %s"), - emulator, - strerror(errno)); + virReportSystemError(conn, errno, + _("Cannot find QEMU binary %s"), + emulator); return -1; } @@ -2252,9 +2249,9 @@ static int qemudDomainSave(virDomainPtr } if (close(fd) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, - _("unable to save file %s %s"), - path, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("unable to save file %s"), + path); goto cleanup; } fd = -1; @@ -2415,8 +2412,8 @@ qemudDomainPinVcpu(virDomainPtr dom, if (vm->vcpupids != NULL) { if (sched_setaffinity(vm->vcpupids[vcpu], sizeof(mask), &mask) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, - _("cannot set affinity: %s"), strerror(errno)); + virReportSystemError(dom->conn, errno, "%s", + _("cannot set affinity")); goto cleanup; } } else { @@ -2484,8 +2481,8 @@ qemudDomainGetVcpus(virDomainPtr dom, CPU_ZERO(&mask); if (sched_getaffinity(vm->vcpupids[v], sizeof(mask), &mask) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, - _("cannot get affinity: %s"), strerror(errno)); + virReportSystemError(dom->conn, errno, "%s", + _("cannot get affinity")); goto cleanup; } @@ -3468,23 +3465,23 @@ static int qemudDomainSetAutostart(virDo int err; if ((err = virFileMakePath(driver->autostartDir))) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(dom->conn, err, + _("cannot create autostart directory %s"), + driver->autostartDir); goto cleanup; } if (symlink(configFile, autostartLink) < 0) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s to '%s': %s"), - autostartLink, configFile, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to create symlink '%s to '%s'"), + autostartLink, configFile); goto cleanup; } } else { if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - autostartLink, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to delete symlink '%s'"), + autostartLink); goto cleanup; } } diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -88,6 +88,8 @@ #include "util.h" #include "event.h" +#define VIR_FROM_THIS VIR_FROM_REMOTE + #ifdef WIN32 #define pipe(fds) _pipe(fds,4096, _O_BINARY) #endif @@ -586,9 +588,9 @@ doRemoteOpen (virConnectPtr conn, } freeaddrinfo (res); - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to connect to '%s': %s"), - priv->hostname, strerror (saved_errno)); + virReportSystemError(conn, saved_errno, + _("unable to connect to '%s'"), + priv->hostname); goto failed; tcp_connected: @@ -607,9 +609,9 @@ doRemoteOpen (virConnectPtr conn, uid_t uid = getuid(); if (!(pw = getpwuid(uid))) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to lookup user '%d': %s"), - uid, strerror (errno)); + virReportSystemError(conn, errno, + _("unable to lookup user '%d'"), + uid); goto failed; } @@ -641,9 +643,8 @@ doRemoteOpen (virConnectPtr conn, autostart_retry: priv->sock = socket (AF_UNIX, SOCK_STREAM, 0); if (priv->sock == -1) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to create socket %s"), - strerror (errno)); + virReportSystemError(conn, errno, "%s", + _("unable to create socket")); goto failed; } if (connect (priv->sock, (struct sockaddr *) &addr, sizeof addr) == -1) { @@ -665,9 +666,9 @@ doRemoteOpen (virConnectPtr conn, goto autostart_retry; } } - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to connect to '%s': %s"), - sockname, strerror (errno)); + virReportSystemError(conn, errno, + _("unable to connect to '%s'"), + sockname); goto failed; } @@ -725,9 +726,8 @@ doRemoteOpen (virConnectPtr conn, * to faff around with two file descriptors (a la 'pipe(2)'). */ if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to create socket pair %s"), - strerror (errno)); + virReportSystemError(conn, errno, "%s", + _("unable to create socket pair")); goto failed; } @@ -754,16 +754,14 @@ doRemoteOpen (virConnectPtr conn, } /* switch (transport) */ if (virSetNonBlock(priv->sock) < 0) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to make socket non-blocking %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("unable to make socket non-blocking")); goto failed; } if (pipe(wakeupFD) < 0) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("unable to make pipe %s"), - strerror(errno)); + virReportSystemError(conn, errno, "%s", + _("unable to make pipe")); goto failed; } priv->wakeupReadFD = wakeupFD[0]; @@ -1004,9 +1002,9 @@ check_cert_file (virConnectPtr conn, con { struct stat sb; if (stat(file, &sb) < 0) { - errorf(conn, VIR_ERR_RPC, - _("Cannot access %s '%s': %s (%d)"), - type, file, strerror(errno), errno); + virReportSystemError(conn, errno, + _("Cannot access %s '%s'"), + type, file); return -1; } return 0; @@ -1193,9 +1191,8 @@ verify_certificate (virConnectPtr conn A } if ((now = time(NULL)) == ((time_t)-1)) { - errorf (conn, VIR_ERR_SYSTEM_ERROR, - _("cannot get current time: %s"), - strerror (errno)); + virReportSystemError(conn, errno, "%s", + _("cannot get current time")); return -1; } @@ -5180,10 +5177,8 @@ remoteAuthSASL (virConnectPtr conn, stru /* Get local address in form IPADDR:PORT */ salen = sizeof(sa); if (getsockname(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { - virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("failed to get sock address %d (%s)"), - errno, strerror(errno)); + virReportSystemError(in_open ? NULL : conn, errno, "%s", + _("failed to get sock address")); goto cleanup; } if ((localAddr = addrToString(&sa, salen)) == NULL) @@ -5192,10 +5187,8 @@ remoteAuthSASL (virConnectPtr conn, stru /* Get remote address in form IPADDR:PORT */ salen = sizeof(sa); if (getpeername(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { - virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, - VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, - _("failed to get peer address %d (%s)"), - errno, strerror(errno)); + virReportSystemError(in_open ? NULL : conn, errno, "%s", + _("failed to get peer address")); goto cleanup; } if ((remoteAddr = addrToString(&sa, salen)) == NULL) @@ -5721,8 +5714,8 @@ processCallWrite(virConnectPtr conn, if (errno == EWOULDBLOCK) return 0; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); + virReportSystemError(in_open ? NULL : conn, errno, + "%s", _("cannot send data")); return -1; } @@ -5771,10 +5764,8 @@ processCallRead(virConnectPtr conn, if (errno == EWOULDBLOCK) return 0; - errorf (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, - _("failed to read from socket %s"), - strerror (errno)); + virReportSystemError(in_open ? NULL : conn, errno, + "%s", _("cannot recv data")); } else { errorf (in_open ? NULL : conn, VIR_ERR_SYSTEM_ERROR, diff --git a/src/storage_backend.c b/src/storage_backend.c --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -62,6 +62,8 @@ #endif +#define VIR_FROM_THIS VIR_FROM_STORAGE + static virStorageBackendPtr backends[] = { #if WITH_STORAGE_DIR &virStorageBackendDirectory, @@ -104,9 +106,9 @@ virStorageBackendUpdateVolInfo(virConnec int ret, fd; if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open volume '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open volume '%s'"), + vol->target.path); return -1; } @@ -163,9 +165,9 @@ virStorageBackendUpdateVolInfoFD(virConn #endif if (fstat(fd, &sb) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot stat file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot stat file '%s'"), + vol->target.path); return -1; } @@ -195,9 +197,9 @@ virStorageBackendUpdateVolInfoFD(virConn */ end = lseek(fd, 0, SEEK_END); if (end == (off_t)-1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot seek to end of file '%s':%s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot seek to end of file '%s'"), + vol->target.path); return -1; } vol->allocation = end; @@ -215,16 +217,16 @@ virStorageBackendUpdateVolInfoFD(virConn start = lseek(fd, 0, SEEK_SET); if (start < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot seek to beginning of file '%s':%s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot seek to beginning of file '%s'"), + vol->target.path); return -1; } bytes = saferead(fd, buffer, sizeof(buffer)); if (bytes < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read beginning of file '%s':%s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read beginning of file '%s'"), + vol->target.path); return -1; } @@ -248,9 +250,9 @@ virStorageBackendUpdateVolInfoFD(virConn #if HAVE_SELINUX if (fgetfilecon(fd, &filecon) == -1) { if (errno != ENODATA && errno != ENOTSUP) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot get file context of %s: %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot get file context of '%s'"), + vol->target.path); return -1; } else { vol->target.perms.label = NULL; @@ -258,7 +260,7 @@ virStorageBackendUpdateVolInfoFD(virConn } else { vol->target.perms.label = strdup(filecon); if (vol->target.perms.label == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("context")); + virReportOOMError(conn); return -1; } freecon(filecon); @@ -341,10 +343,9 @@ virStorageBackendStablePath(virConnectPt usleep(100 * 1000); goto reopen; } - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read dir %s: %s"), - pool->def->target.path, - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read dir '%s'"), + pool->def->target.path); return NULL; } @@ -359,7 +360,7 @@ virStorageBackendStablePath(virConnectPt if (VIR_ALLOC_N(stablepath, strlen(pool->def->target.path) + 1 + strlen(dent->d_name) + 1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("path")); + virReportOOMError(conn); closedir(dh); return NULL; } @@ -386,7 +387,7 @@ virStorageBackendStablePath(virConnectPt stablepath = strdup(devpath); if (stablepath == NULL) - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("dup path")); + virReportOOMError(conn); return stablepath; } @@ -423,7 +424,7 @@ virStorageBackendRunProgRegex(virConnect /* Compile all regular expressions */ if (VIR_ALLOC_N(reg, nregex) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("regex")); + virReportOOMError(conn); return -1; } @@ -448,13 +449,11 @@ virStorageBackendRunProgRegex(virConnect /* Storage for matched variables */ if (VIR_ALLOC_N(groups, totgroups) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("regex groups")); + virReportOOMError(conn); goto cleanup; } if (VIR_ALLOC_N(vars, maxvars+1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("regex groups")); + virReportOOMError(conn); goto cleanup; } @@ -490,8 +489,7 @@ virStorageBackendRunProgRegex(virConnect line[vars[j+1].rm_eo] = '\0'; if ((groups[ngroup++] = strdup(line + vars[j+1].rm_so)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("regex groups")); + virReportOOMError(conn); goto cleanup; } } @@ -538,9 +536,9 @@ virStorageBackendRunProgRegex(virConnect return -1; if (err == -1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to wait for command: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("failed to wait for command '%s'"), + prog[0]); return -1; } else { if (WIFEXITED(exitstatus)) { @@ -588,8 +586,7 @@ virStorageBackendRunProgNul(virConnectPt return -1; if (VIR_ALLOC_N(v, n_columns) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("n_columns too large")); + virReportOOMError(conn); return -1; } for (i = 0; i < n_columns; i++) @@ -636,8 +633,8 @@ virStorageBackendRunProgNul(virConnectPt if (feof (fp)) err = 0; else - virStorageReportError (conn, VIR_ERR_INTERNAL_ERROR, - _("read error: %s"), strerror (errno)); + virReportSystemError(conn, errno, + _("read error on pipe to '%s'"), prog[0]); cleanup: for (i = 0; i < n_columns; i++) @@ -657,9 +654,9 @@ virStorageBackendRunProgNul(virConnectPt return -1; if (w_err == -1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to wait for command: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("failed to wait for command '%s'"), + prog[0]); return -1; } else { if (WIFEXITED(exitstatus)) { diff --git a/src/storage_backend_disk.c b/src/storage_backend_disk.c --- a/src/storage_backend_disk.c +++ b/src/storage_backend_disk.c @@ -32,6 +32,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + #define PARTHELPER BINDIR "/libvirt_parthelper" static int @@ -44,13 +46,13 @@ virStorageBackendDiskMakeDataVol(virConn if (vol == NULL) { if (VIR_ALLOC(vol) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); virStorageVolDefFree(vol); return -1; } @@ -61,14 +63,14 @@ virStorageBackendDiskMakeDataVol(virConn */ tmp = strrchr(groups[0], '/'); if ((vol->name = strdup(tmp ? tmp + 1 : groups[0])) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } } if (vol->target.path == NULL) { if ((devpath = strdup(groups[0])) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } @@ -89,15 +91,14 @@ virStorageBackendDiskMakeDataVol(virConn if (vol->key == NULL) { /* XXX base off a unique key of the underlying disk */ if ((vol->key = strdup(vol->target.path)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume")); + virReportOOMError(conn); return -1; } } if (vol->source.extents == NULL) { if (VIR_ALLOC(vol->source.extents) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("volume extents")); + virReportOOMError(conn); return -1; } vol->source.nextent = 1; @@ -118,7 +119,7 @@ virStorageBackendDiskMakeDataVol(virConn if ((vol->source.extents[0].path = strdup(pool->def->source.devices[0].path)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("extents")); + virReportOOMError(conn); return -1; } } @@ -367,9 +368,9 @@ virStorageBackendDiskDeleteVol(virConnec if ((n = readlink(vol->target.path, devpath, sizeof(devpath))) < 0 && errno != EINVAL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Couldn't read volume target path '%s'. %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("Couldn't read volume target path '%s'"), + vol->target.path); return -1; } else if (n <= 0) { strncpy(devpath, vol->target.path, PATH_MAX); diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -122,6 +122,7 @@ const struct FileTypeInfo const fileType */ }; +#define VIR_FROM_THIS VIR_FROM_STORAGE @@ -136,9 +137,9 @@ static int virStorageBackendProbeFile(vi int len, i, ret; if ((fd = open(def->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open volume '%s': %s"), - def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open volume '%s'"), + def->target.path); return -1; } @@ -148,9 +149,9 @@ static int virStorageBackendProbeFile(vi } if ((len = read(fd, head, sizeof(head))) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read header '%s': %s"), - def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read header '%s'"), + def->target.path); close(fd); return -1; } @@ -277,7 +278,7 @@ virStorageBackendFileSystemNetFindPoolSo } if (VIR_REALLOC_N(state->list.sources, state->list.nsources+1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } memset(state->list.sources + state->list.nsources, 0, sizeof(*state->list.sources)); @@ -335,7 +336,7 @@ virStorageBackendFileSystemNetFindPoolSo xpath_ctxt = xmlXPathNewContext(doc); if (xpath_ctxt == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xpath_ctxt")); + virReportOOMError(conn); goto cleanup; } @@ -354,7 +355,7 @@ virStorageBackendFileSystemNetFindPoolSo retval = virStoragePoolSourceListFormat(conn, &state.list); if (retval == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("retval")); + virReportOOMError(conn); goto cleanup; } @@ -387,9 +388,9 @@ virStorageBackendFileSystemIsMounted(vir struct mntent *ent; if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read %s: %s"), - _PATH_MOUNTED, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read mount list '%s'"), + _PATH_MOUNTED); return -1; } @@ -485,7 +486,7 @@ virStorageBackendFileSystemMount(virConn if (pool->def->type == VIR_STORAGE_POOL_NETFS) { if (VIR_ALLOC_N(src, strlen(pool->def->source.host.name) + 1 + strlen(pool->def->source.dir) + 1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("source")); + virReportOOMError(conn); return -1; } strcpy(src, pool->def->source.host.name); @@ -493,7 +494,7 @@ virStorageBackendFileSystemMount(virConn strcat(src, pool->def->source.dir); } else { if ((src = strdup(pool->def->source.devices[0].path)) == NULL) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("source")); + virReportOOMError(conn); return -1; } } @@ -600,10 +601,11 @@ virStorageBackendFileSystemBuild(virConn virStoragePoolObjPtr pool, unsigned int flags ATTRIBUTE_UNUSED) { - if (virFileMakePath(pool->def->target.path) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create path '%s': %s"), - pool->def->target.path, strerror(errno)); + int err; + if ((err = virFileMakePath(pool->def->target.path)) < 0) { + virReportSystemError(conn, err, + _("cannot create path '%s'"), + pool->def->target.path); return -1; } @@ -625,9 +627,9 @@ virStorageBackendFileSystemRefresh(virCo virStorageVolDefPtr vol = NULL; if (!(dir = opendir(pool->def->target.path))) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open path '%s': %s"), - pool->def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open path '%s'"), + pool->def->target.path); goto cleanup; } @@ -674,9 +676,9 @@ virStorageBackendFileSystemRefresh(virCo if (statvfs(pool->def->target.path, &sb) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot statvfs path '%s': %s"), - pool->def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot statvfs path '%s'"), + pool->def->target.path); return -1; } pool->def->capacity = ((unsigned long long)sb.f_frsize * @@ -688,7 +690,7 @@ virStorageBackendFileSystemRefresh(virCo return 0; no_memory: - virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); /* fallthrough */ cleanup: @@ -740,9 +742,9 @@ virStorageBackendFileSystemDelete(virCon /* XXX delete all vols first ? */ if (unlink(pool->def->target.path) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unlink path '%s': %s"), - pool->def->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot unlink path '%s'"), + pool->def->target.path); return -1; } @@ -764,7 +766,7 @@ virStorageBackendFileSystemVolCreate(vir if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1) < 0) { - virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("target")); + virReportOOMError(conn); return -1; } vol->type = VIR_STORAGE_VOL_FILE; @@ -773,17 +775,16 @@ virStorageBackendFileSystemVolCreate(vir strcat(vol->target.path, vol->name); vol->key = strdup(vol->target.path); if (vol->key == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("storage vol key")); + virReportOOMError(conn); return -1; } if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, vol->target.perms.mode)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); return -1; } @@ -798,9 +799,9 @@ virStorageBackendFileSystemVolCreate(vir if (bytes > remain) bytes = remain; if ((bytes = safewrite(fd, zeros, bytes)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot fill file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot fill file '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; @@ -811,25 +812,25 @@ virStorageBackendFileSystemVolCreate(vir /* Now seek to final size, possibly making the file sparse */ if (ftruncate(fd, vol->capacity) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot extend file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot extend file '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; } } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); return -1; } if ((fd = open(vol->target.path, O_RDWR)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); return -1; } } else { @@ -862,9 +863,9 @@ virStorageBackendFileSystemVolCreate(vir } if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); unlink(vol->target.path); return -1; } @@ -897,9 +898,9 @@ virStorageBackendFileSystemVolCreate(vir } if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); unlink(vol->target.path); return -1; } @@ -914,18 +915,18 @@ virStorageBackendFileSystemVolCreate(vir /* We can only chown/grp if root */ if (getuid() == 0) { if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file owner '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file owner '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; } } if (fchmod(fd, vol->target.perms.mode) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file mode '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file mode '%s'"), + vol->target.path); unlink(vol->target.path); close(fd); return -1; @@ -939,9 +940,9 @@ virStorageBackendFileSystemVolCreate(vir } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot close file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + vol->target.path); unlink(vol->target.path); return -1; } @@ -962,9 +963,9 @@ virStorageBackendFileSystemVolDelete(vir if (unlink(vol->target.path) < 0) { /* Silently ignore failures where the vol has already gone away */ if (errno != ENOENT) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unlink file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot unlink file '%s'"), + vol->target.path); return -1; } } diff --git a/src/storage_backend_iscsi.c b/src/storage_backend_iscsi.c --- a/src/storage_backend_iscsi.c +++ b/src/storage_backend_iscsi.c @@ -39,6 +39,9 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + + static int virStorageBackendISCSITargetIP(virConnectPtr conn, const char *hostname, @@ -204,9 +207,9 @@ virStorageBackendISCSINewLun(virConnectP usleep(100 * 1000); goto reopen; } - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open %s: %s"), - devpath, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open '%s'"), + devpath); goto cleanup; } @@ -322,9 +325,9 @@ virStorageBackendISCSIFindLUNs(virConnec sysdir = opendir(sysfs_path); if (sysdir == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to opendir sysfs path %s: %s"), - sysfs_path, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); return -1; } while ((sys_dirent = readdir(sysdir))) { @@ -354,10 +357,9 @@ virStorageBackendISCSIFindLUNs(virConnec n = scandir(sysfs_path, &namelist, notdotdir, versionsort); if (n <= 0) { /* we didn't find any reasonable entries; return failure */ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to find any LUNs for session %s: %s"), - session, strerror(errno)); - + virReportSystemError(conn, errno, + _("Failed to find any LUNs for session '%s'"), + session); return -1; } @@ -407,9 +409,9 @@ virStorageBackendISCSIFindLUNs(virConnec sysdir = opendir(sysfs_path); if (sysdir == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to opendir sysfs path %s: %s"), - sysfs_path, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); retval = -1; goto namelist_cleanup; } @@ -443,9 +445,9 @@ virStorageBackendISCSIFindLUNs(virConnec host, bus, target, lun); sysdir = opendir(sysfs_path); if (sysdir == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to opendir sysfs path %s: %s"), - sysfs_path, strerror(errno)); + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); retval = -1; goto namelist_cleanup; } diff --git a/src/storage_backend_logical.c b/src/storage_backend_logical.c --- a/src/storage_backend_logical.c +++ b/src/storage_backend_logical.c @@ -37,6 +37,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + #define PV_BLANK_SECTOR_SIZE 512 @@ -400,22 +402,22 @@ virStorageBackendLogicalBuildPool(virCon * rather than trying to figure out if we're a disk or partition */ if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open device %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open device '%s'"), + pool->def->source.devices[i].path); goto cleanup; } if (safewrite(fd, zeros, sizeof(zeros)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot clear device header %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot clear device header of '%s'"), + pool->def->source.devices[i].path); close(fd); goto cleanup; } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot close device %s"), - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot close device '%s'"), + pool->def->source.devices[i].path); goto cleanup; } @@ -538,10 +540,9 @@ virStorageBackendLogicalDeletePool(virCo pvargv[1] = pool->def->source.devices[i].path; if (virRun(conn, pvargv, NULL) < 0) { error = -1; - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot remove PV device %s: %s"), - pool->def->source.devices[i].path, - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot remove PV device '%s'"), + pool->def->source.devices[i].path); break; } } @@ -591,41 +592,41 @@ virStorageBackendLogicalCreateVol(virCon return -1; if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot read path '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); goto cleanup; } /* We can only chown/grp if root */ if (getuid() == 0) { if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file owner '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file owner '%s'"), + vol->target.path); goto cleanup; } } if (fchmod(fd, vol->target.perms.mode) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot set file mode '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot set file mode '%s'"), + vol->target.path); goto cleanup; } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot close file '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + vol->target.path); goto cleanup; } fd = -1; /* Fill in data about this new vol */ if (virStorageBackendLogicalFindLVs(conn, pool, vol) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot find newly created volume '%s': %s"), - vol->target.path, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot find newly created volume '%s'"), + vol->target.path); goto cleanup; } diff --git a/src/storage_conf.c b/src/storage_conf.c --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -43,6 +43,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + /* Work around broken limits.h on debian etch */ #if defined __GNUC__ && defined _GCC_LIMITS_H_ && ! defined ULLONG_MAX # define ULLONG_MAX ULONG_LONG_MAX @@ -1405,9 +1407,9 @@ virStoragePoolObjSaveDef(virConnectPtr c char path[PATH_MAX]; if ((err = virFileMakePath(driver->configDir))) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config directory %s: %s"), - driver->configDir, strerror(err)); + virStorageReportError(conn, err, + _("cannot create config directory %s"), + driver->configDir); return -1; } @@ -1448,24 +1450,24 @@ virStoragePoolObjSaveDef(virConnectPtr c if ((fd = open(pool->configFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR )) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create config file %s: %s"), - pool->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot create config file %s"), + pool->configFile); goto cleanup; } towrite = strlen(xml); if (safewrite(fd, xml, towrite) != towrite) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot write config file %s: %s"), - pool->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot write config file %s"), + pool->configFile); goto cleanup; } if (close(fd) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot save config file %s: %s"), - pool->configFile, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot save config file %s"), + pool->configFile); goto cleanup; } diff --git a/src/storage_driver.c b/src/storage_driver.c --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -41,6 +41,8 @@ #include "memory.h" #include "storage_backend.h" +#define VIR_FROM_THIS VIR_FROM_STORAGE + #define storageLog(msg...) fprintf(stderr, msg) static virStorageDriverStatePtr driverState; @@ -379,8 +381,7 @@ storageListPools(virConnectPtr conn, if (virStoragePoolObjIsActive(driver->pools.objs[i])) { if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) { virStoragePoolObjUnlock(driver->pools.objs[i]); - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("names")); + virReportOOMError(conn); goto cleanup; } got++; @@ -428,8 +429,7 @@ storageListDefinedPools(virConnectPtr co if (!virStoragePoolObjIsActive(driver->pools.objs[i])) { if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) { virStoragePoolObjUnlock(driver->pools.objs[i]); - virStorageReportError(conn, VIR_ERR_NO_MEMORY, - "%s", _("names")); + virReportOOMError(conn); goto cleanup; } got++; @@ -952,25 +952,24 @@ storagePoolSetAutostart(virStoragePoolPt int err; if ((err = virFileMakePath(driver->autostartDir))) { - virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(obj->conn, err, + _("cannot create autostart directory %s"), + driver->autostartDir); goto cleanup; } if (symlink(pool->configFile, pool->autostartLink) < 0) { - virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s' to '%s': %s"), - pool->autostartLink, pool->configFile, - strerror(errno)); + virReportSystemError(obj->conn, errno, + _("Failed to create symlink '%s' to '%s'"), + pool->autostartLink, pool->configFile); goto cleanup; } } else { if (unlink(pool->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - pool->autostartLink, strerror(errno)); + virReportSystemError(obj->conn, errno, + _("Failed to delete symlink '%s'"), + pool->autostartLink); goto cleanup; } } @@ -1042,8 +1041,7 @@ storagePoolListVolumes(virStoragePoolPtr for (i = 0 ; i < pool->volumes.count && n < maxnames ; i++) { if ((names[n++] = strdup(pool->volumes.objs[i]->name)) == NULL) { - virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, - "%s", _("name")); + virReportOOMError(obj->conn); goto cleanup; } } @@ -1224,7 +1222,7 @@ storageVolumeCreateXML(virStoragePoolPtr if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { - virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(obj->conn); goto cleanup; } @@ -1453,7 +1451,7 @@ storageVolumeGetPath(virStorageVolPtr ob ret = strdup(vol->target.path); if (ret == NULL) - virStorageReportError(obj->conn, VIR_ERR_NO_MEMORY, "%s", _("path")); + virReportOOMError(obj->conn); cleanup: if (pool) diff --git a/src/test.c b/src/test.c --- a/src/test.c +++ b/src/test.c @@ -46,6 +46,8 @@ #include "xml.h" #include "threads.h" +#define VIR_FROM_THIS VIR_FROM_TEST + #define MAX_CPUS 128 struct _testCell { @@ -154,7 +156,7 @@ testBuildCapabilities(virConnectPtr conn return caps; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); virCapabilitiesFree(caps); return NULL; } @@ -195,7 +197,7 @@ static const char *defaultPoolXML = static const unsigned long long defaultPoolCap = (100 * 1024 * 1024 * 1024ull); static const unsigned long long defaultPoolAlloc = 0; -static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr pool); +static int testStoragePoolObjSetDefaults(virConnectPtr conn, virStoragePoolObjPtr pool); static int testOpenDefault(virConnectPtr conn) { int u; @@ -209,7 +211,7 @@ static int testOpenDefault(virConnectPtr virStoragePoolObjPtr poolobj = NULL; if (VIR_ALLOC(privconn) < 0) { - testError(conn, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn); return VIR_DRV_OPEN_ERROR; } if (virMutexInit(&privconn->lock) < 0) { @@ -223,7 +225,8 @@ static int testOpenDefault(virConnectPtr conn->privateData = privconn; if (gettimeofday(&tv, NULL) < 0) { - testError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("getting time of day")); + virReportSystemError(conn, errno, + "%s", _("getting time of day")); goto error; } @@ -276,7 +279,7 @@ static int testOpenDefault(virConnectPtr goto error; } - if (testStoragePoolObjSetDefaults(poolobj) == -1) { + if (testStoragePoolObjSetDefaults(conn, poolobj) == -1) { virStoragePoolObjUnlock(poolobj); goto error; } @@ -336,7 +339,7 @@ static int testOpenFromFile(virConnectPt virDomainObjPtr dom; testConnPtr privconn; if (VIR_ALLOC(privconn) < 0) { - testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn); return VIR_DRV_OPEN_ERROR; } if (virMutexInit(&privconn->lock) < 0) { @@ -353,9 +356,9 @@ static int testOpenFromFile(virConnectPt goto error; if ((fd = open(file, O_RDONLY)) < 0) { - testError(NULL, VIR_ERR_INTERNAL_ERROR, - _("loading host definition file '%s': %s"), - file, strerror(errno)); + virReportSystemError(NULL, errno, + _("loading host definition file '%s'"), + file); goto error; } @@ -573,7 +576,7 @@ static int testOpenFromFile(virConnectPt goto error; } - if (testStoragePoolObjSetDefaults(pool) == -1) { + if (testStoragePoolObjSetDefaults(conn, pool) == -1) { virStoragePoolObjUnlock(pool); goto error; } @@ -673,8 +676,8 @@ static char *testGetHostname (virConnect result = virGetHostname(); if (result == NULL) { - testError (conn, VIR_ERR_SYSTEM_ERROR, "%s", - strerror (errno)); + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); return NULL; } /* Caller frees this string. */ @@ -703,7 +706,7 @@ static char *testGetCapabilities (virCon char *xml; testDriverLock(privconn); if ((xml = virCapabilitiesFormatXML(privconn->caps)) == NULL) - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); testDriverUnlock(privconn); return xml; } @@ -1111,42 +1114,42 @@ static int testDomainSave(virDomainPtr d xml = testDomainDumpXML(domain, 0); if (xml == NULL) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' failed to allocate space for metadata: %s"), - domain->name, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' failed to allocate space for metadata"), + domain->name); goto cleanup; } if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': open failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': open failed"), + domain->name, path); goto cleanup; } len = strlen(xml); if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } if (safewrite(fd, (char*)&len, sizeof(len)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } if (safewrite(fd, xml, len) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } if (close(fd) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("saving domain '%s' to '%s': write failed: %s"), - domain->name, path, strerror(errno)); + virReportSystemError(domain->conn, errno, + _("saving domain '%s' to '%s': write failed"), + domain->name, path); goto cleanup; } fd = -1; @@ -1189,13 +1192,15 @@ static int testDomainRestore(virConnectP int ret = -1; if ((fd = open(path, O_RDONLY)) < 0) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot read domain image")); + virReportSystemError(conn, errno, + _("cannot read domain image '%s'"), + path); goto cleanup; } - if (read(fd, magic, sizeof(magic)) != sizeof(magic)) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("incomplete save header")); + if (saferead(fd, magic, sizeof(magic)) != sizeof(magic)) { + virReportSystemError(conn, errno, + _("incomplete save header in '%s'"), + path); goto cleanup; } if (memcmp(magic, TEST_SAVE_MAGIC, sizeof(magic))) { @@ -1203,9 +1208,10 @@ static int testDomainRestore(virConnectP "%s", _("mismatched header magic")); goto cleanup; } - if (read(fd, (char*)&len, sizeof(len)) != sizeof(len)) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to read metadata length")); + if (saferead(fd, (char*)&len, sizeof(len)) != sizeof(len)) { + virReportSystemError(conn, errno, + _("failed to read metadata length in '%s'"), + path); goto cleanup; } if (len < 1 || len > 8192) { @@ -1214,12 +1220,12 @@ static int testDomainRestore(virConnectP goto cleanup; } if (VIR_ALLOC_N(xml, len+1) < 0) { - testError(conn, VIR_ERR_NO_MEMORY, "xml"); + virReportOOMError(conn); goto cleanup; } - if (read(fd, xml, len) != len) { - testError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("incomplete metdata")); + if (saferead(fd, xml, len) != len) { + virReportSystemError(conn, errno, + _("incomplete metdata in '%s'"), path); goto cleanup; } xml[len] = '\0'; @@ -1269,21 +1275,21 @@ static int testDomainCoreDump(virDomainP } if ((fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("domain '%s' coredump: failed to open %s: %s"), - domain->name, to, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("domain '%s' coredump: failed to open %s"), + domain->name, to); goto cleanup; } if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("domain '%s' coredump: failed to write header to %s: %s"), - domain->name, to, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("domain '%s' coredump: failed to write header to %s"), + domain->name, to); goto cleanup; } if (close(fd) < 0) { - testError(domain->conn, VIR_ERR_INTERNAL_ERROR, - _("domain '%s' coredump: write failed: %s: %s"), - domain->name, to, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("domain '%s' coredump: write failed: %s"), + domain->name, to); goto cleanup; } privdom->state = VIR_DOMAIN_SHUTOFF; @@ -1306,7 +1312,7 @@ cleanup: static char *testGetOSType(virDomainPtr dom) { char *ret = strdup("linux"); if (!ret) - testError(dom->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(dom->conn); return ret; } @@ -1491,7 +1497,7 @@ static int testListDefinedDomains(virCon return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < maxnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -1682,7 +1688,7 @@ static char *testDomainGetSchedulerType( *nparams = 1; type = strdup("fair"); if (!type) - testError(domain->conn, VIR_ERR_NO_MEMORY, "schedular"); + virReportOOMError(domain->conn); return type; } @@ -1864,7 +1870,7 @@ static int testListNetworks(virConnectPt return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -1907,7 +1913,7 @@ static int testListDefinedNetworks(virCo return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -2099,7 +2105,7 @@ static char *testNetworkGetBridgeName(vi if (privnet->def->bridge && !(bridge = strdup(privnet->def->bridge))) { - testError(network->conn, VIR_ERR_NO_MEMORY, "network"); + virReportOOMError(network->conn); goto cleanup; } @@ -2164,7 +2170,8 @@ cleanup: * Storage Driver routines */ -static int testStoragePoolObjSetDefaults(virStoragePoolObjPtr pool) { +static int testStoragePoolObjSetDefaults(virConnectPtr conn, + virStoragePoolObjPtr pool) { pool->def->capacity = defaultPoolCap; pool->def->allocation = defaultPoolAlloc; @@ -2172,7 +2179,7 @@ static int testStoragePoolObjSetDefaults pool->configFile = strdup("\0"); if (!pool->configFile) { - testError(NULL, VIR_ERR_NO_MEMORY, "configFile"); + virReportOOMError(conn); return -1; } @@ -2284,7 +2291,7 @@ testStorageListPools(virConnectPtr conn, return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -2331,7 +2338,7 @@ testStorageListDefinedPools(virConnectPt return n; no_memory: - testError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); for (n = 0 ; n < nnames ; n++) VIR_FREE(names[n]); testDriverUnlock(privconn); @@ -2408,7 +2415,7 @@ testStoragePoolCreate(virConnectPtr conn } def = NULL; - if (testStoragePoolObjSetDefaults(pool) == -1) { + if (testStoragePoolObjSetDefaults(conn, pool) == -1) { virStoragePoolObjRemove(&privconn->pools, pool); pool = NULL; goto cleanup; @@ -2447,7 +2454,7 @@ testStoragePoolDefine(virConnectPtr conn } def = NULL; - if (testStoragePoolObjSetDefaults(pool) == -1) { + if (testStoragePoolObjSetDefaults(conn, pool) == -1) { virStoragePoolObjRemove(&privconn->pools, pool); pool = NULL; goto cleanup; @@ -2806,7 +2813,7 @@ testStoragePoolListVolumes(virStoragePoo for (i = 0 ; i < privpool->volumes.count && n < maxnames ; i++) { if ((names[n++] = strdup(privpool->volumes.objs[i]->name)) == NULL) { - testError(pool->conn, VIR_ERR_NO_MEMORY, "%s", _("name")); + virReportOOMError(pool->conn); goto cleanup; } } @@ -2986,14 +2993,14 @@ testStorageVolumeCreateXML(virStoragePoo if (VIR_REALLOC_N(privpool->volumes.objs, privpool->volumes.count+1) < 0) { - testError(pool->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(pool->conn); goto cleanup; } if (VIR_ALLOC_N(privvol->target.path, strlen(privpool->def->target.path) + 1 + strlen(privvol->name) + 1) < 0) { - testError(pool->conn, VIR_ERR_NO_MEMORY, "%s", _("target")); + virReportOOMError(pool->conn); goto cleanup; } @@ -3002,8 +3009,7 @@ testStorageVolumeCreateXML(virStoragePoo strcat(privvol->target.path, privvol->name); privvol->key = strdup(privvol->target.path); if (privvol->key == NULL) { - testError(pool->conn, VIR_ERR_INTERNAL_ERROR, "%s", - _("storage vol key")); + virReportOOMError(pool->conn); goto cleanup; } @@ -3224,7 +3230,7 @@ testStorageVolumeGetPath(virStorageVolPt ret = strdup(privvol->target.path); if (ret == NULL) - testError(vol->conn, VIR_ERR_NO_MEMORY, "%s", _("path")); + virReportOOMError(vol->conn); cleanup: if (privpool) diff --git a/src/uml_driver.c b/src/uml_driver.c --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -64,6 +64,8 @@ #include "datatypes.h" #include "logging.h" +#define VIR_FROM_THIS VIR_FROM_UML + /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" @@ -159,7 +161,7 @@ umlIdentifyOneChrPTY(virConnectPtr conn, char *res = NULL; int retries = 0; if (virAsprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } requery: @@ -168,7 +170,7 @@ requery: if (STRPREFIX(res, "pts:")) { VIR_FREE(def->data.file.path); if ((def->data.file.path = strdup(res + 4)) == NULL) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); VIR_FREE(res); VIR_FREE(cmd); return -1; @@ -523,7 +525,7 @@ static int umlReadPidFile(virConnectPtr vm->pid = -1; if (virAsprintf(&pidfile, "%s/%s/pid", driver->monitorDir, vm->def->name) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } @@ -549,9 +551,9 @@ reopen: cleanup: if (rc != 0) - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to read pid: %s: %s"), - pidfile, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to read pid: %s"), + pidfile); VIR_FREE(pidfile); return rc; } @@ -564,7 +566,7 @@ static int umlMonitorAddress(virConnectP if (virAsprintf(&sockname, "%s/%s/mconsole", driver->monitorDir, vm->def->name) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } @@ -597,16 +599,16 @@ restat: } if ((vm->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot open socket %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot open socket")); return -1; } memset(addr.sun_path, 0, sizeof addr.sun_path); sprintf(addr.sun_path + 1, "%u", getpid()); if (bind(vm->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot bind socket %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot bind socket")); close(vm->monitor); vm->monitor = -1; return -1; @@ -658,9 +660,9 @@ static int umlMonitorCommand(virConnectP req.version = MONITOR_VERSION; req.length = strlen(cmd); if (req.length > (MONITOR_BUFLEN-1)) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot send too long command %s: %s"), - cmd, strerror(EINVAL)); + virReportSystemError(conn, EINVAL, + _("cannot send too long command %s (%d bytes)"), + cmd, req.length); return -1; } strncpy(req.data, cmd, req.length); @@ -668,9 +670,9 @@ static int umlMonitorCommand(virConnectP if (sendto(vm->monitor, &req, sizeof req, 0, (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot send command %s: %s"), - cmd, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot send command %s"), + cmd); return -1; } @@ -678,15 +680,14 @@ static int umlMonitorCommand(virConnectP addrlen = sizeof(addr); if (recvfrom(vm->monitor, &res, sizeof res, 0, (struct sockaddr *)&addr, &addrlen) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot read reply %s: %s"), - cmd, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read reply %s"), + cmd); goto error; } if (VIR_REALLOC_N(retdata, retlen + res.length) < 0) { - umlReportError(conn, NULL, NULL, - VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); goto error; } memcpy(retdata + retlen, res.data, res.length); @@ -740,39 +741,38 @@ static int umlStartVMDaemon(virConnectPt * in a sub-process so its hard to feed back a useful error */ if (stat(vm->def->os.kernel, &sb) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Cannot find UML kernel %s: %s"), - vm->def->os.kernel, strerror(errno)); + virReportSystemError(conn, errno, + _("Cannot find UML kernel %s"), + vm->def->os.kernel); return -1; } if (virFileMakePath(driver->logDir) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create log directory %s"), - driver->logDir); + virReportSystemError(conn, errno, + _("cannot create log directory %s"), + driver->logDir); return -1; } if (virAsprintf(&logfile, "%s/%s.log", driver->logDir, vm->def->name) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } if ((logfd = open(logfile, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to create logfile %s: %s"), - logfile, strerror(errno)); + virReportSystemError(conn, errno, + _("failed to create logfile %s"), + logfile); VIR_FREE(logfile); return -1; } VIR_FREE(logfile); if (umlSetCloseExec(logfd) < 0) { - umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Unable to set VM logfile close-on-exec flag %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("Unable to set VM logfile close-on-exec flag")); close(logfd); return -1; } @@ -909,7 +909,7 @@ static virDrvOpenStatus umlOpen(virConne } else { conn->uri = xmlParseURI(uid ? "uml:///session" : "uml:///system"); if (!conn->uri) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,NULL); + virReportOOMError(conn); return VIR_DRV_OPEN_ERROR; } } @@ -946,8 +946,7 @@ static char *umlGetCapabilities(virConne umlDriverLock(driver); if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, - "%s", _("failed to allocate space for capabilities support")); + virReportOOMError(conn); umlDriverUnlock(driver); return xml; @@ -1157,8 +1156,8 @@ umlGetHostname (virConnectPtr conn) result = virGetHostname(); if (result == NULL) { - umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, - "%s", strerror (errno)); + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); return NULL; } /* Caller frees this string. */ @@ -1326,8 +1325,7 @@ static char *umlDomainGetOSType(virDomai } if (!(type = strdup(vm->def->os.type))) - umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_MEMORY, - "%s", _("failed to allocate space for ostype")); + virReportOOMError(dom->conn); cleanup: if (vm) @@ -1511,8 +1509,7 @@ static int umlListDefinedDomains(virConn virDomainObjLock(driver->domains.objs[i]); if (!virDomainIsActive(driver->domains.objs[i])) { if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { - umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, - "%s", _("failed to allocate space for VM name string")); + virReportOOMError(conn); virDomainObjUnlock(driver->domains.objs[i]); goto cleanup; } @@ -1711,23 +1708,23 @@ static int umlDomainSetAutostart(virDoma int err; if ((err = virFileMakePath(driver->autostartDir))) { - umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create autostart directory %s: %s"), - driver->autostartDir, strerror(err)); + virReportSystemError(dom->conn, err, + _("cannot create autostart directory %s"), + driver->autostartDir); goto cleanup; } if (symlink(configFile, autostartLink) < 0) { - umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to create symlink '%s to '%s': %s"), - autostartLink, configFile, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to create symlink '%s to '%s'"), + autostartLink, configFile); goto cleanup; } } else { if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { - umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failed to delete symlink '%s': %s"), - autostartLink, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("Failed to delete symlink '%s'"), + autostartLink); goto cleanup; } } @@ -1786,8 +1783,8 @@ umlDomainBlockPeek (virDomainPtr dom, /* The path is correct, now try to open it and get its size. */ fd = open (path, O_RDONLY); if (fd == -1) { - umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, - "%s", strerror (errno)); + virReportSystemError(dom->conn, errno, + _("cannot open %s"), path); goto cleanup; } @@ -1797,8 +1794,8 @@ umlDomainBlockPeek (virDomainPtr dom, */ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || saferead (fd, buffer, size) == (ssize_t) -1) { - umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, - "%s", strerror (errno)); + virReportSystemError(dom->conn, errno, + _("cannot read %s"), path); goto cleanup; } diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -67,6 +67,7 @@ #define virLog(msg...) fprintf(stderr, msg) +#define VIR_FROM_THIS VIR_FROM_NONE #define ReportError(conn, code, fmt...) \ virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ @@ -212,37 +213,36 @@ __virExec(virConnectPtr conn, */ sigfillset(&newmask); if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot block signals: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot block signals")); return -1; } if ((null = open("/dev/null", O_RDONLY)) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot open %s: %s"), - "/dev/null", strerror(errno)); + virReportSystemError(conn, errno, + _("cannot open %s"), + "/dev/null"); goto cleanup; } if (outfd != NULL) { if (*outfd == -1) { if (pipe(pipeout) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot create pipe: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot create pipe")); goto cleanup; } if ((flags & VIR_EXEC_NONBLOCK) && virSetNonBlock(pipeout[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set non-blocking file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set non-blocking file descriptor flag")); goto cleanup; } if (virSetCloseExec(pipeout[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set close-on-exec file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set close-on-exec file descriptor flag")); goto cleanup; } @@ -259,21 +259,21 @@ __virExec(virConnectPtr conn, if (errfd != NULL) { if (*errfd == -1) { if (pipe(pipeerr) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to create pipe: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("Failed to create pipe")); goto cleanup; } if ((flags & VIR_EXEC_NONBLOCK) && virSetNonBlock(pipeerr[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set non-blocking file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set non-blocking file descriptor flag")); goto cleanup; } if (virSetCloseExec(pipeerr[0]) == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to set close-on-exec file descriptor flag")); + virReportSystemError(conn, errno, + "%s", _("Failed to set close-on-exec file descriptor flag")); goto cleanup; } @@ -288,8 +288,8 @@ __virExec(virConnectPtr conn, } if ((pid = fork()) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot fork child process: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot fork child process")); goto cleanup; } @@ -307,9 +307,8 @@ __virExec(virConnectPtr conn, /* Restore our original signal mask now child is safely running */ if (pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unblock signals: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot unblock signals")); return -1; } @@ -345,9 +344,8 @@ __virExec(virConnectPtr conn, and don't want to propagate that to children */ sigemptyset(&newmask); if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot unblock signals: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot unblock signals")); return -1; } @@ -363,24 +361,21 @@ __virExec(virConnectPtr conn, if (flags & VIR_EXEC_DAEMON) { if (setsid() < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot become session leader: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot become session leader")); _exit(1); } if (chdir("/") < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot change to root directory: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot change to root directory: %s")); _exit(1); } pid = fork(); if (pid < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot fork child process: %s"), - strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot fork child process")); _exit(1); } @@ -390,20 +385,20 @@ __virExec(virConnectPtr conn, if (dup2(infd >= 0 ? infd : null, STDIN_FILENO) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to setup stdin file handle: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("failed to setup stdin file handle")); _exit(1); } if (childout > 0 && dup2(childout, STDOUT_FILENO) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to setup stdout file handle: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("failed to setup stdout file handle")); _exit(1); } if (childerr > 0 && dup2(childerr, STDERR_FILENO) < 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to setup stderr file handle: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("failed to setup stderr file handle")); _exit(1); } @@ -419,9 +414,9 @@ __virExec(virConnectPtr conn, else execvp(argv[0], (char **) argv); - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot execute binary '%s': %s"), - argv[0], strerror(errno)); + virReportSystemError(conn, errno, + _("cannot execute binary %s"), + argv[0]); _exit(1); @@ -535,8 +530,8 @@ virPipeReadUntilEOF(virConnectPtr conn, continue; pollerr: - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("poll error: %s"), strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("poll error")); goto error; } @@ -599,16 +594,15 @@ virRun(virConnectPtr conn, while ((waitret = waitpid(childpid, &exitstatus, 0) == -1) && errno == EINTR); if (waitret == -1) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot wait for '%s': %s"), - argv[0], strerror(errno)); + virReportSystemError(conn, errno, + _("cannot wait for '%s'"), + argv[0]); goto error; } if (status == NULL) { errno = EINVAL; if (WIFEXITED(exitstatus) && WEXITSTATUS(exitstatus) != 0) { - ReportError(conn, VIR_ERR_INTERNAL_ERROR, _("'%s' exited with non-zero status %d and " "signal %d: %s"), argv_str, diff --git a/src/virterror.c b/src/virterror.c --- a/src/virterror.c +++ b/src/virterror.c @@ -20,6 +20,7 @@ #include "logging.h" #include "memory.h" #include "threads.h" +#include "util.h" virThreadLocal virLastErr; @@ -334,7 +335,7 @@ virResetLastError(void) * If the connection object was discovered to be invalid by * an API call, then the error will be reported against the * global error object. - * + * * Since 0.6.0, all errors reported in the per-connection object * are also duplicated in the global error object. As such an * application can always use virGetLastError(). This method @@ -365,7 +366,7 @@ virConnGetLastError(virConnectPtr conn) * If the connection object was discovered to be invalid by * an API call, then the error will be reported against the * global error object. - * + * * Since 0.6.0, all errors reported in the per-connection object * are also duplicated in the global error object. As such an * application can always use virGetLastError(). This method @@ -983,7 +984,7 @@ virErrorMsg(virErrorNumber error, const void virReportErrorHelper(virConnectPtr conn, int domcode, int errcode, const char *filename ATTRIBUTE_UNUSED, const char *funcname ATTRIBUTE_UNUSED, - long long linenr ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED, const char *fmt, ...) { va_list args; @@ -1005,3 +1006,69 @@ void virReportErrorHelper(virConnectPtr } + + + + +void virReportSystemErrorFull(virConnectPtr conn, + int domcode, + int theerrno, + const char *filename ATTRIBUTE_UNUSED, + const char *funcname ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED, + const char *fmt, ...) +{ + va_list args; + char errorMessage[1024]; + char systemError[1024]; + char *theerrnostr; + const char *virerr; + char *combined = NULL; + +#ifdef HAVE_STRERROR_R +#ifdef __USE_GNU + /* Annoying linux specific API contract */ + theerrnostr = strerror_r(theerrno, systemError, sizeof(systemError)); +#else + strerror_r(theerrno, systemError, sizeof(systemError)); + theerrnostr = systemError; +#endif +#else + /* Mingw lacks strerror_r() and its strerror() is definitely not + * threadsafe, so safest option is to just print the raw errno + * value - we can at least reliably & safely look it up in the + * header files for debug purposes + */ + snprintf(systemError, sizeof(systemError), "errno=%d", theerrno); + theerrnostr = systemError; +#endif + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args); + va_end(args); + } else { + errorMessage[0] = '\0'; + } + + if (virAsprintf(&combined, "%s: %s", errorMessage, theerrnostr) < 0) + combined = theerrnostr; /* OOM, so lets just pass the strerror info as best effort */ + + virerr = virErrorMsg(VIR_ERR_SYSTEM_ERROR, (errorMessage[0] ? errorMessage : NULL)); + virRaiseError(conn, NULL, NULL, domcode, VIR_ERR_SYSTEM_ERROR, VIR_ERR_ERROR, + virerr, errorMessage, NULL, -1, -1, virerr, errorMessage); +} + + +void virReportOOMErrorFull(virConnectPtr conn, + int domcode, + const char *filename ATTRIBUTE_UNUSED, + const char *funcname ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED) +{ + const char *virerr; + + virerr = virErrorMsg(VIR_ERR_NO_MEMORY, NULL); + virRaiseError(conn, NULL, NULL, domcode, VIR_ERR_NO_MEMORY, VIR_ERR_ERROR, + virerr, NULL, NULL, -1, -1, virerr, NULL); +} diff --git a/src/virterror_internal.h b/src/virterror_internal.h --- a/src/virterror_internal.h +++ b/src/virterror_internal.h @@ -48,10 +48,36 @@ const char *virErrorMsg(virErrorNumber e void virReportErrorHelper(virConnectPtr conn, int domcode, int errcode, const char *filename ATTRIBUTE_UNUSED, const char *funcname ATTRIBUTE_UNUSED, - long long linenr ATTRIBUTE_UNUSED, + size_t linenr ATTRIBUTE_UNUSED, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 7, 8); +void virReportSystemErrorFull(virConnectPtr conn, + int domcode, + int theerrno, + const char *filename, + const char *funcname, + size_t linenr, + const char *fmt, ...) + ATTRIBUTE_FORMAT(printf, 7, 8); + +#define virReportSystemError(conn, theerrno, fmt,...) \ + virReportSystemErrorFull((conn), \ + VIR_FROM_THIS, \ + (theerrno), \ + __FILE__, __FUNCTION__, __LINE__, \ + (fmt), __VA_ARGS__) + +void virReportOOMErrorFull(virConnectPtr conn, + int domcode, + const char *filename, + const char *funcname, + size_t linenr); + +#define virReportOOMError(conn) \ + virReportOOMErrorFull((conn), VIR_FROM_THIS, \ + __FILE__, __FUNCTION__, __LINE__) + void virSetGlobalError(void); void virSetConnError(virConnectPtr conn); diff --git a/src/xen_inotify.c b/src/xen_inotify.c --- a/src/xen_inotify.c +++ b/src/xen_inotify.c @@ -41,6 +41,8 @@ #include "xm_internal.h" /* for xenXMDomainConfigParse */ +#define VIR_FROM_THIS VIR_FROM_XEN_INOTIFY + #define virXenInotifyError(conn, code, fmt...) \ virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) @@ -390,9 +392,10 @@ xenInotifyOpen(virConnectPtr conn ATTRIB } /* populate initial list */ - if (!(dh = opendir(configDir))) { - virXenInotifyError (NULL, VIR_ERR_INTERNAL_ERROR, - "%s", strerror(errno)); + if (!(dh = opendir(configDir))) { + virReportSystemError(NULL, errno, + _("cannot open directory: %s"), + configDir); return -1; } while ((ent = readdir(dh))) { diff --git a/src/xen_internal.c b/src/xen_internal.c --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -58,6 +58,8 @@ #include "capabilities.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_XEN + /* * so far there is 2 versions of the structures usable for doing * hypervisor calls. @@ -2532,8 +2534,9 @@ xenHypervisorMakeCapabilities(virConnect cpuinfo = fopen ("/proc/cpuinfo", "r"); if (cpuinfo == NULL) { if (errno != ENOENT) { - virXenError (conn, VIR_ERR_SYSTEM_ERROR, - "/proc/cpuinfo: %s", strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read file %s"), + "/proc/cpuinfo"); return NULL; } } @@ -2542,9 +2545,9 @@ xenHypervisorMakeCapabilities(virConnect if (capabilities == NULL) { if (errno != ENOENT) { fclose(cpuinfo); - virXenError (conn, VIR_ERR_SYSTEM_ERROR, - "/sys/hypervisor/properties/capabilities: %s", - strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read file %s"), + "/sys/hypervisor/properties/capabilities"); return NULL; } } diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -44,6 +44,8 @@ #include "util.h" #include "memory.h" +#define VIR_FROM_THIS VIR_FROM_XEN + static int xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); static int @@ -451,7 +453,8 @@ xenUnifiedGetHostname (virConnectPtr con result = virGetHostname(); if (result == NULL) { - xenUnifiedError (conn, VIR_ERR_SYSTEM_ERROR, "%s", strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); return NULL; } /* Caller frees this string. */ diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -49,6 +49,8 @@ /* required for cpumap_t */ #include <xen/dom0_ops.h> +#define VIR_FROM_THIS VIR_FROM_XEND + #ifndef PROXY /* @@ -4101,14 +4103,13 @@ xenDaemonDomainMigratePrepare (virConnec if (uri_in == NULL) { r = gethostname (hostname, HOST_NAME_MAX+1); if (r == -1) { - virXendError (dconn, VIR_ERR_SYSTEM_ERROR, - _("gethostname failed: %s"), strerror (errno)); + virReportSystemError(dconn, errno, + _("unable to resolve name %s"), hostname); return -1; } *uri_out = strdup (hostname); if (*uri_out == NULL) { - virXendError (dconn, VIR_ERR_SYSTEM_ERROR, - _("failed to strdup hostname: %s"), strerror (errno)); + virReportOOMError(dconn); return -1; } } @@ -4742,9 +4743,9 @@ xenDaemonDomainBlockPeek (virDomainPtr d /* The path is correct, now try to open it and get its size. */ fd = open (path, O_RDONLY); if (fd == -1) { - virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, - _("failed to open for reading: %s: %s"), - path, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("failed to open for reading: %s"), + path); goto cleanup; } @@ -4754,9 +4755,9 @@ xenDaemonDomainBlockPeek (virDomainPtr d */ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || saferead (fd, buffer, size) == (ssize_t) -1) { - virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, - _("failed to lseek or read from file: %s: %s"), - path, strerror (errno)); + virReportSystemError(domain->conn, errno, + _("failed to lseek or read from file: %s"), + path); goto cleanup; } diff --git a/src/xm_internal.c b/src/xm_internal.c --- a/src/xm_internal.c +++ b/src/xm_internal.c @@ -47,6 +47,7 @@ #include "memory.h" #include "logging.h" +#define VIR_FROM_THIS VIR_FROM_XENXM #ifdef WITH_RHEL5_API #define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 0 @@ -284,7 +285,7 @@ static int xenXMConfigCopyStringInternal } if (!(*value = strdup(val->str))) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } @@ -413,8 +414,9 @@ xenXMConfigCacheAddFile(virConnectPtr co /* Get modified time */ if ((stat(filename, &st) < 0)) { - xenXMError (conn, VIR_ERR_INTERNAL_ERROR, - _("cannot stat %s: %s"), filename, strerror(errno)); + virReportSystemError(conn, errno, + _("cannot stat: %s"), + filename); return -1; } @@ -449,7 +451,7 @@ xenXMConfigCacheAddFile(virConnectPtr co } else { /* Completely new entry */ newborn = 1; if (VIR_ALLOC(entry) < 0) { - xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno)); + virReportOOMError(conn); return -1; } memcpy(entry->filename, filename, PATH_MAX); @@ -503,7 +505,8 @@ int xenXMConfigCacheRefresh (virConnectP int ret = -1; if (now == ((time_t)-1)) { - xenXMError (conn, VIR_ERR_SYSTEM_ERROR, "%s", strerror(errno)); + virReportSystemError(conn, errno, + "%s", _("cannot get time of day")); return (-1); } @@ -515,7 +518,9 @@ int xenXMConfigCacheRefresh (virConnectP /* Process the files in the config dir */ if (!(dh = opendir(configDir))) { - xenXMError (conn, VIR_ERR_SYSTEM_ERROR, "%s", strerror(errno)); + virReportSystemError(conn, errno, + _("cannot read directory %s"), + configDir); return (-1); } @@ -1297,7 +1302,7 @@ xenXMDomainConfigParse(virConnectPtr con return def; no_memory: - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); /* fallthrough */ cleanup: virDomainGraphicsDefFree(graphics); @@ -1531,14 +1536,14 @@ int xenXMDomainPinVcpu(virDomainPtr doma } if (virBufferError(&mapbuf)) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, "%s", _("allocate buffer")); + virReportOOMError(domain->conn); return -1; } mapstr = virBufferContentAndReset(&mapbuf); if (VIR_ALLOC_N(cpuset, maxcpu) < 0) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, "%s", _("allocate buffer")); + virReportOOMError(domain->conn); goto cleanup; } if (virDomainCpuSetParse(domain->conn, @@ -1781,12 +1786,12 @@ static int xenXMDomainConfigFormatDisk(v virBufferAddLit(&buf, ",w"); if (virBufferError(&buf)) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); return -1; } if (VIR_ALLOC(val) < 0) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); goto cleanup; } @@ -1853,7 +1858,7 @@ static int xenXMDomainConfigFormatNet(vi net->model); if (VIR_ALLOC(val) < 0) { - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); goto cleanup; } @@ -2254,7 +2259,7 @@ virConfPtr xenXMDomainConfigFormat(virCo return conf; no_memory: - xenXMError(conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(conn); cleanup: virConfFreeValue(diskVal); @@ -2348,7 +2353,7 @@ virDomainPtr xenXMDomainDefineXML(virCon goto error; if (VIR_ALLOC(entry) < 0) { - xenXMError(conn, VIR_ERR_NO_MEMORY, "%s", _("config")); + virReportOOMError(conn); goto error; } @@ -2544,7 +2549,7 @@ xenXMDomainAttachDevice(virDomainPtr dom case VIR_DOMAIN_DEVICE_DISK: { if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(domain->conn); goto cleanup; } def->disks[def->ndisks++] = dev->data.disk; @@ -2557,7 +2562,7 @@ xenXMDomainAttachDevice(virDomainPtr dom case VIR_DOMAIN_DEVICE_NET: { if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { - xenXMError(domain->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(domain->conn); goto cleanup; } def->nets[def->nnets++] = dev->data.net; @@ -2715,15 +2720,15 @@ int xenXMDomainGetAutostart(virDomainPtr int ret = -1; if (!linkname || !config) { - xenXMError(dom->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(dom->conn); goto cleanup; } *autostart = virFileLinkPointsTo(linkname, config); if (*autostart < 0) { - xenXMError(dom->conn, VIR_ERR_INTERNAL_ERROR, - _("failed to check autostart link %s: %s"), - linkname, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("cannot check link %s points to config %s"), + linkname, config); goto cleanup; } @@ -2743,24 +2748,24 @@ int xenXMDomainSetAutostart(virDomainPtr int ret = -1; if (!linkname || !config) { - xenXMError(dom->conn, VIR_ERR_NO_MEMORY, NULL); + virReportOOMError(dom->conn); goto cleanup; } if (autostart) { if (symlink(config, linkname) < 0 && errno != EEXIST) { - xenXMError(dom->conn, VIR_ERR_INTERNAL_ERROR, - _("failed to create link %s: %s"), - linkname, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("failed to create link %s to %s"), + config, linkname); goto cleanup; } } else { if (unlink(linkname) < 0 && errno != ENOENT) { - xenXMError(dom->conn, VIR_ERR_INTERNAL_ERROR, - _("failed to remove link %s: %s"), - linkname, strerror(errno)); + virReportSystemError(dom->conn, errno, + _("failed to remove link %s"), + linkname); goto cleanup; } } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 20, 2009 at 12:46:11PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 19, 2009 at 11:40:56AM +0100, Jim Meyering wrote:
diff --git a/src/test.c b/src/test.c ... @@ -336,7 +339,7 @@ static int testOpenFromFile(virConnectPt virDomainObjPtr dom; testConnPtr privconn; if (VIR_ALLOC(privconn) < 0) { - testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); + virReportOOMError(conn);
I don't remember the rules for NULL vs non-NULL conn. Is this s/NULL/conn/ transform valid? I think there's one more like it.
Here is an update with the fixes you mention in this mail, and re-diffed based on latest CVS.
Opps, i sent the wrong version of this new patch - which is probably why Jim saw a re-introduced use of strerror(). The correct patch which I'll apply is the same as this but with that sterrror removed of course :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
Incidentally, virFileMakePath will have to go, eventually. Not only is it recursive in the number of components of the name being created, but allocating PATH_MAX for each stack frame is very wasteful, and might make it easy to blow the stack/DoS -- if there's a way to make libvirtd call it with an abusive argument. Worst still, it creates directories with mkdir(path, 0777). No virFileMakePath caller is careful to chmod _all_ of the possibly-just-created directories, and even if they all were, there'd still be a race.
I'm not sure about the mkdir 0777 mode - it will be modulated by the process umask which will typically be 002 (regular user) or 022 (root). It may be worth checking the callers of this to see if this will be a problem or not.
True, but creating leading directories with too-lenient permissions is perhaps too harsh a penalty for running libvirtd with an overly-permissive umask. Maybe we should just make libvirtd examine the current umask and if it's too permissive, just bail out. But even that might not work, since when I'm running it as non-root it's impossible to know if I want directories to be group-writable or not, in general.
As for the stack/recursion issue, the impl is fairly naive. It can be trivially re-written to do one single strdup() of the incoming path, and then implement the whole thing using iteration instead of recursion.
Surprisingly, doing it _right_ is highly non-trivial. Luckily, there's code in gnulib to do this. I think it's even thread-safe. Though it's GPL'd, we should be able to fix that, if needed.
Finally, the malicious user supplied data issue - reasonably sure we only use directories based off getpwuid / /etc/libvirt/libvirtd.conf and don't allow user to supply arbitrary paths, but again worth checking the callers.
I glanced through the callers, and they look ok, since all names are from compiled-in constants or are read from config files. This seems to be the sole exception: src/storage_backend_fs.c: if (virFileMakePath(pool->def->target.path) < 0) {

With the domain events code, the callbacks triggered upon events get given a virDomainPtr object instance. In many cases it'd be desirable to grab this object and keep it in your app code. Unfortunately it is free'd the moment the callback finishes executing. When allowing multiple threads to access a single virConnectPtr object it is neccessary to ensure no thread releases it (virConnectCLose) while another thread is still using it. The way to address both of these problems is to allow an application to take an explicit reference on the object in question. So this patch exposes methods to allow an app to increment the ref count on all our public objects. To release the ref count, the existing virConectClose/ virDOmainFree, etc methods suffice include/libvirt/libvirt.h | 6 + include/libvirt/libvirt.h.in | 6 + src/libvirt.c | 183 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 4 files changed, 206 insertions(+), 1 deletion(-) Daniel diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -396,6 +396,7 @@ virConnectPtr virConnectOpenAu virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags); +int virConnectRef (virConnectPtr conn); int virConnectClose (virConnectPtr conn); const char * virConnectGetType (virConnectPtr conn); int virConnectGetVersion (virConnectPtr conn, @@ -453,6 +454,7 @@ int virDomainReboot int virDomainReboot (virDomainPtr domain, unsigned int flags); int virDomainDestroy (virDomainPtr domain); +int virDomainRef (virDomainPtr domain); int virDomainFree (virDomainPtr domain); /* @@ -765,6 +767,7 @@ int virNetworkCreate * Network destroy/free */ int virNetworkDestroy (virNetworkPtr network); +int virNetworkRef (virNetworkPtr network); int virNetworkFree (virNetworkPtr network); /* @@ -925,6 +928,7 @@ int virStoragePoolDe int virStoragePoolDestroy (virStoragePoolPtr pool); int virStoragePoolDelete (virStoragePoolPtr pool, unsigned int flags); +int virStoragePoolRef (virStoragePoolPtr pool); int virStoragePoolFree (virStoragePoolPtr pool); int virStoragePoolRefresh (virStoragePoolPtr pool, unsigned int flags); @@ -978,6 +982,7 @@ virStorageVolPtr virStorageVolCre unsigned int flags); int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); +int virStorageVolRef (virStorageVolPtr vol); int virStorageVolFree (virStorageVolPtr vol); int virStorageVolGetInfo (virStorageVolPtr vol, @@ -1045,6 +1050,7 @@ char * virNodeDeviceGet char * virNodeDeviceGetXMLDesc (virNodeDevicePtr dev, unsigned int flags); +int virNodeDeviceRef (virNodeDevicePtr dev); int virNodeDeviceFree (virNodeDevicePtr dev); /* diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -396,6 +396,7 @@ virConnectPtr virConnectOpenAu virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags); +int virConnectRef (virConnectPtr conn); int virConnectClose (virConnectPtr conn); const char * virConnectGetType (virConnectPtr conn); int virConnectGetVersion (virConnectPtr conn, @@ -453,6 +454,7 @@ int virDomainReboot int virDomainReboot (virDomainPtr domain, unsigned int flags); int virDomainDestroy (virDomainPtr domain); +int virDomainRef (virDomainPtr domain); int virDomainFree (virDomainPtr domain); /* @@ -765,6 +767,7 @@ int virNetworkCreate * Network destroy/free */ int virNetworkDestroy (virNetworkPtr network); +int virNetworkRef (virNetworkPtr network); int virNetworkFree (virNetworkPtr network); /* @@ -925,6 +928,7 @@ int virStoragePoolDe int virStoragePoolDestroy (virStoragePoolPtr pool); int virStoragePoolDelete (virStoragePoolPtr pool, unsigned int flags); +int virStoragePoolRef (virStoragePoolPtr pool); int virStoragePoolFree (virStoragePoolPtr pool); int virStoragePoolRefresh (virStoragePoolPtr pool, unsigned int flags); @@ -978,6 +982,7 @@ virStorageVolPtr virStorageVolCre unsigned int flags); int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); +int virStorageVolRef (virStorageVolPtr vol); int virStorageVolFree (virStorageVolPtr vol); int virStorageVolGetInfo (virStorageVolPtr vol, @@ -1045,6 +1050,7 @@ char * virNodeDeviceGet char * virNodeDeviceGetXMLDesc (virNodeDevicePtr dev, unsigned int flags); +int virNodeDeviceRef (virNodeDevicePtr dev); int virNodeDeviceFree (virNodeDevicePtr dev); /* diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -1119,6 +1119,35 @@ virConnectClose(virConnectPtr conn) return (0); } +/** + * virConnectRef: + * @conn: the connection to hold a reference on + * + * Increment the reference count on the connection. For each + * additional call to this method, there shall be a corresponding + * call to virConnectClose to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a connection would increment + * the reference count. + */ +int +virConnectRef(virConnectPtr conn) +{ + if ((!VIR_IS_CONNECT(conn))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&conn->lock); + DEBUG("conn=%p refs=%d", conn, conn->refs); + conn->refs++; + virMutexUnlock(&conn->lock); + return 0; +} + /* * Not for public use. This function is part of the internal * implementation of driver features in the remote case. @@ -1775,6 +1804,36 @@ virDomainFree(virDomainPtr domain) return -1; return(0); } + +/** + * virDomainRef: + * @conn: the domain to hold a reference on + * + * Increment the reference count on the domain. For each + * additional call to this method, there shall be a corresponding + * call to virDomainFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a domain would increment + * the reference count. + */ +int +virDomainRef(virDomainPtr domain) +{ + if ((!VIR_IS_CONNECTED_DOMAIN(domain))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&domain->conn->lock); + DEBUG("domain=%p refs=%d", domain, domain->refs); + domain->refs++; + virMutexUnlock(&domain->conn->lock); + return 0; +} + /** * virDomainSuspend: @@ -4814,6 +4873,35 @@ virNetworkFree(virNetworkPtr network) } /** + * virNetworkRef: + * @conn: the network to hold a reference on + * + * Increment the reference count on the network. For each + * additional call to this method, there shall be a corresponding + * call to virNetworkFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a network would increment + * the reference count. + */ +int +virNetworkRef(virNetworkPtr network) +{ + if ((!VIR_IS_CONNECTED_NETWORK(network))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&network->conn->lock); + DEBUG("network=%p refs=%d", network, network->refs); + network->refs++; + virMutexUnlock(&network->conn->lock); + return 0; +} + +/** * virNetworkGetName: * @network: a network object * @@ -5869,6 +5957,35 @@ virStoragePoolFree(virStoragePoolPtr poo /** + * virStoragePoolRef: + * @conn: the pool to hold a reference on + * + * Increment the reference count on the pool. For each + * additional call to this method, there shall be a corresponding + * call to virStoragePoolFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a pool would increment + * the reference count. + */ +int +virStoragePoolRef(virStoragePoolPtr pool) +{ + if ((!VIR_IS_CONNECTED_STORAGE_POOL(pool))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&pool->conn->lock); + DEBUG("pool=%p refs=%d", pool, pool->refs); + pool->refs++; + virMutexUnlock(&pool->conn->lock); + return 0; +} + +/** * virStoragePoolRefresh: * @pool: pointer to storage pool * @flags: flags to control refresh behaviour (currently unused, use 0) @@ -6619,6 +6736,35 @@ virStorageVolFree(virStorageVolPtr vol) return(0); } + +/** + * virStorageVolRef: + * @conn: the vol to hold a reference on + * + * Increment the reference count on the vol. For each + * additional call to this method, there shall be a corresponding + * call to virStorageVolFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a vol would increment + * the reference count. + */ +int +virStorageVolRef(virStorageVolPtr vol) +{ + if ((!VIR_IS_CONNECTED_STORAGE_VOL(vol))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&vol->conn->lock); + DEBUG("vol=%p refs=%d", vol, vol->refs); + vol->refs++; + virMutexUnlock(&vol->conn->lock); + return 0; +} /** * virStorageVolGetInfo: @@ -7093,6 +7239,36 @@ int virNodeDeviceFree(virNodeDevicePtr d } +/** + * virNodeDeviceRef: + * @conn: the dev to hold a reference on + * + * Increment the reference count on the dev. For each + * additional call to this method, there shall be a corresponding + * call to virNodeDeviceFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using a dev would increment + * the reference count. + */ +int +virNodeDeviceRef(virNodeDevicePtr dev) +{ + if ((!VIR_IS_CONNECTED_NODE_DEVICE(dev))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&dev->conn->lock); + DEBUG("dev=%p refs=%d", dev, dev->refs); + dev->refs++; + virMutexUnlock(&dev->conn->lock); + return 0; +} + + /* * Domain Event Notification */ @@ -7107,6 +7283,13 @@ int virNodeDeviceFree(virNodeDevicePtr d * Adds a Domain Event Callback. * Registering for a domain callback will enable delivery of the events * + * The virDomainPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the domain object after the callback + * returns, it shall take a reference to it, by calling virDomainRef. + * The reference can be released once the object is no longer required + * by calling virDomainFree. + * * Returns 0 on success, -1 on failure */ int diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -236,5 +236,15 @@ LIBVIRT_0.5.0 { } LIBVIRT_0.4.5; -# no new entry point in 0.5.1 +LIBVIRT_0.6.0 { + global: + virConnectRef; + virDomainRef; + virNetworkRef; + virStoragePoolRef; + virStorageVolRef; + virNodeDeviceRef; + +} LIBVIRT_0.5.0; + # .... define new API here using predicted next version number .... -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:43:08PM +0000, Daniel P. Berrange wrote:
With the domain events code, the callbacks triggered upon events get given a virDomainPtr object instance. In many cases it'd be desirable to grab this object and keep it in your app code. Unfortunately it is free'd the moment the callback finishes executing.
When allowing multiple threads to access a single virConnectPtr object it is neccessary to ensure no thread releases it (virConnectCLose) while another thread is still using it.
The way to address both of these problems is to allow an application to take an explicit reference on the object in question. So this patch exposes methods to allow an app to increment the ref count on all our public objects. To release the ref count, the existing virConectClose/ virDOmainFree, etc methods suffice
include/libvirt/libvirt.h | 6 + include/libvirt/libvirt.h.in | 6 + src/libvirt.c | 183 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 4 files changed, 206 insertions(+), 1 deletion(-)
Poor man's garbage collection ... +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Thu, Jan 15, 2009 at 05:18:40PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:43:08PM +0000, Daniel P. Berrange wrote:
With the domain events code, the callbacks triggered upon events get given a virDomainPtr object instance. In many cases it'd be desirable to grab this object and keep it in your app code. Unfortunately it is free'd the moment the callback finishes executing.
When allowing multiple threads to access a single virConnectPtr object it is neccessary to ensure no thread releases it (virConnectCLose) while another thread is still using it.
The way to address both of these problems is to allow an application to take an explicit reference on the object in question. So this patch exposes methods to allow an app to increment the ref count on all our public objects. To release the ref count, the existing virConectClose/ virDOmainFree, etc methods suffice
include/libvirt/libvirt.h | 6 + include/libvirt/libvirt.h.in | 6 + src/libvirt.c | 183 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 4 files changed, 206 insertions(+), 1 deletion(-)
Poor man's garbage collection ... +1.
Yeah it's unfortunate we have to add this at the API level, but unfortunately there is no way around. I wonder if we could resurrect some of the deprecated fields of error structure now, seems to me that would be possible now. Patch looks fine, +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Jan 16, 2009 at 01:35:37PM +0100, Daniel Veillard wrote:
On Thu, Jan 15, 2009 at 05:18:40PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:43:08PM +0000, Daniel P. Berrange wrote:
With the domain events code, the callbacks triggered upon events get given a virDomainPtr object instance. In many cases it'd be desirable to grab this object and keep it in your app code. Unfortunately it is free'd the moment the callback finishes executing.
When allowing multiple threads to access a single virConnectPtr object it is neccessary to ensure no thread releases it (virConnectCLose) while another thread is still using it.
The way to address both of these problems is to allow an application to take an explicit reference on the object in question. So this patch exposes methods to allow an app to increment the ref count on all our public objects. To release the ref count, the existing virConectClose/ virDOmainFree, etc methods suffice
include/libvirt/libvirt.h | 6 + include/libvirt/libvirt.h.in | 6 + src/libvirt.c | 183 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 4 files changed, 206 insertions(+), 1 deletion(-)
Poor man's garbage collection ... +1.
Yeah it's unfortunate we have to add this at the API level, but unfortunately there is no way around. I wonder if we could resurrect some of the deprecated fields of error structure now, seems to me that would be possible now.
Unfortunately this isn't sufficient to make use of the dom/net fields practical. If we hold onto a reference when setting the error object, then we have a possible indefinite memory leak, unless the app calling libvirt always runs virResetLastError() after every API call. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 20, 2009 at 11:18:40AM +0000, Daniel P. Berrange wrote:
Unfortunately this isn't sufficient to make use of the dom/net fields practical. If we hold onto a reference when setting the error object, then we have a possible indefinite memory leak, unless the app calling libvirt always runs virResetLastError() after every API call.
I don't think it really matters. The application can keep a handle on the original dom/net objects passed into the call, if it needs them for error reporting. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora

On Fri, Jan 16, 2009 at 01:35:37PM +0100, Daniel Veillard wrote:
On Thu, Jan 15, 2009 at 05:18:40PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:43:08PM +0000, Daniel P. Berrange wrote:
With the domain events code, the callbacks triggered upon events get given a virDomainPtr object instance. In many cases it'd be desirable to grab this object and keep it in your app code. Unfortunately it is free'd the moment the callback finishes executing.
When allowing multiple threads to access a single virConnectPtr object it is neccessary to ensure no thread releases it (virConnectCLose) while another thread is still using it.
The way to address both of these problems is to allow an application to take an explicit reference on the object in question. So this patch exposes methods to allow an app to increment the ref count on all our public objects. To release the ref count, the existing virConectClose/ virDOmainFree, etc methods suffice
include/libvirt/libvirt.h | 6 + include/libvirt/libvirt.h.in | 6 + src/libvirt.c | 183 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 4 files changed, 206 insertions(+), 1 deletion(-)
Poor man's garbage collection ... +1.
Yeah it's unfortunate we have to add this at the API level, but unfortunately there is no way around. I wonder if we could resurrect some of the deprecated fields of error structure now, seems to me that would be possible now.
Patch looks fine, +1
Comitted this patch Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
The way to address both of these problems is to allow an application to take an explicit reference on the object in question. So this patch exposes methods to allow an app to increment the ref count on all our public objects. To release the ref count, the existing virConectClose/ virDOmainFree, etc methods suffice
ACK

The 'inotify' driver currently keeps all its state in a global static variables. Not so great since we access this from all sorts of places and its hard to guarentee thread safety. Also, we're using per-connection FD watches to update this state, so if multiple Xen connections were active, they'd all be updating the same globl state. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though, but not by very much xen_inotify.c | 105 +++++++++++++++++++++++++++++++--------------------------- xen_unified.h | 7 +++ xs_internal.c | 33 ++++++++---------- 3 files changed, 79 insertions(+), 66 deletions(-) Daniel diff --git a/src/xen_inotify.c b/src/xen_inotify.c --- a/src/xen_inotify.c +++ b/src/xen_inotify.c @@ -48,9 +48,6 @@ __FUNCTION__, __LINE__, fmt) #define LIBVIRTD_DOMAINS_DIR "/var/lib/xend/domains" -static const char *configDir = NULL; -static int useXenConfigCache = 0; -static xenUnifiedDomainInfoListPtr configInfoList = NULL; struct xenUnifiedDriver xenInotifyDriver = { xenInotifyOpen, /* open */ @@ -121,6 +118,7 @@ xenInotifyXendDomainsDirLookup(virConnec virDomainPtr dom; const char *uuid_str; unsigned char rawuuid[VIR_UUID_BUFLEN]; + xenUnifiedPrivatePtr priv = conn->privateData; /* xend is managing domains. we will get * a filename in the manner: @@ -142,15 +140,15 @@ xenInotifyXendDomainsDirLookup(virConnec /* If we are here, the domain has gone away. search for, and create a domain from the stored list info */ - for (i=0; i<configInfoList->count; i++) { - if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { - *name = strdup(configInfoList->doms[i]->name); + for (i = 0 ; i < priv->configInfoList->count ; i++) { + if (!memcmp(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { + *name = strdup(priv->configInfoList->doms[i]->name); if (!*name) { virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, _("finding dom for %s"), uuid_str); return -1; } - memcpy(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN); + memcpy(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN); DEBUG0("Found dom on list"); return 0; } @@ -172,7 +170,8 @@ xenInotifyDomainLookup(virConnectPtr con xenInotifyDomainLookup(virConnectPtr conn, const char *filename, char **name, unsigned char *uuid) { - if (useXenConfigCache) + xenUnifiedPrivatePtr priv = conn->privateData; + if (priv->useXenConfigCache) return xenInotifyXenCacheLookup(filename, name, uuid); else return xenInotifyXendDomainsDirLookup(conn, filename, name, uuid); @@ -195,8 +194,9 @@ xenInotifyDomainEventFromFile(virConnect } static int -xenInotifyXendDomainsDirRemoveEntry(virConnectPtr conn ATTRIBUTE_UNUSED, +xenInotifyXendDomainsDirRemoveEntry(virConnectPtr conn, const char *fname) { + xenUnifiedPrivatePtr priv = conn->privateData; const char *uuidstr = fname + strlen(LIBVIRTD_DOMAINS_DIR) + 1; unsigned char uuid[VIR_UUID_BUFLEN]; int i; @@ -208,22 +208,22 @@ xenInotifyXendDomainsDirRemoveEntry(virC } /* match and remove on uuid */ - for (i=0; i<configInfoList->count; i++) { - if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { - VIR_FREE(configInfoList->doms[i]->name); - VIR_FREE(configInfoList->doms[i]); + for (i = 0 ; i < priv->configInfoList->count ; i++) { + if (!memcmp(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { + VIR_FREE(priv->configInfoList->doms[i]->name); + VIR_FREE(priv->configInfoList->doms[i]); - if (i < (configInfoList->count - 1)) - memmove(configInfoList->doms + i, - configInfoList->doms + i + 1, - sizeof(*(configInfoList->doms)) * - (configInfoList->count - (i + 1))); + if (i < (priv->configInfoList->count - 1)) + memmove(priv->configInfoList->doms + i, + priv->configInfoList->doms + i + 1, + sizeof(*(priv->configInfoList->doms)) * + (priv->configInfoList->count - (i + 1))); - if (VIR_REALLOC_N(configInfoList->doms, - configInfoList->count - 1) < 0) { + if (VIR_REALLOC_N(priv->configInfoList->doms, + priv->configInfoList->count - 1) < 0) { ; /* Failure to reduce memory allocation isn't fatal */ } - configInfoList->count--; + priv->configInfoList->count--; return 0; } } @@ -235,13 +235,15 @@ xenInotifyXendDomainsDirAddEntry(virConn const char *fname) { char *name = NULL; unsigned char uuid[VIR_UUID_BUFLEN]; + xenUnifiedPrivatePtr priv = conn->privateData; + if (xenInotifyDomainLookup(conn, fname, &name, uuid) < 0) { virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("Error looking up domain")); return -1; } - if( xenUnifiedAddDomainInfo(configInfoList, + if (xenUnifiedAddDomainInfo(priv->configInfoList, -1, name, uuid) < 0) { virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("Error adding file to config cache")); @@ -255,15 +257,19 @@ static int static int xenInotifyRemoveDomainConfigInfo(virConnectPtr conn, const char *fname) { - return useXenConfigCache ? xenXMConfigCacheRemoveFile(conn, fname) : - xenInotifyXendDomainsDirRemoveEntry(conn, fname); + xenUnifiedPrivatePtr priv = conn->privateData; + return priv->useXenConfigCache ? + xenXMConfigCacheRemoveFile(conn, fname) : + xenInotifyXendDomainsDirRemoveEntry(conn, fname); } static int xenInotifyAddDomainConfigInfo(virConnectPtr conn, const char *fname) { - return useXenConfigCache ? xenXMConfigCacheAddFile(conn, fname) : - xenInotifyXendDomainsDirAddEntry(conn, fname); + xenUnifiedPrivatePtr priv = conn->privateData; + return priv->useXenConfigCache ? + xenXMConfigCacheAddFile(conn, fname) : + xenInotifyXendDomainsDirAddEntry(conn, fname); } static void @@ -277,7 +283,7 @@ xenInotifyEvent(int watch ATTRIBUTE_UNUS struct inotify_event *e; int got; char *tmp, *name; - virConnectPtr conn = (virConnectPtr) data; + virConnectPtr conn = data; xenUnifiedPrivatePtr priv = NULL; DEBUG0("got inotify event"); @@ -315,7 +321,8 @@ reread: name = (char *)&(e->name); - snprintf(fname, 1024, "%s/%s", configDir, name); + snprintf(fname, 1024, "%s/%s", + priv->configDir, name); if (e->mask & (IN_DELETE | IN_MOVED_FROM)) { virDomainEventPtr event = @@ -378,24 +385,24 @@ xenInotifyOpen(virConnectPtr conn ATTRIB if(priv->xendConfigVersion <= 2) { /* /etc/xen */ - configDir = xenXMGetConfigDir(); - useXenConfigCache = 1; + priv->configDir = xenXMGetConfigDir(); + priv->useXenConfigCache = 1; } else { /* /var/lib/xend/domains/<uuid>/config.sxp */ - configDir = LIBVIRTD_DOMAINS_DIR; - useXenConfigCache = 0; + priv->configDir = LIBVIRTD_DOMAINS_DIR; + priv->useXenConfigCache = 0; - if ( VIR_ALLOC(configInfoList ) < 0) { + if (VIR_ALLOC(priv->configInfoList) < 0) { virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to allocate configInfoList")); + "%s", _("failed to allocate configInfoList")); return -1; } /* populate initial list */ - if (!(dh = opendir(configDir))) { + if (!(dh = opendir(priv->configDir))) { virReportSystemError(NULL, errno, _("cannot open directory: %s"), - configDir); + priv->configDir); return -1; } while ((ent = readdir(dh))) { @@ -403,9 +410,10 @@ xenInotifyOpen(virConnectPtr conn ATTRIB continue; /* Build the full file path */ - if ((strlen(configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX) + if ((strlen(priv->configDir) + 1 + + strlen(ent->d_name) + 1) > PATH_MAX) continue; - strcpy(path, configDir); + strcpy(path, priv->configDir); strcat(path, "/"); strcat(path, ent->d_name); @@ -419,24 +427,25 @@ xenInotifyOpen(virConnectPtr conn ATTRIB } if ((priv->inotifyFD = inotify_init()) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("initializing inotify")); + virReportSystemError(NULL, errno, + "%s", _("initializing inotify")); return -1; } - DEBUG("Adding a watch on %s", configDir); + DEBUG("Adding a watch on %s", priv->configDir); if (inotify_add_watch(priv->inotifyFD, - configDir, + priv->configDir, IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - _("adding watch on %s"), _(configDir)); + virReportSystemError(NULL, errno, + _("adding watch on %s"), + priv->configDir); return -1; } DEBUG0("Building initial config cache"); - if (useXenConfigCache && + if (priv->useXenConfigCache && xenXMConfigCacheRefresh (conn) < 0) { DEBUG("Failed to enable XM config cache %s", conn->err.message); return -1; @@ -464,10 +473,10 @@ int int xenInotifyClose(virConnectPtr conn) { - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + xenUnifiedPrivatePtr priv = conn->privateData; - if(configInfoList) - xenUnifiedDomainInfoListFree(configInfoList); + if (priv->configInfoList) + xenUnifiedDomainInfoListFree(priv->configInfoList); if (priv->inotifyWatch != -1) virEventRemoveHandle(priv->inotifyWatch); diff --git a/src/xen_unified.h b/src/xen_unified.h --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -156,6 +156,9 @@ struct _xenUnifiedPrivate { /* A list of xenstore watches */ xenStoreWatchListPtr xsWatchList; int xsWatch; + /* A list of active domain name/uuids */ + xenUnifiedDomainInfoListPtr activeDomainList; + /* An list of callbacks */ virDomainEventCallbackListPtr domainEventCallbacks; @@ -164,6 +167,10 @@ struct _xenUnifiedPrivate { /* The inotify fd */ int inotifyFD; int inotifyWatch; + + const char *configDir; + int useXenConfigCache ; + xenUnifiedDomainInfoListPtr configInfoList; #endif }; diff --git a/src/xs_internal.c b/src/xs_internal.c --- a/src/xs_internal.c +++ b/src/xs_internal.c @@ -46,9 +46,6 @@ #endif #ifndef PROXY -/* A list of active domain name/uuids */ -static xenUnifiedDomainInfoListPtr activeDomainList = NULL; - static char *xenStoreDomainGetOSType(virDomainPtr domain); struct xenUnifiedDriver xenStoreDriver = { @@ -312,7 +309,7 @@ xenStoreOpen(virConnectPtr conn, #ifndef PROXY /* Init activeDomainList */ - if ( VIR_ALLOC(activeDomainList) < 0) { + if (VIR_ALLOC(priv->activeDomainList) < 0) { virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("failed to allocate activeDomainList")); return -1; @@ -389,7 +386,7 @@ xenStoreClose(virConnectPtr conn) xenStoreWatchListFree(priv->xsWatchList); #ifndef PROXY - xenUnifiedDomainInfoListFree(activeDomainList); + xenUnifiedDomainInfoListFree(priv->activeDomainList); #endif if (priv->xshandle == NULL) return(-1); @@ -1188,7 +1185,7 @@ int xenStoreDomainIntroduced(virConnectP int *new_domids; int nread; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque; + xenUnifiedPrivatePtr priv = opaque; retry: new_domain_cnt = xenStoreNumOfDomains(conn); @@ -1207,8 +1204,8 @@ retry: missing = 0; for (i=0 ; i < new_domain_cnt ; i++) { found = 0; - for (j = 0 ; j < activeDomainList->count ; j++) { - if (activeDomainList->doms[j]->id == new_domids[i]) { + for (j = 0 ; j < priv->activeDomainList->count ; j++) { + if (priv->activeDomainList->doms[j]->id == new_domids[i]) { found = 1; break; } @@ -1236,7 +1233,7 @@ retry: xenUnifiedDomainEventDispatch(priv, event); /* Add to the list */ - xenUnifiedAddDomainInfo(activeDomainList, + xenUnifiedAddDomainInfo(priv->activeDomainList, new_domids[i], name, uuid); VIR_FREE(name); @@ -1265,7 +1262,7 @@ int xenStoreDomainReleased(virConnectPtr xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque; - if(!activeDomainList->count) return 0; + if(!priv->activeDomainList->count) return 0; retry: new_domain_cnt = xenStoreNumOfDomains(conn); @@ -1283,10 +1280,10 @@ retry: } removed = 0; - for (j=0 ; j < activeDomainList->count ; j++) { + for (j=0 ; j < priv->activeDomainList->count ; j++) { found = 0; for (i=0 ; i < new_domain_cnt ; i++) { - if (activeDomainList->doms[j]->id == new_domids[i]) { + if (priv->activeDomainList->doms[j]->id == new_domids[i]) { found = 1; break; } @@ -1295,18 +1292,18 @@ retry: if (!found) { virDomainEventPtr event = virDomainEventNew(-1, - activeDomainList->doms[j]->name, - activeDomainList->doms[j]->uuid, + priv->activeDomainList->doms[j]->name, + priv->activeDomainList->doms[j]->uuid, VIR_DOMAIN_EVENT_STOPPED, VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); if (event) xenUnifiedDomainEventDispatch(priv, event); /* Remove from the list */ - xenUnifiedRemoveDomainInfo(activeDomainList, - activeDomainList->doms[j]->id, - activeDomainList->doms[j]->name, - activeDomainList->doms[j]->uuid); + xenUnifiedRemoveDomainInfo(priv->activeDomainList, + priv->activeDomainList->doms[j]->id, + priv->activeDomainList->doms[j]->name, + priv->activeDomainList->doms[j]->uuid); removed = 1; } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:43:38PM +0000, Daniel P. Berrange wrote:
The 'inotify' driver currently keeps all its state in a global static variables. Not so great since we access this from all sorts of places and its hard to guarentee thread safety. Also, we're using per-connection FD watches to update this state, so if multiple Xen connections were active, they'd all be updating the same globl state. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though, but not by very much
I'm not really familiar with this code. It seems reasonable, but I'll let someone else check it. Just to say ...
- if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) {
memcmp (...) == 0 or define a new equality test macro? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The 'inotify' driver currently keeps all its state in a global static variables. Not so great since we access this from all sorts of places and its hard to guarentee thread safety. Also, we're using per-connection FD watches to update this state, so if multiple Xen connections were active, they'd all be updating the same globl state. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though, but not by very much
Looks fine. ACK

On Mon, Jan 19, 2009 at 03:14:19PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The 'inotify' driver currently keeps all its state in a global static variables. Not so great since we access this from all sorts of places and its hard to guarentee thread safety. Also, we're using per-connection FD watches to update this state, so if multiple Xen connections were active, they'd all be updating the same globl state. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though, but not by very much
Looks fine. ACK
Comitted this. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

The 'xm' driver currently keeps all its state in a global static variables. Not cool, since we access this from all sorts of places and its hard to guarentee thread safety. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though. Undecided whether this is a big problem or not. If so, I'll can try and redo the next thread locking patch to use a lock over the existing global state. xen_inotify.c | 4 xen_unified.c | 1 xen_unified.h | 14 ++- xm_internal.c | 262 +++++++++++++++++++++++++++++----------------------------- xm_internal.h | 3 5 files changed, 149 insertions(+), 135 deletions(-) Daniel diff --git a/src/xen_inotify.c b/src/xen_inotify.c --- a/src/xen_inotify.c +++ b/src/xen_inotify.c @@ -383,9 +383,7 @@ xenInotifyOpen(virConnectPtr conn ATTRIB char path[PATH_MAX]; xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - if(priv->xendConfigVersion <= 2) { - /* /etc/xen */ - priv->configDir = xenXMGetConfigDir(); + if (priv->configDir) { priv->useXenConfigCache = 1; } else { /* /var/lib/xend/domains/<uuid>/config.sxp */ diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -1465,7 +1465,6 @@ xenRegister (void) { /* Ignore failures here. */ (void) xenHypervisorInit (); - (void) xenXMInit (); return virRegisterDriver (&xenUnifiedDriver); } diff --git a/src/xen_unified.h b/src/xen_unified.h --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -20,6 +20,7 @@ #include "xen_inotify.h" #endif #include "domain_event.h" +#include "hash.h" #ifndef HAVE_WINSOCK2_H #include <sys/un.h> @@ -163,15 +164,26 @@ struct _xenUnifiedPrivate { /* An list of callbacks */ virDomainEventCallbackListPtr domainEventCallbacks; + /* Location of config files, either /etc + * or /var/lib/xen */ + const char *configDir; + #if WITH_XEN_INOTIFY /* The inotify fd */ int inotifyFD; int inotifyWatch; - const char *configDir; int useXenConfigCache ; xenUnifiedDomainInfoListPtr configInfoList; #endif + + /* For the 'xm' driver */ + /* Primary config file name -> virDomainDef map */ + virHashTablePtr configCache; + /* Domain name to config file name */ + virHashTablePtr nameConfigMap; + /* So we don't refresh too often */ + time_t lastRefresh; }; typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; diff --git a/src/xm_internal.c b/src/xm_internal.c --- a/src/xm_internal.c +++ b/src/xm_internal.c @@ -56,15 +56,6 @@ static int xenXMConfigSetString(virConfPtr conf, const char *setting, const char *str); - -static char configDir[PATH_MAX]; -/* Config file name to config object */ -static virHashTablePtr configCache = NULL; -/* Name to config file name */ -static virHashTablePtr nameConfigMap = NULL; -static int nconnections = 0; -static time_t lastRefresh = 0; - char * xenXMAutoAssignMac(void); static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml); static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml); @@ -120,41 +111,10 @@ struct xenUnifiedDriver xenXMDriver = { NULL, /* domainSetSchedulerParameters */ }; -virHashTablePtr xenXMGetConfigCache (void) { - return configCache; -} - -char *xenXMGetConfigDir (void) { - return configDir; -} - #define xenXMError(conn, code, fmt...) \ virReportErrorHelper(conn, VIR_FROM_XENXM, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) -int -xenXMInit (void) -{ - char *envConfigDir; - int safeMode = 0; - - /* Disable use of env variable if running setuid */ - if ((geteuid() != getuid()) || - (getegid() != getgid())) - safeMode = 1; - - if (!safeMode && - (envConfigDir = getenv("LIBVIRT_XM_CONFIG_DIR")) != NULL) { - strncpy(configDir, envConfigDir, PATH_MAX-1); - configDir[PATH_MAX-1] = '\0'; - } else { - strcpy(configDir, XM_CONFIG_DIR); - } - - return 0; -} - - /* Convenience method to grab a int from the config file object */ static int xenXMConfigGetBool(virConnectPtr conn, virConfPtr conf, @@ -328,19 +288,23 @@ static void xenXMConfigFree(void *payloa VIR_FREE(entry); } +struct xenXMConfigReaperData { + xenUnifiedPrivatePtr priv; + time_t now; +}; /* Remove any configs which were not refreshed recently */ static int xenXMConfigReaper(const void *payload, const char *key ATTRIBUTE_UNUSED, const void *data) { - time_t now = *(const time_t *)data; + const struct xenXMConfigReaperData *args = data; xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; /* We're going to purge this config file, so check if it is currently mapped as owner of a named domain. */ - if (entry->refreshedAt != now) { + if (entry->refreshedAt != args->now) { const char *olddomname = entry->def->name; - char *nameowner = (char *)virHashLookup(nameConfigMap, olddomname); + char *nameowner = (char *)virHashLookup(args->priv->nameConfigMap, olddomname); if (nameowner && STREQ(nameowner, key)) { - virHashRemoveEntry(nameConfigMap, olddomname, NULL); + virHashRemoveEntry(args->priv->nameConfigMap, olddomname, NULL); } return (1); } @@ -376,19 +340,20 @@ xenXMConfigSaveFile(virConnectPtr conn, } int -xenXMConfigCacheRemoveFile(virConnectPtr conn ATTRIBUTE_UNUSED, +xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename) { + xenUnifiedPrivatePtr priv = conn->privateData; xenXMConfCachePtr entry; - entry = virHashLookup(configCache, filename); + entry = virHashLookup(priv->configCache, filename); if (!entry) { DEBUG("No config entry for %s", filename); return 0; } - virHashRemoveEntry(nameConfigMap, entry->def->name, NULL); - virHashRemoveEntry(configCache, filename, xenXMConfigFree); + virHashRemoveEntry(priv->nameConfigMap, entry->def->name, NULL); + virHashRemoveEntry(priv->configCache, filename, xenXMConfigFree); DEBUG("Removed %s %s", entry->def->name, filename); return 0; } @@ -397,6 +362,7 @@ xenXMConfigCacheRemoveFile(virConnectPtr int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename) { + xenUnifiedPrivatePtr priv = conn->privateData; xenXMConfCachePtr entry; struct stat st; int newborn = 0; @@ -421,7 +387,7 @@ xenXMConfigCacheAddFile(virConnectPtr co /* If we already have a matching entry and it is not modified, then carry on to next one*/ - if ((entry = virHashLookup(configCache, filename))) { + if ((entry = virHashLookup(priv->configCache, filename))) { char *nameowner; if (entry->refreshedAt >= st.st_mtime) { @@ -432,9 +398,9 @@ xenXMConfigCacheAddFile(virConnectPtr co /* If we currently own the name, then release it and re-acquire it later - just in case it was renamed */ - nameowner = (char *)virHashLookup(nameConfigMap, entry->def->name); + nameowner = (char *)virHashLookup(priv->nameConfigMap, entry->def->name); if (nameowner && STREQ(nameowner, filename)) { - virHashRemoveEntry(nameConfigMap, entry->def->name, NULL); + virHashRemoveEntry(priv->nameConfigMap, entry->def->name, NULL); } /* Clear existing config entry which needs refresh */ @@ -453,7 +419,7 @@ xenXMConfigCacheAddFile(virConnectPtr co if (!(entry->def = xenXMConfigReadFile(conn, entry->filename))) { DEBUG("Failed to read %s", entry->filename); if (!newborn) - virHashRemoveEntry(configCache, filename, NULL); + virHashRemoveEntry(priv->configCache, filename, NULL); VIR_FREE(entry); return -1; } @@ -461,7 +427,7 @@ xenXMConfigCacheAddFile(virConnectPtr co /* If its a completely new entry, it must be stuck into the cache (refresh'd entries are already registered) */ if (newborn) { - if (virHashAddEntry(configCache, entry->filename, entry) < 0) { + if (virHashAddEntry(priv->configCache, entry->filename, entry) < 0) { virDomainDefFree(entry->def); VIR_FREE(entry); xenXMError (conn, VIR_ERR_INTERNAL_ERROR, @@ -473,9 +439,9 @@ xenXMConfigCacheAddFile(virConnectPtr co /* See if we need to map this config file in as the primary owner * of the domain in question */ - if (!virHashLookup(nameConfigMap, entry->def->name)) { - if (virHashAddEntry(nameConfigMap, entry->def->name, entry->filename) < 0) { - virHashRemoveEntry(configCache, filename, NULL); + if (!virHashLookup(priv->nameConfigMap, entry->def->name)) { + if (virHashAddEntry(priv->nameConfigMap, entry->def->name, entry->filename) < 0) { + virHashRemoveEntry(priv->configCache, filename, NULL); virDomainDefFree(entry->def); VIR_FREE(entry); } @@ -491,10 +457,12 @@ xenXMConfigCacheAddFile(virConnectPtr co has rate-limited so never rescans more frequently than once every X seconds */ int xenXMConfigCacheRefresh (virConnectPtr conn) { + xenUnifiedPrivatePtr priv = conn->privateData; DIR *dh; struct dirent *ent; time_t now = time(NULL); int ret = -1; + struct xenXMConfigReaperData args; if (now == ((time_t)-1)) { virReportSystemError(conn, errno, @@ -503,16 +471,16 @@ int xenXMConfigCacheRefresh (virConnectP } /* Rate limit re-scans */ - if ((now - lastRefresh) < XM_REFRESH_INTERVAL) + if ((now - priv->lastRefresh) < XM_REFRESH_INTERVAL) return (0); - lastRefresh = now; + priv->lastRefresh = now; /* Process the files in the config dir */ - if (!(dh = opendir(configDir))) { + if (!(dh = opendir(priv->configDir))) { virReportSystemError(conn, errno, _("cannot read directory %s"), - configDir); + priv->configDir); return (-1); } @@ -547,9 +515,9 @@ int xenXMConfigCacheRefresh (virConnectP continue; /* Build the full file path */ - if ((strlen(configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX) + if ((strlen(priv->configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX) continue; - strcpy(path, configDir); + strcpy(path, priv->configDir); strcat(path, "/"); strcat(path, ent->d_name); @@ -570,7 +538,9 @@ int xenXMConfigCacheRefresh (virConnectP their refresh timestamp - the timestamp should match 'now' if they were refreshed. If timestamp doesn't match then the config is no longer on disk */ - virHashRemoveSet(configCache, xenXMConfigReaper, xenXMConfigFree, (const void*) &now); + args.now = now; + args.priv = priv; + virHashRemoveSet(priv->configCache, xenXMConfigReaper, xenXMConfigFree, &args); ret = 0; if (dh) @@ -587,26 +557,27 @@ int xenXMConfigCacheRefresh (virConnectP * to open all end up using the same cache of files */ virDrvOpenStatus -xenXMOpen (virConnectPtr conn ATTRIBUTE_UNUSED, +xenXMOpen (virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { - if (configCache == NULL) { - configCache = virHashCreate(50); - if (!configCache) - return (-1); - nameConfigMap = virHashCreate(50); - if (!nameConfigMap) { - virHashFree(configCache, NULL); - configCache = NULL; - return (-1); - } - /* Force the cache to be reloaded next time that - * xenXMConfigCacheRefresh is called. - */ - lastRefresh = 0; + xenUnifiedPrivatePtr priv = conn->privateData; + + priv->configDir = XM_CONFIG_DIR; + + priv->configCache = virHashCreate(50); + if (!priv->configCache) + return (-1); + priv->nameConfigMap = virHashCreate(50); + if (!priv->nameConfigMap) { + virHashFree(priv->configCache, NULL); + priv->configCache = NULL; + return (-1); } - nconnections++; + /* Force the cache to be reloaded next time that + * xenXMConfigCacheRefresh is called. + */ + priv->lastRefresh = 0; return (0); } @@ -615,14 +586,12 @@ xenXMOpen (virConnectPtr conn ATTRIBUTE_ * Free the config files in the cache if this is the * last connection */ -int xenXMClose(virConnectPtr conn ATTRIBUTE_UNUSED) { - nconnections--; - if (nconnections <= 0) { - virHashFree(nameConfigMap, NULL); - nameConfigMap = NULL; - virHashFree(configCache, xenXMConfigFree); - configCache = NULL; - } +int xenXMClose(virConnectPtr conn) { + xenUnifiedPrivatePtr priv = conn->privateData; + + virHashFree(priv->nameConfigMap, NULL); + virHashFree(priv->configCache, xenXMConfigFree); + return (0); } @@ -631,6 +600,7 @@ int xenXMClose(virConnectPtr conn ATTRIB * VCPUs and memory. */ int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { @@ -642,10 +612,12 @@ int xenXMDomainGetInfo(virDomainPtr doma if (domain->id != -1) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); memset(info, 0, sizeof(virDomainInfo)); @@ -1310,6 +1282,7 @@ no_memory: * domain, suitable for later feeding for virDomainCreateXML */ char *xenXMDomainDumpXML(virDomainPtr domain, int flags) { + xenUnifiedPrivatePtr priv = domain->conn->privateData; const char *filename; xenXMConfCachePtr entry; @@ -1321,10 +1294,12 @@ char *xenXMDomainDumpXML(virDomainPtr do if (domain->id != -1) return (NULL); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (NULL); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (NULL); return virDomainDefFormat(domain->conn, entry->def, flags); @@ -1335,6 +1310,7 @@ char *xenXMDomainDumpXML(virDomainPtr do * Update amount of memory in the config file */ int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; @@ -1350,10 +1326,12 @@ int xenXMDomainSetMemory(virDomainPtr do if (memory < 1024 * MIN_XEN_GUEST_SIZE) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); entry->def->memory = memory; @@ -1373,6 +1351,7 @@ int xenXMDomainSetMemory(virDomainPtr do * Update maximum memory limit in config */ int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; @@ -1386,10 +1365,12 @@ int xenXMDomainSetMaxMemory(virDomainPtr if (domain->id != -1) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); entry->def->maxmem = memory; @@ -1409,6 +1390,7 @@ int xenXMDomainSetMaxMemory(virDomainPtr * Get max memory limit from config */ unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; @@ -1420,10 +1402,12 @@ unsigned long xenXMDomainGetMaxMemory(vi if (domain->id != -1) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); return entry->def->maxmem; @@ -1433,6 +1417,7 @@ unsigned long xenXMDomainGetMaxMemory(vi * Set the VCPU count in config */ int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; @@ -1446,10 +1431,12 @@ int xenXMDomainSetVcpus(virDomainPtr dom if (domain->id != -1) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); entry->def->vcpus = vcpus; @@ -1478,6 +1465,7 @@ int xenXMDomainPinVcpu(virDomainPtr doma unsigned int vcpu ATTRIBUTE_UNUSED, unsigned char *cpumap, int maplen) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; virBuffer mapbuf = VIR_BUFFER_INITIALIZER; @@ -1504,11 +1492,13 @@ int xenXMDomainPinVcpu(virDomainPtr doma return -1; } - if (!(filename = virHashLookup(nameConfigMap, domain->name))) { + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) { xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("virHashLookup")); return -1; } - if (!(entry = virHashLookup(configCache, filename))) { + if (!(entry = virHashLookup(priv->configCache, filename))) { xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("can't retrieve config file for domain")); return -1; @@ -1563,6 +1553,7 @@ int xenXMDomainPinVcpu(virDomainPtr doma * Find an inactive domain based on its name */ virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; virDomainPtr ret; @@ -1576,15 +1567,17 @@ virDomainPtr xenXMDomainLookupByName(vir return (NULL); } + priv = conn->privateData; + #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) return (NULL); #endif - if (!(filename = virHashLookup(nameConfigMap, domname))) + if (!(filename = virHashLookup(priv->nameConfigMap, domname))) return (NULL); - if (!(entry = virHashLookup(configCache, filename))) { + if (!(entry = virHashLookup(priv->configCache, filename))) { return (NULL); } @@ -1618,6 +1611,7 @@ static int xenXMDomainSearchForUUID(cons */ virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { + xenUnifiedPrivatePtr priv; xenXMConfCachePtr entry; virDomainPtr ret; @@ -1630,12 +1624,14 @@ virDomainPtr xenXMDomainLookupByUUID(vir return (NULL); } + priv = conn->privateData; + #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) return (NULL); #endif - if (!(entry = virHashSearch(configCache, xenXMDomainSearchForUUID, (const void *)uuid))) { + if (!(entry = virHashSearch(priv->configCache, xenXMDomainSearchForUUID, (const void *)uuid))) { return (NULL); } @@ -1666,10 +1662,10 @@ int xenXMDomainCreate(virDomainPtr domai if (domain->id != -1) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); if (!(sexpr = xenDaemonFormatSxpr(domain->conn, entry->def, priv->xendConfigVersion))) { @@ -2294,16 +2290,16 @@ virDomainPtr xenXMDomainDefineXML(virCon VIR_DOMAIN_XML_INACTIVE))) return (NULL); - if (virHashLookup(nameConfigMap, def->name)) { + if (virHashLookup(priv->nameConfigMap, def->name)) { /* domain exists, we will overwrite it */ - if (!(oldfilename = (char *)virHashLookup(nameConfigMap, def->name))) { + if (!(oldfilename = (char *)virHashLookup(priv->nameConfigMap, def->name))) { xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("can't retrieve config filename for domain to overwrite")); goto error; } - if (!(entry = virHashLookup(configCache, oldfilename))) { + if (!(entry = virHashLookup(priv->configCache, oldfilename))) { xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("can't retrieve config entry for domain to overwrite")); goto error; @@ -2314,14 +2310,14 @@ virDomainPtr xenXMDomainDefineXML(virCon goto error; /* Remove the name -> filename mapping */ - if (virHashRemoveEntry(nameConfigMap, def->name, NULL) < 0) { + if (virHashRemoveEntry(priv->nameConfigMap, def->name, NULL) < 0) { xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("failed to remove old domain from config map")); goto error; } /* Remove the config record itself */ - if (virHashRemoveEntry(configCache, oldfilename, xenXMConfigFree) < 0) { + if (virHashRemoveEntry(priv->configCache, oldfilename, xenXMConfigFree) < 0) { xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("failed to remove old domain from config map")); goto error; @@ -2330,13 +2326,13 @@ virDomainPtr xenXMDomainDefineXML(virCon entry = NULL; } - if ((strlen(configDir) + 1 + strlen(def->name) + 1) > PATH_MAX) { + if ((strlen(priv->configDir) + 1 + strlen(def->name) + 1) > PATH_MAX) { xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("config file name is too long")); goto error; } - strcpy(filename, configDir); + strcpy(filename, priv->configDir); strcat(filename, "/"); strcat(filename, def->name); @@ -2357,14 +2353,14 @@ virDomainPtr xenXMDomainDefineXML(virCon memmove(entry->filename, filename, PATH_MAX); entry->def = def; - if (virHashAddEntry(configCache, filename, entry) < 0) { + if (virHashAddEntry(priv->configCache, filename, entry) < 0) { xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("unable to store config file handle")); goto error; } - if (virHashAddEntry(nameConfigMap, def->name, entry->filename) < 0) { - virHashRemoveEntry(configCache, filename, NULL); + if (virHashAddEntry(priv->nameConfigMap, def->name, entry->filename) < 0) { + virHashRemoveEntry(priv->configCache, filename, NULL); xenXMError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("unable to store config file handle")); goto error; @@ -2387,6 +2383,7 @@ virDomainPtr xenXMDomainDefineXML(virCon * Delete a domain from disk */ int xenXMDomainUndefine(virDomainPtr domain) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { @@ -2400,21 +2397,23 @@ int xenXMDomainUndefine(virDomainPtr dom if (domain->conn->flags & VIR_CONNECT_RO) return (-1); - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1); - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1); if (unlink(entry->filename) < 0) return (-1); /* Remove the name -> filename mapping */ - if (virHashRemoveEntry(nameConfigMap, domain->name, NULL) < 0) + if (virHashRemoveEntry(priv->nameConfigMap, domain->name, NULL) < 0) return(-1); /* Remove the config record itself */ - if (virHashRemoveEntry(configCache, entry->filename, xenXMConfigFree) < 0) + if (virHashRemoveEntry(priv->configCache, entry->filename, xenXMConfigFree) < 0) return (-1); return (0); @@ -2449,6 +2448,7 @@ static void xenXMListIterator(const void * are currently running */ int xenXMListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { + xenUnifiedPrivatePtr priv; struct xenXMListIteratorContext ctx; if (!VIR_IS_CONNECT(conn)) { @@ -2456,20 +2456,22 @@ int xenXMListDefinedDomains(virConnectPt return (-1); } + priv = conn->privateData; + #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) return (-1); #endif - if (maxnames > virHashSize(configCache)) - maxnames = virHashSize(configCache); + if (maxnames > virHashSize(priv->configCache)) + maxnames = virHashSize(priv->configCache); ctx.conn = conn; ctx.count = 0; ctx.max = maxnames; ctx.names = names; - virHashForEach(nameConfigMap, xenXMListIterator, &ctx); + virHashForEach(priv->nameConfigMap, xenXMListIterator, &ctx); return (ctx.count); } @@ -2478,17 +2480,21 @@ int xenXMListDefinedDomains(virConnectPt * based on number running */ int xenXMNumOfDefinedDomains(virConnectPtr conn) { + xenUnifiedPrivatePtr priv; + if (!VIR_IS_CONNECT(conn)) { xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); return (-1); } + priv = conn->privateData; + #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) return (-1); #endif - return virHashSize(nameConfigMap); + return virHashSize(priv->nameConfigMap); } @@ -2524,9 +2530,9 @@ xenXMDomainAttachDevice(virDomainPtr dom if (domain->id != -1) return -1; - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return -1; - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return -1; def = entry->def; @@ -2613,9 +2619,9 @@ xenXMDomainDetachDevice(virDomainPtr dom return -1; if (domain->id != -1) return -1; - if (!(filename = virHashLookup(nameConfigMap, domain->name))) + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return -1; - if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return -1; def = entry->def; diff --git a/src/xm_internal.h b/src/xm_internal.h --- a/src/xm_internal.h +++ b/src/xm_internal.h @@ -31,10 +31,9 @@ #include "domain_conf.h" extern struct xenUnifiedDriver xenXMDriver; -int xenXMInit (void); virHashTablePtr xenXMGetConfigCache(void); -char *xenXMGetConfigDir(void); + int xenXMConfigCacheRefresh (virConnectPtr conn); int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename); int xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:44:00PM +0000, Daniel P. Berrange wrote:
The 'xm' driver currently keeps all its state in a global static variables. Not cool, since we access this from all sorts of places and its hard to guarentee thread safety. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though.
Undecided whether this is a big problem or not. If so, I'll can try and redo the next thread locking patch to use a lock over the existing global state.
xen_inotify.c | 4 xen_unified.c | 1 xen_unified.h | 14 ++- xm_internal.c | 262 +++++++++++++++++++++++++++++----------------------------- xm_internal.h | 3 5 files changed, 149 insertions(+), 135 deletions(-)
The patch appears straightforward enough, and for the XM driver (ie. ancient history Xen) I'm sure we don't care much about a tiny bit of extra memory. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

On Thu, Jan 15, 2009 at 05:23:33PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:44:00PM +0000, Daniel P. Berrange wrote:
The 'xm' driver currently keeps all its state in a global static variables. Not cool, since we access this from all sorts of places and its hard to guarentee thread safety. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though.
Undecided whether this is a big problem or not. If so, I'll can try and redo the next thread locking patch to use a lock over the existing global state.
xen_inotify.c | 4 xen_unified.c | 1 xen_unified.h | 14 ++- xm_internal.c | 262 +++++++++++++++++++++++++++++----------------------------- xm_internal.h | 3 5 files changed, 149 insertions(+), 135 deletions(-)
The patch appears straightforward enough, and for the XM driver (ie. ancient history Xen) I'm sure we don't care much about a tiny bit of extra memory.
I don't care about the memory usage at this point, I'm more concerned about inserting bugs in that code. Looks a relatively mechanical change and it's probably safer than something based on locking. +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The 'xm' driver currently keeps all its state in a global static variables. Not cool, since we access this from all sorts of places and its hard to guarentee thread safety. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though.
Undecided whether this is a big problem or not. If so, I'll can try and redo the next thread locking patch to use a lock over the existing global state.
Looks fine, modulo a potential NULL dereference and an obsolete comment. ...
typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; diff --git a/src/xm_internal.c b/src/xm_internal.c ... @@ -615,14 +586,12 @@ xenXMOpen (virConnectPtr conn ATTRIBUTE_ * Free the config files in the cache if this is the * last connection */
It'd be good to remove or reword the comment above. Mentioning "last connection" doesn't make sense anymore, since the hash table is now per-connection.
-int xenXMClose(virConnectPtr conn ATTRIBUTE_UNUSED) { - nconnections--; - if (nconnections <= 0) { - virHashFree(nameConfigMap, NULL); - nameConfigMap = NULL; - virHashFree(configCache, xenXMConfigFree); - configCache = NULL; - } +int xenXMClose(virConnectPtr conn) {
Shouldn't this function be "static"? Same with xenXMOpen.
+ xenUnifiedPrivatePtr priv = conn->privateData; + + virHashFree(priv->nameConfigMap, NULL); + virHashFree(priv->configCache, xenXMConfigFree); + return (0); } @@ -631,6 +600,7 @@ int xenXMClose(virConnectPtr conn ATTRIB * VCPUs and memory. */ int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) { + xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { @@ -642,10 +612,12 @@ int xenXMDomainGetInfo(virDomainPtr doma if (domain->id != -1) return (-1);
- if (!(filename = virHashLookup(nameConfigMap, domain->name))) + priv = domain->conn->privateData; + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) return (-1);
- if (!(entry = virHashLookup(configCache, filename))) + if (!(entry = virHashLookup(priv->configCache, filename))) return (-1);
memset(info, 0, sizeof(virDomainInfo)); @@ -1310,6 +1282,7 @@ no_memory: * domain, suitable for later feeding for virDomainCreateXML */ char *xenXMDomainDumpXML(virDomainPtr domain, int flags) { + xenUnifiedPrivatePtr priv = domain->conn->privateData;
dereferencing "domain" here can't be right, because immediately after the above line, we see these tests for NULL domain and domain->conn: if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } Besides, "priv" is already initialized in the next hunk (below). IME, best is just to move the declaration down to the first use. That's better from a maintainability standpoint, since then there's no risk of accidentally using "priv" between the declaration and first use.

On Mon, Jan 19, 2009 at 04:07:33PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The 'xm' driver currently keeps all its state in a global static variables. Not cool, since we access this from all sorts of places and its hard to guarentee thread safety. So we move the state into the virConnect object. This will increase memory usage if a single process has multiple Xen connections open though.
Undecided whether this is a big problem or not. If so, I'll can try and redo the next thread locking patch to use a lock over the existing global state.
Looks fine, modulo a potential NULL dereference and an obsolete comment.
...
typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; diff --git a/src/xm_internal.c b/src/xm_internal.c ... @@ -615,14 +586,12 @@ xenXMOpen (virConnectPtr conn ATTRIBUTE_ * Free the config files in the cache if this is the * last connection */
It'd be good to remove or reword the comment above. Mentioning "last connection" doesn't make sense anymore, since the hash table is now per-connection.
-int xenXMClose(virConnectPtr conn ATTRIBUTE_UNUSED) { - nconnections--; - if (nconnections <= 0) { - virHashFree(nameConfigMap, NULL); - nameConfigMap = NULL; - virHashFree(configCache, xenXMConfigFree); - configCache = NULL; - } +int xenXMClose(virConnectPtr conn) {
Shouldn't this function be "static"? Same with xenXMOpen.
Probably - for historical reasons many of the Xen functions are pre-declared in the header files, because they used to be called directly before the days of the generic driver API. Should make most of them static now really.
@@ -1310,6 +1282,7 @@ no_memory: * domain, suitable for later feeding for virDomainCreateXML */ char *xenXMDomainDumpXML(virDomainPtr domain, int flags) { + xenUnifiedPrivatePtr priv = domain->conn->privateData;
dereferencing "domain" here can't be right, because immediately after the above line, we see these tests for NULL domain and domain->conn:
if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); }
Besides, "priv" is already initialized in the next hunk (below). IME, best is just to move the declaration down to the first use. That's better from a maintainability standpoint, since then there's no risk of accidentally using "priv" between the declaration and first use.
The giant if (....) chcek for NULL here is really totally unneccessary. This method is only ever called from the driver APIs and the caller already does exactly this check, so repeating it is just cluttering up the code. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

This patch makes the various Xen drivers threadsafe by adding a mutex lock on the xenUnifiedPrivatePtr object. The XenD driver does not really need much locking, since it usually just calls out to XenD. Likewise the Xen driver just makes hypercalls. Locks are needed for libxenstore access, and the xm/inotify drivers since they have shared state src/proxy_internal.c | 173 ++++++++++++++++----------------- src/xen_inotify.c | 25 +++- src/xen_internal.c | 29 +++-- src/xen_unified.c | 176 +++++++++++++++++++++------------- src/xen_unified.h | 36 +++++-- src/xend_internal.c | 37 ++++++- src/xm_internal.c | 256 +++++++++++++++++++++++++++++++++----------------- src/xs_internal.c | 130 +++++++++++++++++++------ src/xs_internal.h | 7 - tests/sexpr2xmltest.c | 19 +++ 10 files changed, 575 insertions(+), 313 deletions(-) Daniel diff --git a/src/proxy_internal.c b/src/proxy_internal.c --- a/src/proxy_internal.c +++ b/src/proxy_internal.c @@ -33,7 +33,7 @@ #define STANDALONE -static int debug = 0; +#define VIR_FROM_THIS VIR_FROM_PROXY static int xenProxyClose(virConnectPtr conn); static virDrvOpenStatus xenProxyOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); @@ -149,19 +149,18 @@ virProxyForkServer(void) const char *proxyarg[2]; if (!proxyPath) { - fprintf(stderr, "failed to find libvirt_proxy\n"); + VIR_WARN0("failed to find libvirt_proxy\n"); return(-1); } - if (debug) - fprintf(stderr, "Asking to launch %s\n", proxyPath); + VIR_DEBUG("Asking to launch %s\n", proxyPath); proxyarg[0] = proxyPath; proxyarg[1] = NULL; if (virExec(NULL, proxyarg, NULL, NULL, &pid, -1, NULL, NULL, VIR_EXEC_DAEMON) < 0) - fprintf(stderr, "Failed to fork libvirt_proxy\n"); + VIR_ERROR0("Failed to fork libvirt_proxy\n"); /* * do a waitpid on the intermediate process to avoid zombies. @@ -226,32 +225,33 @@ retry: return (-1); } - if (debug > 0) - fprintf(stderr, "connected to unix socket %s via %d\n", path, fd); + DEBUG("connected to unix socket %s via %d\n", path, fd); return (fd); } /** - * virProxyCloseClientSocket: - * @fd: the file descriptor for the socket + * virProxyCloseSocket: + * @priv: the Xen proxy data * - * Close the socket from that client + * Close the socket from that client. The caller must + * hold the lock on 'priv' before calling * * Returns 0 in case of success and -1 in case of error */ static int -virProxyCloseClientSocket(int fd) { +virProxyCloseSocket(xenUnifiedPrivatePtr priv) { int ret; - if (fd < 0) + if (priv->proxy < 0) return(-1); - ret = close(fd); + ret = close(priv->proxy); if (ret != 0) - fprintf(stderr, _("Failed to close socket %d\n"), fd); - else if (debug > 0) - fprintf(stderr, "Closed socket %d\n", fd); + VIR_WARN(_("Failed to close socket %d\n"), priv->proxy); + else + VIR_DEBUG("Closed socket %d\n", priv->proxy); + priv->proxy = -1; return(ret); } @@ -260,14 +260,13 @@ virProxyCloseClientSocket(int fd) { * @fd: the socket * @buffer: the target memory area * @len: the length in bytes - * @quiet: quiet access * * Process a read from a client socket * * Returns the number of byte read or -1 in case of error. */ static int -virProxyReadClientSocket(int fd, char *buffer, int len, int quiet) { +virProxyReadClientSocket(int fd, char *buffer, int len) { int ret; if ((fd < 0) || (buffer == NULL) || (len < 0)) @@ -277,18 +276,15 @@ retry: ret = read(fd, buffer, len); if (ret < 0) { if (errno == EINTR) { - if (debug > 0) - fprintf(stderr, "read socket %d interrupted\n", fd); + VIR_DEBUG("read socket %d interrupted\n", fd); goto retry; } - if (!quiet) - fprintf(stderr, _("Failed to read socket %d\n"), fd); + VIR_WARN("Failed to read socket %d\n", fd); return(-1); } - if (debug) - fprintf(stderr, "read %d bytes from socket %d\n", - ret, fd); + VIR_DEBUG("read %d bytes from socket %d\n", + ret, fd); return(ret); } @@ -309,12 +305,11 @@ virProxyWriteClientSocket(int fd, const ret = safewrite(fd, data, len); if (ret < 0) { - fprintf(stderr, _("Failed to write to socket %d\n"), fd); + VIR_WARN(_("Failed to write to socket %d\n"), fd); return(-1); } - if (debug) - fprintf(stderr, "wrote %d bytes to socket %d\n", - len, fd); + VIR_DEBUG("wrote %d bytes to socket %d\n", + len, fd); return(0); } @@ -347,12 +342,9 @@ xenProxyClose(virConnectPtr conn) return -1; } - /* Fail silently. */ - if (priv->proxy == -1) - return -1; - - virProxyCloseClientSocket (priv->proxy); - priv->proxy = -1; + xenUnifiedLock(priv); + virProxyCloseSocket (priv); + xenUnifiedUnlock(priv); return 0; } @@ -376,9 +368,11 @@ xenProxyCommand(virConnectPtr conn, virP return -1; } + xenUnifiedLock(priv); + /* Fail silently. */ if (priv->proxy == -1) - return -1; + goto error; /* * normal communication serial numbers are in 0..4095 @@ -390,62 +384,69 @@ xenProxyCommand(virConnectPtr conn, virP request->serial = serial; ret = virProxyWriteClientSocket(priv->proxy, (const char *) request, request->len); - if (ret < 0) - return(-1); + if (ret < 0) { + if (!quiet) + virReportSystemError(conn, errno, "%s", + _("failed to write proxy request")); + goto error; + } retry: if (answer == NULL) { /* read in situ */ ret = virProxyReadClientSocket(priv->proxy, (char *) request, - sizeof(virProxyPacket), quiet); - if (ret < 0) - return(-1); + sizeof(virProxyPacket)); + if (ret < 0) { + if (!quiet) + virReportSystemError(conn, errno, "%s", + _("failed to read proxy reply")); + goto error; + } if (ret != sizeof(virProxyPacket)) { - fprintf(stderr, - _("Communication error with proxy: got %d bytes of %d\n"), - ret, (int) sizeof(virProxyPacket)); - xenProxyClose(conn); - return(-1); + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes of %d\n"), + ret, (int) sizeof(virProxyPacket)); + goto error; } res = request; if (res->len != sizeof(virProxyPacket)) { - fprintf(stderr, - _("Communication error with proxy: expected %d bytes got %d\n"), - (int) sizeof(virProxyPacket), res->len); - xenProxyClose(conn); - return(-1); + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: expected %d bytes got %d\n"), + (int) sizeof(virProxyPacket), res->len); + goto error; } } else { /* read in packet provided */ ret = virProxyReadClientSocket(priv->proxy, (char *) answer, - sizeof(virProxyPacket), quiet); - if (ret < 0) - return(-1); + sizeof(virProxyPacket)); + if (ret < 0) { + if (!quiet) + virReportSystemError(conn, errno, "%s", + _("failed to read proxy reply")); + goto error; + } if (ret != sizeof(virProxyPacket)) { - fprintf(stderr, - _("Communication error with proxy: got %d bytes of %d\n"), - ret, (int) sizeof(virProxyPacket)); - xenProxyClose(conn); - return(-1); + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes of %d\n"), + ret, (int) sizeof(virProxyPacket)); + goto error; } res = (virProxyPacketPtr) answer; if ((res->len < sizeof(virProxyPacket)) || (res->len > sizeof(virProxyFullPacket))) { - fprintf(stderr, - _("Communication error with proxy: got %d bytes packet\n"), - res->len); - xenProxyClose(conn); - return(-1); + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes packet\n"), + res->len); + goto error; } if (res->len > sizeof(virProxyPacket)) { ret = virProxyReadClientSocket(priv->proxy, (char *) &(answer->extra.arg[0]), - res->len - ret, quiet); + res->len - ret); if (ret != (int) (res->len - sizeof(virProxyPacket))) { - fprintf(stderr, - _("Communication error with proxy: got %d bytes of %d\n"), - ret, (int) sizeof(virProxyPacket)); - xenProxyClose(conn); - return(-1); + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes of %d\n"), + ret, (int) sizeof(virProxyPacket)); + goto error; } } } @@ -454,17 +455,22 @@ retry: */ if ((res == NULL) || (res->version != PROXY_PROTO_VERSION) || (res->len < sizeof(virProxyPacket))) { - fprintf(stderr, "%s", - _("Communication error with proxy: malformed packet\n")); - xenProxyClose(conn); - return(-1); + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: malformed packet\n")); + goto error; } if (res->serial != serial) { - TODO /* Asynchronous communication */ - fprintf(stderr, _("got asynchronous packet number %d\n"), res->serial); + VIR_WARN(_("got asynchronous packet number %d\n"), res->serial); goto retry; } - return(0); + + xenUnifiedUnlock(priv); + return 0; + +error: + virProxyCloseSocket(priv); + xenUnifiedUnlock(priv); + return -1; } /** @@ -507,7 +513,6 @@ xenProxyOpen(virConnectPtr conn, ret = xenProxyCommand(conn, &req, NULL, 1); if ((ret < 0) || (req.command != VIR_PROXY_NONE)) { virProxyError(NULL, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - xenProxyClose(conn); return(-1); } return(0); @@ -549,7 +554,6 @@ xenProxyGetVersion(virConnectPtr conn, u req.len = sizeof(req); ret = xenProxyCommand(conn, &req, NULL, 0); if (ret < 0) { - xenProxyClose(conn); return(-1); } *hvVer = req.data.larg; @@ -587,7 +591,6 @@ xenProxyListDomains(virConnectPtr conn, req.len = sizeof(req); ret = xenProxyCommand(conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(conn); return(-1); } nb = ans.data.arg; @@ -627,7 +630,6 @@ xenProxyNumOfDomains(virConnectPtr conn) req.len = sizeof(req); ret = xenProxyCommand(conn, &req, NULL, 0); if (ret < 0) { - xenProxyClose(conn); return(-1); } return(req.data.arg); @@ -659,7 +661,6 @@ xenProxyDomainGetDomMaxMemory(virConnect req.len = sizeof(req); ret = xenProxyCommand(conn, &req, NULL, 0); if (ret < 0) { - xenProxyClose(conn); return(0); } return(req.data.larg); @@ -724,7 +725,6 @@ xenProxyDomainGetInfo(virDomainPtr domai req.len = sizeof(req); ret = xenProxyCommand(domain->conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(domain->conn); return(-1); } if (ans.len != sizeof(virProxyPacket) + sizeof(virDomainInfo)) { @@ -769,7 +769,6 @@ xenProxyLookupByID(virConnectPtr conn, i req.len = sizeof(req); ret = xenProxyCommand(conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(conn); return(NULL); } if (ans.data.arg == -1) { @@ -814,7 +813,6 @@ xenProxyLookupByUUID(virConnectPtr conn, ret = xenProxyCommand(conn, (virProxyPacketPtr) &req, &req, 0); if (ret < 0) { - xenProxyClose(conn); return(NULL); } if (req.data.arg == -1) { @@ -861,7 +859,6 @@ xenProxyLookupByName(virConnectPtr conn, strcpy(&req.extra.str[0], name); ret = xenProxyCommand(conn, (virProxyPacketPtr) &req, &req, 0); if (ret < 0) { - xenProxyClose(conn); return(NULL); } if (req.data.arg == -1) { @@ -901,7 +898,6 @@ xenProxyNodeGetInfo(virConnectPtr conn, req.len = sizeof(req); ret = xenProxyCommand(conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(conn); return(-1); } if (ans.data.arg == -1) { @@ -941,7 +937,6 @@ xenProxyGetCapabilities (virConnectPtr c req.len = sizeof(req); ret = xenProxyCommand(conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(conn); return NULL; } if (ans.data.arg == -1) @@ -995,7 +990,6 @@ xenProxyDomainDumpXML(virDomainPtr domai req.len = sizeof(req); ret = xenProxyCommand(domain->conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(domain->conn); return(NULL); } if (ans.len <= sizeof(virProxyPacket)) { @@ -1044,7 +1038,6 @@ xenProxyDomainGetOSType(virDomainPtr dom req.len = sizeof(req); ret = xenProxyCommand(domain->conn, &req, &ans, 0); if (ret < 0) { - xenProxyClose(domain->conn); return(NULL); } if ((ans.len == sizeof(virProxyPacket)) && (ans.data.arg < 0)) { diff --git a/src/xen_inotify.c b/src/xen_inotify.c --- a/src/xen_inotify.c +++ b/src/xen_inotify.c @@ -92,11 +92,13 @@ struct xenUnifiedDriver xenInotifyDriver }; static int -xenInotifyXenCacheLookup(const char *filename, +xenInotifyXenCacheLookup(virConnectPtr conn, + const char *filename, char **name, unsigned char *uuid) { + xenUnifiedPrivatePtr priv = conn->privateData; xenXMConfCachePtr entry; - if (!(entry = virHashLookup(xenXMGetConfigCache(), filename))) { + if (!(entry = virHashLookup(priv->configCache, filename))) { DEBUG("No config found for %s", filename); return -1; } @@ -172,7 +174,7 @@ xenInotifyDomainLookup(virConnectPtr con char **name, unsigned char *uuid) { xenUnifiedPrivatePtr priv = conn->privateData; if (priv->useXenConfigCache) - return xenInotifyXenCacheLookup(filename, name, uuid); + return xenInotifyXenCacheLookup(conn, filename, name, uuid); else return xenInotifyXendDomainsDirLookup(conn, filename, name, uuid); } @@ -296,25 +298,27 @@ xenInotifyEvent(int watch ATTRIBUTE_UNUS return; } + xenUnifiedLock(priv); + reread: got = read(fd, buf, sizeof(buf)); if (got == -1) { if (errno == EINTR) goto reread; - return; + goto cleanup; } tmp = buf; while (got) { if (got < sizeof(struct inotify_event)) - return; /* bad */ + goto cleanup; /* bad */ e = (struct inotify_event *)tmp; tmp += sizeof(struct inotify_event); got -= sizeof(struct inotify_event); if (got < e->len) - return; + goto cleanup; tmp += e->len; got -= e->len; @@ -338,14 +342,14 @@ reread: if (xenInotifyRemoveDomainConfigInfo(conn, fname) < 0 ) { virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("Error adding file to config cache")); - return; + goto cleanup; } } else if (e->mask & ( IN_CREATE | IN_CLOSE_WRITE | IN_MOVED_TO) ) { virDomainEventPtr event; if (xenInotifyAddDomainConfigInfo(conn, fname) < 0 ) { virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("Error adding file to config cache")); - return; + goto cleanup; } event = xenInotifyDomainEventFromFile(conn, fname, @@ -361,6 +365,9 @@ reread: } } + +cleanup: + xenUnifiedUnlock(priv); } /** @@ -456,7 +463,7 @@ xenInotifyOpen(virConnectPtr conn ATTRIB DEBUG0("Failed to add inotify handle, disabling events"); } - conn->refs++; + virConnectRef(conn); return 0; } diff --git a/src/xen_internal.c b/src/xen_internal.c --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -1330,9 +1330,14 @@ xenHypervisorDomainBlockStats (virDomain { #ifdef __linux__ xenUnifiedPrivatePtr priv; + int ret; priv = (xenUnifiedPrivatePtr) dom->conn->privateData; - return xenLinuxDomainBlockStats (priv, dom, path, stats); + xenUnifiedLock(priv); + /* Need to lock because it hits the xenstore handle :-( */ + ret = xenLinuxDomainBlockStats (priv, dom, path, stats); + xenUnifiedUnlock(priv); + return ret; #else virXenErrorFunc (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__, "block statistics not supported on this platform", @@ -2627,8 +2632,10 @@ xenHypervisorLookupDomainByID(virConnect if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id) return (NULL); - - if (!(name = xenStoreDomainGetName(conn, id))) + xenUnifiedLock(priv); + name = xenStoreDomainGetName(conn, id); + xenUnifiedUnlock(priv); + if (!name) return (NULL); ret = virGetDomain(conn, name, XEN_GETDOMAININFO_UUID(dominfo)); @@ -2695,7 +2702,10 @@ xenHypervisorLookupDomainByUUID(virConne if (id == -1) return (NULL); - if (!(name = xenStoreDomainGetName(conn, id))) + xenUnifiedLock(priv); + name = xenStoreDomainGetName(conn, id); + xenUnifiedUnlock(priv); + if (!name) return (NULL); ret = virGetDomain(conn, name, uuid); @@ -2928,7 +2938,6 @@ xenHypervisorNodeGetCellsFreeMemory(virC xen_op_v2_sys op_sys; int i, j, ret; xenUnifiedPrivatePtr priv; - int nbNodeCells; if (conn == NULL) { virXenErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, @@ -2936,14 +2945,15 @@ xenHypervisorNodeGetCellsFreeMemory(virC return -1; } - nbNodeCells = xenNbCells(conn); - if (nbNodeCells < 0) { + priv = conn->privateData; + + if (priv->nbNodeCells < 0) { virXenErrorFunc (conn, VIR_ERR_XEN_CALL, __FUNCTION__, "cannot determine actual number of cells",0); return(-1); } - if ((maxCells < 1) || (startCell >= nbNodeCells)) { + if ((maxCells < 1) || (startCell >= priv->nbNodeCells)) { virXenErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, "invalid argument", 0); return -1; @@ -2958,7 +2968,6 @@ xenHypervisorNodeGetCellsFreeMemory(virC return -1; } - priv = (xenUnifiedPrivatePtr) conn->privateData; if (priv->handle < 0) { virXenErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, "priv->handle invalid", 0); @@ -2968,7 +2977,7 @@ xenHypervisorNodeGetCellsFreeMemory(virC memset(&op_sys, 0, sizeof(op_sys)); op_sys.cmd = XEN_V2_OP_GETAVAILHEAP; - for (i = startCell, j = 0;(i < nbNodeCells) && (j < maxCells);i++,j++) { + for (i = startCell, j = 0;(i < priv->nbNodeCells) && (j < maxCells);i++,j++) { op_sys.u.availheap.node = i; ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); if (ret < 0) { diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -56,7 +56,7 @@ xenUnifiedDomainGetVcpus (virDomainPtr d unsigned char *cpumaps, int maplen); /* The five Xen drivers below us. */ -static struct xenUnifiedDriver *drivers[XEN_UNIFIED_NR_DRIVERS] = { +static struct xenUnifiedDriver const * const drivers[XEN_UNIFIED_NR_DRIVERS] = { [XEN_UNIFIED_HYPERVISOR_OFFSET] = &xenHypervisorDriver, [XEN_UNIFIED_PROXY_OFFSET] = &xenProxyDriver, [XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver, @@ -71,14 +71,6 @@ static struct xenUnifiedDriver *drivers[ virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) -/* - * Helper functions currently used in the NUMA code - * Those variables should not be accessed directly but through helper - * functions xenNbCells() and xenNbCpu() available to all Xen backends - */ -static int nbNodeCells = -1; -static int nbNodeCpus = -1; - /** * xenNumaInit: * @conn: pointer to the hypervisor connection @@ -90,42 +82,19 @@ static int nbNodeCpus = -1; static void xenNumaInit(virConnectPtr conn) { virNodeInfo nodeInfo; + xenUnifiedPrivatePtr priv; int ret; ret = xenUnifiedNodeGetInfo(conn, &nodeInfo); if (ret < 0) return; - nbNodeCells = nodeInfo.nodes; - nbNodeCpus = nodeInfo.cpus; + + priv = conn->privateData; + + priv->nbNodeCells = nodeInfo.nodes; + priv->nbNodeCpus = nodeInfo.cpus; } -/** - * xenNbCells: - * @conn: pointer to the hypervisor connection - * - * Number of NUMA cells present in the actual Node - * - * Returns the number of NUMA cells available on that Node - */ -int xenNbCells(virConnectPtr conn) { - if (nbNodeCells < 0) - xenNumaInit(conn); - return(nbNodeCells); -} - -/** - * xenNbCpus: - * @conn: pointer to the hypervisor connection - * - * Number of CPUs present in the actual Node - * - * Returns the number of CPUs available on that Node - */ -int xenNbCpus(virConnectPtr conn) { - if (nbNodeCpus < 0) - xenNumaInit(conn); - return(nbNodeCpus); -} /** * xenDomainUsedCpus: @@ -141,7 +110,7 @@ char * xenDomainUsedCpus(virDomainPtr dom) { char *res = NULL; - int nb_cpu, ncpus; + int ncpus; int nb_vcpu; char *cpulist = NULL; unsigned char *cpumap = NULL; @@ -150,12 +119,14 @@ xenDomainUsedCpus(virDomainPtr dom) int n, m; virVcpuInfoPtr cpuinfo = NULL; virNodeInfo nodeinfo; + xenUnifiedPrivatePtr priv; if (!VIR_IS_CONNECTED_DOMAIN(dom)) return (NULL); - nb_cpu = xenNbCpus(dom->conn); - if (nb_cpu <= 0) + priv = dom->conn->privateData; + + if (priv->nbNodeCpus <= 0) return(NULL); nb_vcpu = xenUnifiedDomainGetMaxVcpus(dom); if (nb_vcpu <= 0) @@ -163,7 +134,7 @@ xenDomainUsedCpus(virDomainPtr dom) if (xenUnifiedNodeGetInfo(dom->conn, &nodeinfo) < 0) return(NULL); - if (VIR_ALLOC_N(cpulist, nb_cpu) < 0) + if (VIR_ALLOC_N(cpulist, priv->nbNodeCpus) < 0) goto done; if (VIR_ALLOC_N(cpuinfo, nb_vcpu) < 0) goto done; @@ -175,19 +146,19 @@ xenDomainUsedCpus(virDomainPtr dom) if ((ncpus = xenUnifiedDomainGetVcpus(dom, cpuinfo, nb_vcpu, cpumap, cpumaplen)) >= 0) { for (n = 0 ; n < ncpus ; n++) { - for (m = 0 ; m < nb_cpu; m++) { + for (m = 0 ; m < priv->nbNodeCpus; m++) { if ((cpulist[m] == 0) && (VIR_CPU_USABLE(cpumap, cpumaplen, n, m))) { cpulist[m] = 1; nb++; /* if all CPU are used just return NULL */ - if (nb == nb_cpu) + if (nb == priv->nbNodeCpus) goto done; } } } - res = virDomainCpuSetFormat(dom->conn, cpulist, nb_cpu); + res = virDomainCpuSetFormat(dom->conn, cpulist, priv->nbNodeCpus); } done: @@ -267,13 +238,22 @@ xenUnifiedOpen (virConnectPtr conn, virC xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating private data"); return VIR_DRV_OPEN_ERROR; } - conn->privateData = priv; + if (virMutexInit(&priv->lock) < 0) { + xenUnifiedError (NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot initialise mutex")); + VIR_FREE(priv); + return VIR_DRV_OPEN_ERROR; + } /* Allocate callback list */ if (VIR_ALLOC(cbList) < 0) { xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating callback list"); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); return VIR_DRV_OPEN_ERROR; } + conn->privateData = priv; + priv->domainEventCallbacks = cbList; priv->handle = -1; @@ -344,6 +324,8 @@ xenUnifiedOpen (virConnectPtr conn, virC } } + xenNumaInit(conn); + if (!(priv->caps = xenHypervisorMakeCapabilities(conn))) { DEBUG0("Failed to make capabilities"); goto fail; @@ -368,7 +350,9 @@ clean: DEBUG0("Failed to activate a mandatory sub-driver"); for (i = 0 ; i < XEN_UNIFIED_NR_DRIVERS ; i++) if (priv->opened[i]) drivers[i]->close(conn); + virMutexDestroy(&priv->lock); VIR_FREE(priv); + conn->privateData = NULL; return ret; } @@ -388,7 +372,8 @@ xenUnifiedClose (virConnectPtr conn) if (priv->opened[i] && drivers[i]->close) (void) drivers[i]->close (conn); - free (conn->privateData); + virMutexDestroy(&priv->lock); + VIR_FREE(conn->privateData); conn->privateData = NULL; return 0; @@ -497,17 +482,15 @@ xenUnifiedNodeGetInfo (virConnectPtr con static char * xenUnifiedGetCapabilities (virConnectPtr conn) { - GET_PRIVATE(conn); - int i; - char *ret; + xenUnifiedPrivatePtr priv = conn->privateData; + char *xml; - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->getCapabilities) { - ret = drivers[i]->getCapabilities (conn); - if (ret) return ret; - } + if (!(xml = virCapabilitiesFormatXML(priv->caps))) { + virReportOOMError(conn); + return NULL; + } - return NULL; + return xml; } static int @@ -1019,7 +1002,9 @@ xenUnifiedDomainDumpXML (virDomainPtr do } else { if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { char *cpus, *res; + xenUnifiedLock(priv); cpus = xenDomainUsedCpus(dom); + xenUnifiedUnlock(priv); res = xenDaemonDomainDumpXML(dom, flags, cpus); VIR_FREE(cpus); return(res); @@ -1363,14 +1348,20 @@ xenUnifiedDomainEventRegister (virConnec void (*freefunc)(void *)) { GET_PRIVATE (conn); + int ret; + + xenUnifiedLock(priv); if (priv->xsWatch == -1) { xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + xenUnifiedUnlock(priv); return -1; } - conn->refs++; - return virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks, - callback, opaque, freefunc); + virConnectRef(conn); + ret = virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks, + callback, opaque, freefunc); + xenUnifiedUnlock(priv); + return ret; } static int @@ -1379,14 +1370,22 @@ xenUnifiedDomainEventDeregister (virConn { int ret; GET_PRIVATE (conn); + + xenUnifiedLock(priv); if (priv->xsWatch == -1) { xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + xenUnifiedUnlock(priv); return -1; } - ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks, - callback); + if (priv->domainEventDispatching) + ret = virDomainEventCallbackListMarkDelete(conn, priv->domainEventCallbacks, + callback); + else + ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks, + callback); virUnrefConnect(conn); + xenUnifiedUnlock(priv); return ret; } @@ -1573,21 +1572,66 @@ xenUnifiedRemoveDomainInfo(xenUnifiedDom return -1; } +static void +xenUnifiedDomainEventDispatchFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventCallback cb, + void *cbopaque, + void *opaque) +{ + xenUnifiedPrivatePtr priv = opaque; + + /* + * Release the lock while the callback is running so that + * we're re-entrant safe for callback work - the callback + * may want to invoke other virt functions & we have already + * protected the one piece of state we have - the callback + * list + */ + xenUnifiedUnlock(priv); + virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL); + xenUnifiedLock(priv); +} + /** * xenUnifiedDomainEventDispatch: + * @priv: the connection to dispatch events on + * @event: the event to dispatch * * Dispatch domain events to registered callbacks * + * The caller must hold the lock in 'priv' before invoking + * */ void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, virDomainEventPtr event) { - if (!priv || !priv->domainEventCallbacks) + if (!priv) return; - virDomainEventDispatch(event, - priv->domainEventCallbacks, - virDomainEventDispatchDefaultFunc, - NULL); + priv->domainEventDispatching = 1; + + if (priv->domainEventCallbacks) { + virDomainEventDispatch(event, + priv->domainEventCallbacks, + xenUnifiedDomainEventDispatchFunc, + priv); + + /* Purge any deleted callbacks */ + virDomainEventCallbackListPurgeMarked(priv->domainEventCallbacks); + } + virDomainEventFree(event); + + priv->domainEventDispatching = 0; } + +void xenUnifiedLock(xenUnifiedPrivatePtr priv) +{ + virMutexLock(&priv->lock); +} + +void xenUnifiedUnlock(xenUnifiedPrivatePtr priv) +{ + virMutexUnlock(&priv->lock); +} diff --git a/src/xen_unified.h b/src/xen_unified.h --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -131,6 +131,12 @@ typedef xenUnifiedDomainInfoList *xenUni * low-level drivers access parts of this structure. */ struct _xenUnifiedPrivate { + virMutex lock; + + /* These initial vars are initialized in Open method + * and readonly thereafter, so can be used without + * holding the lock + */ virCapsPtr caps; int handle; /* Xen hypervisor handle */ @@ -144,25 +150,36 @@ struct _xenUnifiedPrivate { struct sockaddr_un addr_un; /* the unix address */ struct sockaddr_in addr_in; /* the inet address */ - struct xs_handle *xshandle; /* handle to talk to the xenstore */ - - int proxy; /* fd of proxy. */ - /* Keep track of the drivers which opened. We keep a yes/no flag * here for each driver, corresponding to the array drivers in * xen_unified.c. */ int opened[XEN_UNIFIED_NR_DRIVERS]; + + /* + * Everything from this point onwards must be protected + * by the lock when used + */ + + struct xs_handle *xshandle; /* handle to talk to the xenstore */ + + int proxy; /* fd of proxy. */ + + /* A list of xenstore watches */ xenStoreWatchListPtr xsWatchList; int xsWatch; /* A list of active domain name/uuids */ xenUnifiedDomainInfoListPtr activeDomainList; + /* NUMA topology info cache */ + int nbNodeCells; + int nbNodeCpus; /* An list of callbacks */ virDomainEventCallbackListPtr domainEventCallbacks; + int domainEventDispatching; /* Location of config files, either /etc * or /var/lib/xen */ @@ -188,9 +205,6 @@ struct _xenUnifiedPrivate { typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; - -int xenNbCells(virConnectPtr conn); -int xenNbCpus(virConnectPtr conn); char *xenDomainUsedCpus(virDomainPtr dom); void xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr info); @@ -204,4 +218,12 @@ void xenUnifiedDomainEventDispatch (xenU virDomainEventPtr event); unsigned long xenUnifiedVersion(void); +#ifndef PROXY +void xenUnifiedLock(xenUnifiedPrivatePtr priv); +void xenUnifiedUnlock(xenUnifiedPrivatePtr priv); +#else +#define xenUnifiedLock(p) do {} while(0) +#define xenUnifiedUnlock(p) do {} while(0) +#endif + #endif /* __VIR_XEN_UNIFIED_H__ */ diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -1964,18 +1964,25 @@ xenDaemonParseSxprGraphicsOld(virConnect int hvm, int xendConfigVersion) { +#ifndef PROXY + xenUnifiedPrivatePtr priv = conn->privateData; +#endif const char *tmp; virDomainGraphicsDefPtr graphics = NULL; if ((tmp = sexpr_fmt_node(root, "domain/image/%s/vnc", hvm ? "hvm" : "linux")) && tmp[0] == '1') { /* Graphics device (HVM, or old (pre-3.0.4) style PV VNC config) */ - int port = xenStoreDomainGetVNCPort(conn, def->id); + int port; const char *listenAddr = sexpr_fmt_node(root, "domain/image/%s/vnclisten", hvm ? "hvm" : "linux"); const char *vncPasswd = sexpr_fmt_node(root, "domain/image/%s/vncpasswd", hvm ? "hvm" : "linux"); const char *keymap = sexpr_fmt_node(root, "domain/image/%s/keymap", hvm ? "hvm" : "linux"); const char *unused = sexpr_fmt_node(root, "domain/image/%s/vncunused", hvm ? "hvm" : "linux"); + xenUnifiedLock(priv); + port = xenStoreDomainGetVNCPort(conn, def->id); + xenUnifiedUnlock(priv); + if (VIR_ALLOC(graphics) < 0) goto no_memory; @@ -2040,6 +2047,9 @@ xenDaemonParseSxprGraphicsNew(virConnect virDomainDefPtr def, const struct sexpr *root) { +#ifndef PROXY + xenUnifiedPrivatePtr priv = conn->privateData; +#endif virDomainGraphicsDefPtr graphics = NULL; const struct sexpr *cur, *node; const char *tmp; @@ -2071,16 +2081,21 @@ xenDaemonParseSxprGraphicsNew(virConnect !(graphics->data.sdl.xauth = strdup(xauth))) goto no_memory; } else { - int port = xenStoreDomainGetVNCPort(conn, def->id); - if (port == -1) { - // Didn't find port entry in xenstore - port = sexpr_int(node, "device/vfb/vncdisplay"); - } + int port; const char *listenAddr = sexpr_node(node, "device/vfb/vnclisten"); const char *vncPasswd = sexpr_node(node, "device/vfb/vncpasswd");; const char *keymap = sexpr_node(node, "device/vfb/keymap"); const char *unused = sexpr_node(node, "device/vfb/vncunused"); + xenUnifiedLock(priv); + port = xenStoreDomainGetVNCPort(conn, def->id); + xenUnifiedUnlock(priv); + + if (port == -1) { + // Didn't find port entry in xenstore + port = sexpr_int(node, "device/vfb/vncdisplay"); + } + if ((unused && STREQ(unused, "1")) || port == -1) { graphics->data.vnc.autoport = 1; port = -1; @@ -2137,6 +2152,9 @@ xenDaemonParseSxpr(virConnectPtr conn, int xendConfigVersion, const char *cpus) { +#ifndef PROXY + xenUnifiedPrivatePtr priv = conn->privateData; +#endif const char *tmp; virDomainDefPtr def; int hvm = 0; @@ -2356,7 +2374,9 @@ xenDaemonParseSxpr(virConnectPtr conn, goto error; /* Character device config */ + xenUnifiedLock(priv); tty = xenStoreDomainGetConsolePath(conn, def->id); + xenUnifiedUnlock(priv); if (hvm) { tmp = sexpr_node(root, "domain/image/hvm/serial"); if (tmp && STRNEQ(tmp, "none")) { @@ -5464,14 +5484,17 @@ virDomainXMLDevID(virDomainPtr domain, char *ref, int ref_len) { + xenUnifiedPrivatePtr priv = domain->conn->privateData; char *xref; if (dev->type == VIR_DOMAIN_DEVICE_DISK) { strcpy(class, "vbd"); if (dev->data.disk->dst == NULL) return -1; + xenUnifiedLock(priv); xref = xenStoreDomainGetDiskID(domain->conn, domain->id, dev->data.disk->dst); + xenUnifiedUnlock(priv); if (xref == NULL) return -1; @@ -5487,8 +5510,10 @@ virDomainXMLDevID(virDomainPtr domain, strcpy(class, "vif"); + xenUnifiedLock(priv); xref = xenStoreDomainGetNetworkID(domain->conn, domain->id, mac); + xenUnifiedUnlock(priv); if (xref == NULL) return -1; diff --git a/src/xm_internal.c b/src/xm_internal.c --- a/src/xm_internal.c +++ b/src/xm_internal.c @@ -339,6 +339,11 @@ xenXMConfigSaveFile(virConnectPtr conn, return ret; } + +/* + * Caller must hold the lock on 'conn->privateData' before + * calling this funtion + */ int xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename) @@ -359,6 +364,10 @@ xenXMConfigCacheRemoveFile(virConnectPtr } +/* + * Caller must hold the lock on 'conn->privateData' before + * calling this funtion + */ int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename) { @@ -452,10 +461,14 @@ xenXMConfigCacheAddFile(virConnectPtr co } /* This method is called by various methods to scan /etc/xen - (or whatever directory was set by LIBVIRT_XM_CONFIG_DIR - environment variable) and process any domain configs. It - has rate-limited so never rescans more frequently than - once every X seconds */ + * (or whatever directory was set by LIBVIRT_XM_CONFIG_DIR + * environment variable) and process any domain configs. It + * has rate-limited so never rescans more frequently than + * once every X seconds + * + * Caller must hold the lock on 'conn->privateData' before + * calling this funtion + */ int xenXMConfigCacheRefresh (virConnectPtr conn) { xenUnifiedPrivatePtr priv = conn->privateData; DIR *dh; @@ -613,12 +626,13 @@ int xenXMDomainGetInfo(virDomainPtr doma return (-1); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto error; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto error; memset(info, 0, sizeof(virDomainInfo)); info->maxMem = entry->def->maxmem; @@ -627,8 +641,12 @@ int xenXMDomainGetInfo(virDomainPtr doma info->state = VIR_DOMAIN_SHUTOFF; info->cpuTime = 0; + xenUnifiedUnlock(priv); return (0); +error: + xenUnifiedUnlock(priv); + return -1; } #define MAX_VFB 1024 @@ -1285,6 +1303,7 @@ char *xenXMDomainDumpXML(virDomainPtr do xenUnifiedPrivatePtr priv = domain->conn->privateData; const char *filename; xenXMConfCachePtr entry; + char *ret = NULL; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, @@ -1295,14 +1314,19 @@ char *xenXMDomainDumpXML(virDomainPtr do return (NULL); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (NULL); + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return (NULL); + goto cleanup; - return virDomainDefFormat(domain->conn, entry->def, flags); + ret = virDomainDefFormat(domain->conn, entry->def, flags); + +cleanup: + xenUnifiedUnlock(priv); + return ret; } @@ -1313,6 +1337,7 @@ int xenXMDomainSetMemory(virDomainPtr do xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; + int ret = -1; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, @@ -1327,12 +1352,13 @@ int xenXMDomainSetMemory(virDomainPtr do return (-1); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto cleanup; entry->def->memory = memory; if (entry->def->memory > entry->def->maxmem) @@ -1342,9 +1368,12 @@ int xenXMDomainSetMemory(virDomainPtr do * in-memory representation of the config file. I say not! */ if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - return (-1); + goto cleanup; + ret = 0; - return (0); +cleanup: + xenUnifiedUnlock(priv); + return ret; } /* @@ -1354,6 +1383,7 @@ int xenXMDomainSetMaxMemory(virDomainPtr xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; + int ret = -1; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, @@ -1366,12 +1396,13 @@ int xenXMDomainSetMaxMemory(virDomainPtr return (-1); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto cleanup; entry->def->maxmem = memory; if (entry->def->memory > entry->def->maxmem) @@ -1381,9 +1412,12 @@ int xenXMDomainSetMaxMemory(virDomainPtr * in-memory representation of the config file. I say not! */ if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - return (-1); + goto cleanup; + ret = 0; - return (0); +cleanup: + xenUnifiedUnlock(priv); + return ret; } /* @@ -1393,24 +1427,30 @@ unsigned long xenXMDomainGetMaxMemory(vi xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; + unsigned long ret = 0; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); + return (0); } if (domain->id != -1) - return (-1); + return (0); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto cleanup; - return entry->def->maxmem; + ret = entry->def->maxmem; + +cleanup: + xenUnifiedUnlock(priv); + return ret; } /* @@ -1420,6 +1460,7 @@ int xenXMDomainSetVcpus(virDomainPtr dom xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; + int ret = -1; if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, @@ -1432,12 +1473,13 @@ int xenXMDomainSetVcpus(virDomainPtr dom return (-1); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto cleanup; entry->def->vcpus = vcpus; @@ -1445,9 +1487,12 @@ int xenXMDomainSetVcpus(virDomainPtr dom * in-memory representation of the config file. I say not! */ if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - return (-1); + goto cleanup; + ret = 0; - return (0); +cleanup: + xenUnifiedUnlock(priv); + return ret; } /** @@ -1493,15 +1538,16 @@ int xenXMDomainPinVcpu(virDomainPtr doma } priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) { xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("virHashLookup")); - return -1; + goto cleanup; } if (!(entry = virHashLookup(priv->configCache, filename))) { xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("can't retrieve config file for domain")); - return -1; + goto cleanup; } /* from bit map, build character string of mapped CPU numbers */ @@ -1519,7 +1565,7 @@ int xenXMDomainPinVcpu(virDomainPtr doma if (virBufferError(&mapbuf)) { virReportOOMError(domain->conn); - return -1; + goto cleanup; } mapstr = virBufferContentAndReset(&mapbuf); @@ -1546,6 +1592,7 @@ int xenXMDomainPinVcpu(virDomainPtr doma cleanup: VIR_FREE(mapstr); VIR_FREE(cpuset); + xenUnifiedUnlock(priv); return (ret); } @@ -1556,7 +1603,7 @@ virDomainPtr xenXMDomainLookupByName(vir xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; - virDomainPtr ret; + virDomainPtr ret = NULL; if (!VIR_IS_CONNECT(conn)) { xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -1568,27 +1615,28 @@ virDomainPtr xenXMDomainLookupByName(vir } priv = conn->privateData; + xenUnifiedLock(priv); #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) - return (NULL); + goto cleanup; #endif if (!(filename = virHashLookup(priv->nameConfigMap, domname))) - return (NULL); + goto cleanup; - if (!(entry = virHashLookup(priv->configCache, filename))) { - return (NULL); - } + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; - if (!(ret = virGetDomain(conn, domname, entry->def->uuid))) { - return (NULL); - } + if (!(ret = virGetDomain(conn, domname, entry->def->uuid))) + goto cleanup; /* Ensure its marked inactive, because may be cached handle to a previously active domain */ ret->id = -1; +cleanup: + xenUnifiedUnlock(priv); return (ret); } @@ -1613,7 +1661,7 @@ virDomainPtr xenXMDomainLookupByUUID(vir const unsigned char *uuid) { xenUnifiedPrivatePtr priv; xenXMConfCachePtr entry; - virDomainPtr ret; + virDomainPtr ret = NULL; if (!VIR_IS_CONNECT(conn)) { xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -1625,24 +1673,25 @@ virDomainPtr xenXMDomainLookupByUUID(vir } priv = conn->privateData; + xenUnifiedLock(priv); #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) - return (NULL); + goto cleanup; #endif - if (!(entry = virHashSearch(priv->configCache, xenXMDomainSearchForUUID, (const void *)uuid))) { - return (NULL); - } + if (!(entry = virHashSearch(priv->configCache, xenXMDomainSearchForUUID, (const void *)uuid))) + goto cleanup; - if (!(ret = virGetDomain(conn, entry->def->name, uuid))) { - return (NULL); - } + if (!(ret = virGetDomain(conn, entry->def->name, uuid))) + goto cleanup; /* Ensure its marked inactive, because may be cached handle to a previously active domain */ ret->id = -1; +cleanup: + xenUnifiedUnlock(priv); return (ret); } @@ -1652,7 +1701,7 @@ virDomainPtr xenXMDomainLookupByUUID(vir */ int xenXMDomainCreate(virDomainPtr domain) { char *sexpr; - int ret; + int ret = -1; xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; @@ -1662,43 +1711,45 @@ int xenXMDomainCreate(virDomainPtr domai if (domain->id != -1) return (-1); + xenUnifiedLock(priv); + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto error; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto error; if (!(sexpr = xenDaemonFormatSxpr(domain->conn, entry->def, priv->xendConfigVersion))) { xenXMError(domain->conn, VIR_ERR_XML_ERROR, "%s", _("failed to build sexpr")); - return (-1); + goto error; } ret = xenDaemonDomainCreateXML(domain->conn, sexpr); VIR_FREE(sexpr); - if (ret != 0) { - return (-1); - } + if (ret != 0) + goto error; if ((ret = xenDaemonDomainLookupByName_ids(domain->conn, domain->name, - entry->def->uuid)) < 0) { - return (-1); - } + entry->def->uuid)) < 0) + goto error; domain->id = ret; if ((ret = xend_wait_for_devices(domain->conn, domain->name)) < 0) - goto cleanup; + goto error; if ((ret = xenDaemonDomainResume(domain)) < 0) - goto cleanup; + goto error; + xenUnifiedUnlock(priv); return (0); - cleanup: + error: if (domain->id != -1) { xenDaemonDomainDestroy(domain); domain->id = -1; } + xenUnifiedUnlock(priv); return (-1); } @@ -2281,14 +2332,20 @@ virDomainPtr xenXMDomainDefineXML(virCon if (conn->flags & VIR_CONNECT_RO) return (NULL); + xenUnifiedLock(priv); + #ifndef WITH_XEN_INOTIFY - if (xenXMConfigCacheRefresh (conn) < 0) + if (xenXMConfigCacheRefresh (conn) < 0) { + xenUnifiedUnlock(priv); return (NULL); + } #endif if (!(def = virDomainDefParseString(conn, priv->caps, xml, - VIR_DOMAIN_XML_INACTIVE))) + VIR_DOMAIN_XML_INACTIVE))) { + xenUnifiedUnlock(priv); return (NULL); + } if (virHashLookup(priv->nameConfigMap, def->name)) { /* domain exists, we will overwrite it */ @@ -2366,16 +2423,16 @@ virDomainPtr xenXMDomainDefineXML(virCon goto error; } - if (!(ret = virGetDomain(conn, def->name, def->uuid))) - return NULL; + if ((ret = virGetDomain(conn, def->name, def->uuid))) + ret->id = -1; - ret->id = -1; - + xenUnifiedUnlock(priv); return (ret); error: VIR_FREE(entry); virDomainDefFree(def); + xenUnifiedUnlock(priv); return (NULL); } @@ -2386,6 +2443,8 @@ int xenXMDomainUndefine(virDomainPtr dom xenUnifiedPrivatePtr priv; const char *filename; xenXMConfCachePtr entry; + int ret = -1; + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, __FUNCTION__); @@ -2398,25 +2457,30 @@ int xenXMDomainUndefine(virDomainPtr dom return (-1); priv = domain->conn->privateData; + xenUnifiedLock(priv); if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return (-1); + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return (-1); + goto cleanup; if (unlink(entry->filename) < 0) - return (-1); + goto cleanup; /* Remove the name -> filename mapping */ if (virHashRemoveEntry(priv->nameConfigMap, domain->name, NULL) < 0) - return(-1); + goto cleanup; /* Remove the config record itself */ if (virHashRemoveEntry(priv->configCache, entry->filename, xenXMConfigFree) < 0) - return (-1); + goto cleanup; - return (0); + ret = 0; + +cleanup: + xenUnifiedUnlock(priv); + return ret; } struct xenXMListIteratorContext { @@ -2450,6 +2514,7 @@ static void xenXMListIterator(const void int xenXMListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { xenUnifiedPrivatePtr priv; struct xenXMListIteratorContext ctx; + int ret = -1; if (!VIR_IS_CONNECT(conn)) { xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -2457,10 +2522,11 @@ int xenXMListDefinedDomains(virConnectPt } priv = conn->privateData; + xenUnifiedLock(priv); #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) - return (-1); + goto cleanup; #endif if (maxnames > virHashSize(priv->configCache)) @@ -2472,7 +2538,13 @@ int xenXMListDefinedDomains(virConnectPt ctx.names = names; virHashForEach(priv->nameConfigMap, xenXMListIterator, &ctx); - return (ctx.count); + ret = ctx.count; + +#ifndef WITH_XEN_INOTIFY +cleanup: +#endif + xenUnifiedUnlock(priv); + return ret; } /* @@ -2481,6 +2553,7 @@ int xenXMListDefinedDomains(virConnectPt */ int xenXMNumOfDefinedDomains(virConnectPtr conn) { xenUnifiedPrivatePtr priv; + int ret = -1; if (!VIR_IS_CONNECT(conn)) { xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); @@ -2488,13 +2561,20 @@ int xenXMNumOfDefinedDomains(virConnectP } priv = conn->privateData; + xenUnifiedLock(priv); #ifndef WITH_XEN_INOTIFY if (xenXMConfigCacheRefresh (conn) < 0) - return (-1); + goto cleanup; #endif - return virHashSize(priv->nameConfigMap); + ret = virHashSize(priv->nameConfigMap); + +#ifndef WITH_XEN_INOTIFY +cleanup: +#endif + xenUnifiedUnlock(priv); + return ret; } @@ -2523,24 +2603,25 @@ xenXMDomainAttachDevice(virDomainPtr dom return -1; } - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (domain->conn->flags & VIR_CONNECT_RO) return -1; if (domain->id != -1) return -1; + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + xenUnifiedLock(priv); + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return -1; + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return -1; + goto cleanup; def = entry->def; if (!(dev = virDomainDeviceDefParse(domain->conn, priv->caps, entry->def, xml, VIR_DOMAIN_XML_INACTIVE))) - return -1; + goto cleanup; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -2583,7 +2664,7 @@ xenXMDomainAttachDevice(virDomainPtr dom cleanup: virDomainDeviceDefFree(dev); - + xenUnifiedUnlock(priv); return ret; } @@ -2613,23 +2694,25 @@ xenXMDomainDetachDevice(virDomainPtr dom return -1; } - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (domain->conn->flags & VIR_CONNECT_RO) return -1; if (domain->id != -1) return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + xenUnifiedLock(priv); + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - return -1; + goto cleanup; if (!(entry = virHashLookup(priv->configCache, filename))) - return -1; + goto cleanup; def = entry->def; if (!(dev = virDomainDeviceDefParse(domain->conn, priv->caps, entry->def, xml, VIR_DOMAIN_XML_INACTIVE))) - return -1; + goto cleanup; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -2681,6 +2764,7 @@ xenXMDomainDetachDevice(virDomainPtr dom cleanup: virDomainDeviceDefFree(dev); + xenUnifiedUnlock(priv); return (ret); } diff --git a/src/xs_internal.c b/src/xs_internal.c --- a/src/xs_internal.c +++ b/src/xs_internal.c @@ -37,16 +37,10 @@ #include "xs_internal.h" #include "xen_internal.h" /* for xenHypervisorCheckID */ -#ifdef __linux__ -#define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd" -#elif defined(__sun) -#define XEN_HYPERVISOR_SOCKET "/dev/xen/privcmd" -#else -#error "unsupported platform" -#endif - #ifndef PROXY static char *xenStoreDomainGetOSType(virDomainPtr domain); +static void xenStoreWatchEvent(int watch, int fd, int events, void *data); +static void xenStoreWatchListFree(xenStoreWatchListPtr list); struct xenUnifiedDriver xenStoreDriver = { xenStoreOpen, /* open */ @@ -374,6 +368,7 @@ xenStoreClose(virConnectPtr conn) priv = (xenUnifiedPrivatePtr) conn->privateData; +#ifndef PROXY if (xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain") < 0) { DEBUG0("Warning, could not remove @introduceDomain watch"); /* not fatal */ @@ -385,7 +380,6 @@ xenStoreClose(virConnectPtr conn) } xenStoreWatchListFree(priv->xsWatchList); -#ifndef PROXY xenUnifiedDomainInfoListFree(priv->activeDomainList); #endif if (priv->xshandle == NULL) @@ -516,17 +510,21 @@ xenStoreDomainGetMaxMemory(virDomainPtr { char *tmp; unsigned long ret = 0; + xenUnifiedPrivatePtr priv; if (!VIR_IS_CONNECTED_DOMAIN(domain)) return (ret); if (domain->id == -1) return(-1); + priv = domain->conn->privateData; + xenUnifiedLock(priv); tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target"); if (tmp != NULL) { ret = (unsigned long) atol(tmp); - free(tmp); + VIR_FREE(tmp); } + xenUnifiedUnlock(priv); return(ret); } @@ -589,12 +587,14 @@ xenStoreListDomains(virConnectPtr conn, } priv = (xenUnifiedPrivatePtr) conn->privateData; + + xenUnifiedLock(priv); if (priv->xshandle == NULL) - return(-1); + goto error; idlist = xs_directory (priv->xshandle, 0, "/local/domain", &num); if (idlist == NULL) - return(-1); + goto error; for (ret = 0, i = 0; (i < num) && (ret < maxids); i++) { id = strtol(idlist[i], &endptr, 10); @@ -609,7 +609,12 @@ xenStoreListDomains(virConnectPtr conn, ids[ret++] = (int) id; } free(idlist); + xenUnifiedUnlock(priv); return(ret); + +error: + xenUnifiedUnlock(priv); + return -1; } /** @@ -694,6 +699,9 @@ done: int xenStoreDomainShutdown(virDomainPtr domain) { + int ret; + xenUnifiedPrivatePtr priv; + if ((domain == NULL) || (domain->conn == NULL)) { virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, __FUNCTION__); @@ -705,7 +713,11 @@ xenStoreDomainShutdown(virDomainPtr doma * this is very hackish, the domU kernel probes for a special * node in the xenstore and launch the shutdown command if found. */ - return(virDomainDoStoreWrite(domain, "control/shutdown", "poweroff")); + priv = domain->conn->privateData; + xenUnifiedLock(priv); + ret = virDomainDoStoreWrite(domain, "control/shutdown", "poweroff"); + xenUnifiedUnlock(priv); + return ret; } /** @@ -722,6 +734,9 @@ xenStoreDomainShutdown(virDomainPtr doma int xenStoreDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED) { + int ret; + xenUnifiedPrivatePtr priv; + if ((domain == NULL) || (domain->conn == NULL)) { virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, __FUNCTION__); @@ -733,7 +748,11 @@ xenStoreDomainReboot(virDomainPtr domain * this is very hackish, the domU kernel probes for a special * node in the xenstore and launch the shutdown command if found. */ - return(virDomainDoStoreWrite(domain, "control/shutdown", "reboot")); + priv = domain->conn->privateData; + xenUnifiedLock(priv); + ret = virDomainDoStoreWrite(domain, "control/shutdown", "reboot"); + xenUnifiedUnlock(priv); + return ret; } /* @@ -757,8 +776,11 @@ xenStoreDomainGetOSType(virDomainPtr dom vm = virDomainGetVM(domain); if (vm) { + xenUnifiedPrivatePtr priv = domain->conn->privateData; + xenUnifiedLock(priv); str = virDomainGetVMInfo(domain, vm, "image/ostype"); - free(vm); + xenUnifiedUnlock(priv); + VIR_FREE(vm); } return (str); @@ -773,6 +795,9 @@ xenStoreDomainGetOSType(virDomainPtr dom * Return the port number on which the domain is listening for VNC * connections. * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * * Returns the port number, -1 in case of error */ int xenStoreDomainGetVNCPort(virConnectPtr conn, int domid) { @@ -801,6 +826,9 @@ int xenStoreDomainGetVNCPort * Returns the path to the serial console. It is the callers * responsibilty to free() the return string. Returns NULL * on error + * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. */ char * xenStoreDomainGetConsolePath(virConnectPtr conn, int domid) { return virDomainDoStoreQuery(conn, domid, "console/tty"); @@ -814,6 +842,9 @@ char * xenStoreDomainGetConsole * * Get the type of domain operation system. * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * * Returns the new string or NULL in case of error, the string must be * freed by the caller. */ @@ -858,6 +889,9 @@ xenStoreDomainGetOSTypeID(virConnectPtr * Get the reference (i.e. the string number) for the device on that domain * which uses the given mac address * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * * Returns the new string or NULL in case of error, the string must be * freed by the caller. */ @@ -910,6 +944,9 @@ xenStoreDomainGetNetworkID(virConnectPtr * Get the reference (i.e. the string number) for the device on that domain * which uses the given virtual block device name * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * * Returns the new string or NULL in case of error, the string must be * freed by the caller. */ @@ -973,6 +1010,10 @@ xenStoreDomainGetDiskID(virConnectPtr co return (NULL); } +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ char *xenStoreDomainGetName(virConnectPtr conn, int id) { char prop[200]; @@ -989,6 +1030,10 @@ char *xenStoreDomainGetName(virConnectPt } #ifndef PROXY +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ int xenStoreDomainGetUUID(virConnectPtr conn, int id, unsigned char *uuid) { @@ -1015,9 +1060,9 @@ int xenStoreDomainGetUUID(virConnectPtr return ret; } -#endif //PROXY -void xenStoreWatchListFree(xenStoreWatchListPtr list) +static void +xenStoreWatchListFree(xenStoreWatchListPtr list) { int i; for (i=0; i<list->count; i++) { @@ -1028,6 +1073,10 @@ void xenStoreWatchListFree(xenStoreWatch VIR_FREE(list); } +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ int xenStoreAddWatch(virConnectPtr conn, const char *path, const char *token, @@ -1080,6 +1129,10 @@ int xenStoreAddWatch(virConnectPtr conn, return xs_watch(priv->xshandle, watch->path, watch->token); } +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ int xenStoreRemoveWatch(virConnectPtr conn, const char *path, const char *token) @@ -1122,18 +1175,17 @@ int xenStoreRemoveWatch(virConnectPtr co ; /* Failure to reduce memory allocation isn't fatal */ } list->count--; -#ifndef PROXY virUnrefConnect(conn); -#endif return 0; } } return -1; } -xenStoreWatchPtr xenStoreFindWatch(xenStoreWatchListPtr list, - const char *path, - const char *token) +static xenStoreWatchPtr +xenStoreFindWatch(xenStoreWatchListPtr list, + const char *path, + const char *token) { int i; for (i = 0 ; i < list->count ; i++) @@ -1144,10 +1196,11 @@ xenStoreWatchPtr xenStoreFindWatch(xenSt return NULL; } -void xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED, - int fd ATTRIBUTE_UNUSED, - int events ATTRIBUTE_UNUSED, - void *data) +static void +xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int events ATTRIBUTE_UNUSED, + void *data) { char **event; char *path; @@ -1158,11 +1211,15 @@ void xenStoreWatchEvent(int watch ATTRIB virConnectPtr conn = data; xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; if(!priv) return; - if(!priv->xshandle) return; + + xenUnifiedLock(priv); + + if(!priv->xshandle) + goto cleanup; event = xs_read_watch(priv->xshandle, &stringCount); if (!event) - return; + goto cleanup; path = event[XS_WATCH_PATH]; token = event[XS_WATCH_TOKEN]; @@ -1171,10 +1228,17 @@ void xenStoreWatchEvent(int watch ATTRIB if( sw ) sw->cb(conn, path, token, sw->opaque); VIR_FREE(event); + +cleanup: + xenUnifiedUnlock(priv); } -#ifndef PROXY -/* The domain callback for the @introduceDomain watch */ + +/* + * The domain callback for the @introduceDomain watch + * + * The lock on 'priv' is held when calling this + */ int xenStoreDomainIntroduced(virConnectPtr conn, const char *path ATTRIBUTE_UNUSED, const char *token ATTRIBUTE_UNUSED, @@ -1249,7 +1313,11 @@ retry: return 0; } -/* The domain callback for the @destroyDomain watch */ +/* + * The domain callback for the @destroyDomain watch + * + * The lock on 'priv' is held when calling this + */ int xenStoreDomainReleased(virConnectPtr conn, const char *path ATTRIBUTE_UNUSED, const char *token ATTRIBUTE_UNUSED, diff --git a/src/xs_internal.h b/src/xs_internal.h --- a/src/xs_internal.h +++ b/src/xs_internal.h @@ -78,8 +78,6 @@ typedef struct _xenStoreWatchList xenSto typedef xenStoreWatchList *xenStoreWatchListPtr; -void xenStoreWatchListFree(xenStoreWatchListPtr head); - int xenStoreAddWatch(virConnectPtr conn, const char *path, const char *token, @@ -88,11 +86,6 @@ int xenStoreAddWatch(virConn int xenStoreRemoveWatch(virConnectPtr conn, const char *path, const char *token); -xenStoreWatchPtr xenStoreFindWatch(xenStoreWatchListPtr list, - const char *path, - const char *token); - -void xenStoreWatchEvent(int watch, int fd, int events, void *data); /* domain events */ int xenStoreDomainIntroduced(virConnectPtr conn, diff --git a/tests/sexpr2xmltest.c b/tests/sexpr2xmltest.c --- a/tests/sexpr2xmltest.c +++ b/tests/sexpr2xmltest.c @@ -5,7 +5,9 @@ #include <unistd.h> #include "internal.h" +#include "datatypes.h" #include "xml.h" +#include "xen_unified.h" #include "xend_internal.h" #include "testutils.h" @@ -23,6 +25,11 @@ static int testCompareFiles(const char * char *sexprPtr = &(sexprData[0]); int ret = -1; virDomainDefPtr def = NULL; + virConnectPtr conn; + struct _xenUnifiedPrivate priv; + + conn = virGetConnect(); + if (!conn) goto fail; if (virtTestLoadFile(xml, &xmlPtr, MAX_FILE) < 0) goto fail; @@ -30,7 +37,15 @@ static int testCompareFiles(const char * if (virtTestLoadFile(sexpr, &sexprPtr, MAX_FILE) < 0) goto fail; - if (!(def = xenDaemonParseSxprString(NULL, sexprData, xendConfigVersion))) + memset(&priv, 0, sizeof(priv)); + /* Many puppies died to bring you this code. */ + priv.xendConfigVersion = xendConfigVersion; + conn->privateData = &priv; + + if (virMutexInit(&priv.lock) < 0) + goto fail; + + if (!(def = xenDaemonParseSxprString(conn, sexprData, xendConfigVersion))) goto fail; if (!(gotxml = virDomainDefFormat(NULL, def, 0))) @@ -46,6 +61,8 @@ static int testCompareFiles(const char * fail: free(gotxml); virDomainDefFree(def); + virUnrefConnect(conn); + virMutexDestroy(&priv.lock); return ret; } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:44:21PM +0000, Daniel P. Berrange wrote:
This patch makes the various Xen drivers threadsafe by adding a mutex lock on the xenUnifiedPrivatePtr object. The XenD driver does not really need much locking, since it usually just calls out to XenD. Likewise the Xen driver just makes hypercalls. Locks are needed for libxenstore access, and the xm/inotify drivers since they have shared state
src/proxy_internal.c | 173 ++++++++++++++++----------------- src/xen_inotify.c | 25 +++- src/xen_internal.c | 29 +++-- src/xen_unified.c | 176 +++++++++++++++++++++------------- src/xen_unified.h | 36 +++++-- src/xend_internal.c | 37 ++++++- src/xm_internal.c | 256 +++++++++++++++++++++++++++++++++----------------- src/xs_internal.c | 130 +++++++++++++++++++------ src/xs_internal.h | 7 - tests/sexpr2xmltest.c | 19 +++ 10 files changed, 575 insertions(+), 313 deletions(-) + /* Many puppies died to bring you this code. */
This code makes my head spin ... Looks reasonable, but to be honest I would _only_ trust automated verification that the locks are being acquired and dropped correctly, and the structure elements are only being used while locks are acquired. The CIL stuff is doing that? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

On Thu, Jan 15, 2009 at 05:29:12PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:44:21PM +0000, Daniel P. Berrange wrote:
This patch makes the various Xen drivers threadsafe by adding a mutex lock on the xenUnifiedPrivatePtr object. The XenD driver does not really need much locking, since it usually just calls out to XenD. Likewise the Xen driver just makes hypercalls. Locks are needed for libxenstore access, and the xm/inotify drivers since they have shared state
src/proxy_internal.c | 173 ++++++++++++++++----------------- src/xen_inotify.c | 25 +++- src/xen_internal.c | 29 +++-- src/xen_unified.c | 176 +++++++++++++++++++++------------- src/xen_unified.h | 36 +++++-- src/xend_internal.c | 37 ++++++- src/xm_internal.c | 256 +++++++++++++++++++++++++++++++++----------------- src/xs_internal.c | 130 +++++++++++++++++++------ src/xs_internal.h | 7 - tests/sexpr2xmltest.c | 19 +++ 10 files changed, 575 insertions(+), 313 deletions(-) + /* Many puppies died to bring you this code. */
This code makes my head spin ... Looks reasonable, but to be honest I
ditto
would _only_ trust automated verification that the locks are being acquired and dropped correctly, and the structure elements are only being used while locks are acquired. The CIL stuff is doing that?
CIL didn't detected all locking bugs in the previous release :-) So far locking bugs seems to have been detected fairly fast, basically the daemon becomes unresponsive and then hooking up a debugger helps finding the stuff out. The problem is that I'm afraid the xen drivers are less tested and have more complex code paths than for the other hypervisors. Still I guess the best is to commit and test as widely as possible is the best way to feel confident about the change, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch makes the various Xen drivers threadsafe by adding a mutex lock on the xenUnifiedPrivatePtr object. The XenD driver does not really need much locking, since it usually just calls out to XenD. Likewise the Xen driver just makes hypercalls. Locks are needed for libxenstore access, and the xm/inotify drivers since they have shared state
src/proxy_internal.c | 173 ++++++++++++++++----------------- src/xen_inotify.c | 25 +++- src/xen_internal.c | 29 +++-- src/xen_unified.c | 176 +++++++++++++++++++++------------- src/xen_unified.h | 36 +++++-- src/xend_internal.c | 37 ++++++- src/xm_internal.c | 256 +++++++++++++++++++++++++++++++++----------------- src/xs_internal.c | 130 +++++++++++++++++++------ src/xs_internal.h | 7 - tests/sexpr2xmltest.c | 19 +++ 10 files changed, 575 insertions(+), 313 deletions(-)
Looks A-ok to me (yes, it was tedious). ACK. For future reference, it'd be nice to separate the largely mechanical things like s/fprintf/VIR_*/ and s/staticVar/priv->staticVar/ from the more, um, let's say "interesting" changes that make you wish for a good, domain-specific static analyzer.

On Mon, Jan 19, 2009 at 06:56:12PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch makes the various Xen drivers threadsafe by adding a mutex lock on the xenUnifiedPrivatePtr object. The XenD driver does not really need much locking, since it usually just calls out to XenD. Likewise the Xen driver just makes hypercalls. Locks are needed for libxenstore access, and the xm/inotify drivers since they have shared state
src/proxy_internal.c | 173 ++++++++++++++++----------------- src/xen_inotify.c | 25 +++- src/xen_internal.c | 29 +++-- src/xen_unified.c | 176 +++++++++++++++++++++------------- src/xen_unified.h | 36 +++++-- src/xend_internal.c | 37 ++++++- src/xm_internal.c | 256 +++++++++++++++++++++++++++++++++----------------- src/xs_internal.c | 130 +++++++++++++++++++------ src/xs_internal.h | 7 - tests/sexpr2xmltest.c | 19 +++ 10 files changed, 575 insertions(+), 313 deletions(-)
Looks A-ok to me (yes, it was tedious). ACK.
Committed now. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

There are a huge list of functions in POSIX which are not safe to use from multiple threads currently. I generated the list by looking at all libc symbol exports for variants which have a parallel _r symbol. nm -D --defined-only /lib/libc.so.6 \ | grep '_r$' \ | awk '{print $3}' \ | grep -v __ \ | grep -v qsort \ | grep -v readdir \ | sort \ | uniq \ | sed -e 's/_r//' The qsort one is a red herring, since you only need qsort_r if you need to pass a extra 'void * opaque' data blob to your sort function - we don't, so don't need qsort_r. The readdir one is also unneccessary, since reading from a single DIR* is safe from a single thread. readdir_r is also horrific http://womble.decadentplace.org.uk/readdir_r-advisory.html This patch adds a 'make sc_prohibit_nonrentrant' rule to the 'syntax-check' for these forbidden functions. .x-sc_prohibit_nonreentrant | 8 ++++ Makefile.am | 2 + Makefile.maint | 11 +++++ Makefile.nonreentrant | 85 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) Daniel diff --git a/.x-sc_prohibit_nonreentrant b/.x-sc_prohibit_nonreentrant new file mode 100644 --- /dev/null +++ b/.x-sc_prohibit_nonreentrant @@ -0,0 +1,8 @@ +^gnulib/ +^po/ +ChangeLog +^Makefile* +^docs/ +^tests/ +^src/virsh.c +^build-aux/ diff --git a/Makefile.am b/Makefile.am --- a/Makefile.am +++ b/Makefile.am @@ -17,6 +17,8 @@ EXTRA_DIST = \ .x-sc_require_config_h_first \ .x-sc_prohibit_strcmp \ .x-sc_require_config_h \ + .x-sc_prohibit_nonreentrant \ + Makefile.nonreentrant \ autogen.sh man_MANS = virsh.1 diff --git a/Makefile.maint b/Makefile.maint --- a/Makefile.maint +++ b/Makefile.maint @@ -12,6 +12,8 @@ VC_LIST = $(srcdir)/build-aux/vc-list-fi VC_LIST_EXCEPT = \ $(VC_LIST) | if test -f .x-$@; then grep -vEf .x-$@; else grep -v ChangeLog; fi + +include Makefile.nonreentrant # Prevent programs like 'sort' from considering distinct strings to be equal. # Doing it here saves us from having to set LC_ALL elsewhere in this file. @@ -110,6 +112,15 @@ sc_prohibit_asprintf: sc_prohibit_asprintf: @grep -nE '\<[a]sprintf\>' $$($(VC_LIST_EXCEPT)) && \ { echo '$(ME): use virAsprintf, not a'sprintf 1>&2; exit 1; } || : + +sc_prohibit_nonreentrant: + @fail=0 ; \ + for i in $(NON_REENTRANT) ; \ + do \ + grep -nE "\<$$i\>[:space:]*\(" $$($(VC_LIST_EXCEPT)) && \ + fail=1 && echo "$(ME): use $${i}_r, not $${i}" || : ; \ + done ; \ + exit $$fail # Using EXIT_SUCCESS as the first argument to error is misleading, # since when that parameter is 0, error does not exit. Use `0' instead. diff --git a/Makefile.nonreentrant b/Makefile.nonreentrant new file mode 100644 --- /dev/null +++ b/Makefile.nonreentrant @@ -0,0 +1,85 @@ + +# +# Generated by running the following on Fedora 9: +# +# nm -D --defined-only /lib/libc.so.6 \ +# | grep '_r$' \ +# | awk '{print $3}' \ +# | grep -v __ \ +# | grep -v qsort \ # Red herring since we don't need to pass extra args to qsort comparator +# | grep -v readdir \ # This is safe as long as each DIR * instance is only used by one thread +# | sort \ +# | uniq \ +# | sed -e 's/_r//' +# + +NON_REENTRANT = +NON_REENTRANT += asctime +NON_REENTRANT += ctime +NON_REENTRANT += drand48 +NON_REENTRANT += ecvt +NON_REENTRANT += erand48 +NON_REENTRANT += ether_aton +NON_REENTRANT += ether_ntoa +NON_REENTRANT += fcvt +NON_REENTRANT += fgetgrent +NON_REENTRANT += fgetpwent +NON_REENTRANT += fgetspent +NON_REENTRANT += getaliasbyname +NON_REENTRANT += getaliasent +NON_REENTRANT += getdate +NON_REENTRANT += getgrent +NON_REENTRANT += getgrgid +NON_REENTRANT += getgrnam +NON_REENTRANT += gethostbyaddr +NON_REENTRANT += gethostbyname2 +NON_REENTRANT += gethostbyname +NON_REENTRANT += gethostent +NON_REENTRANT += getlogin +NON_REENTRANT += getmntent +NON_REENTRANT += getnetbyaddr +NON_REENTRANT += getnetbyname +NON_REENTRANT += getnetent +NON_REENTRANT += getnetgrent +NON_REENTRANT += getprotobyname +NON_REENTRANT += getprotobynumber +NON_REENTRANT += getprotoent +NON_REENTRANT += getpwent +NON_REENTRANT += getpwnam +NON_REENTRANT += getpwuid +NON_REENTRANT += getrpcbyname +NON_REENTRANT += getrpcbynumber +NON_REENTRANT += getrpcent +NON_REENTRANT += getservbyname +NON_REENTRANT += getservbyport +NON_REENTRANT += getservent +NON_REENTRANT += getspent +NON_REENTRANT += getspnam +NON_REENTRANT += getutent +NON_REENTRANT += getutid +NON_REENTRANT += getutline +NON_REENTRANT += gmtime +NON_REENTRANT += hcreate +NON_REENTRANT += hdestroy +NON_REENTRANT += hsearch +NON_REENTRANT += initstate +NON_REENTRANT += jrand48 +NON_REENTRANT += lcong48 +NON_REENTRANT += localtime +NON_REENTRANT += lrand48 +NON_REENTRANT += mrand48 +NON_REENTRANT += nrand48 +NON_REENTRANT += ptsname +NON_REENTRANT += qecvt +NON_REENTRANT += qfcvt +NON_REENTRANT += random +NON_REENTRANT += rand +NON_REENTRANT += seed48 +NON_REENTRANT += setstate +NON_REENTRANT += sgetspent +NON_REENTRANT += srand48 +NON_REENTRANT += srandom +NON_REENTRANT += strerror +NON_REENTRANT += strtok +NON_REENTRANT += tmpnam +NON_REENTRANT += ttyname -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:44:51PM +0000, Daniel P. Berrange wrote:
There are a huge list of functions in POSIX which are not safe to use from multiple threads currently. I generated the list by looking at all libc symbol exports for variants which have a parallel _r symbol.
nm -D --defined-only /lib/libc.so.6 \ | grep '_r$' \ | awk '{print $3}' \ | grep -v __ \ | grep -v qsort \ | grep -v readdir \ | sort \ | uniq \ | sed -e 's/_r//'
The qsort one is a red herring, since you only need qsort_r if you need to pass a extra 'void * opaque' data blob to your sort function - we don't, so don't need qsort_r.
The readdir one is also unneccessary, since reading from a single DIR* is safe from a single thread. readdir_r is also horrific
http://womble.decadentplace.org.uk/readdir_r-advisory.html
This patch adds a 'make sc_prohibit_nonrentrant' rule to the 'syntax-check' for these forbidden functions.
.x-sc_prohibit_nonreentrant | 8 ++++ Makefile.am | 2 + Makefile.maint | 11 +++++ Makefile.nonreentrant | 85 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+)
Yup, perfect, +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Tue, Jan 13, 2009 at 05:44:51PM +0000, Daniel P. Berrange wrote:
The readdir one is also unneccessary, since reading from a single DIR* is safe from a single thread. readdir_r is also horrific
That web page also says that readdir isn't thread safe, in that an implementation could still use some global state (outside of the DIR*): <quote> Programs using readdir_r may be able to use readdir. According to POSIX the buffer readdir uses is not shared between directory streams. However readdir is not guaranteed to be thread-safe and some implementations may use global state, so for portability the use of readdir in a multithreaded program should be controlled using a mutex. </quote> Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

On Tue, Jan 13, 2009 at 05:44:51PM +0000, Daniel P. Berrange wrote:
There are a huge list of functions in POSIX which are not safe to use from multiple threads currently. I generated the list by looking at all libc symbol exports for variants which have a parallel _r symbol.
nm -D --defined-only /lib/libc.so.6 \ | grep '_r$' \ | awk '{print $3}' \ | grep -v __ \ | grep -v qsort \ | grep -v readdir \ | sort \ | uniq \ | sed -e 's/_r//'
The qsort one is a red herring, since you only need qsort_r if you need to pass a extra 'void * opaque' data blob to your sort function - we don't, so don't need qsort_r.
The readdir one is also unneccessary, since reading from a single DIR* is safe from a single thread. readdir_r is also horrific
http://womble.decadentplace.org.uk/readdir_r-advisory.html
This patch adds a 'make sc_prohibit_nonrentrant' rule to the 'syntax-check' for these forbidden functions.
Cool, good idea ! +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
There are a huge list of functions in POSIX which are not safe to use from multiple threads currently. I generated the list by looking at all libc symbol exports for variants which have a parallel _r symbol.
nm -D --defined-only /lib/libc.so.6 \ | grep '_r$' \ | awk '{print $3}' \ | grep -v __ \ | grep -v qsort \ | grep -v readdir \ | sort \ | uniq \ | sed -e 's/_r//'
Nice. ACK. FYI, I get the identical list on x86_64 F10 with this: nm -D --defined-only /lib64/libc.so.6 \ | perl -nle '/ ([^_]\w+)_r$/ and print $1'|grep -vE 'qsort|readdir'

The GNULIB Makefile.am doesn't set any particular compiler warning flags in the belief that upstream GNULIB developers sort out all warnings before committing their code. This is reasonable for builds on mainstream Linux platforms, but MinGW builds of GNULIB don't have nearly the same level of testing. So This patch adds the libvirt $(WARN_CFLAGS) to the gnulib Makefile.am and then fixes the problems this showed up. Nothing of consequence really. Leave it upto gnulib developers whether its worth fixing these. Makefile.am | 2 gettimeofday.c | 5 +- ioctl.c | 1 poll.c | 4 - strerror.c | 128 ++++++++++++++++++++++++++++----------------------------- 5 files changed, 70 insertions(+), 70 deletions(-) Daniel diff --git a/gnulib/lib/Makefile.am b/gnulib/lib/Makefile.am --- a/gnulib/lib/Makefile.am +++ b/gnulib/lib/Makefile.am @@ -26,7 +26,7 @@ DISTCLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = -AM_CPPFLAGS = +AM_CPPFLAGS = $(WARN_CFLAGS) noinst_LTLIBRARIES += libgnu.la diff --git a/gnulib/lib/gettimeofday.c b/gnulib/lib/gettimeofday.c --- a/gnulib/lib/gettimeofday.c +++ b/gnulib/lib/gettimeofday.c @@ -47,11 +47,12 @@ static struct tm *localtime_buffer_addr On the first call, record the address of the static buffer that localtime uses for its result. */ +#undef localtime +extern struct tm *localtime (time_t const *); + struct tm * rpl_localtime (time_t const *timep) { -#undef localtime - extern struct tm *localtime (time_t const *); struct tm *tm = localtime (timep); if (localtime_buffer_addr == &tm_zero_buffer) diff --git a/gnulib/lib/ioctl.c b/gnulib/lib/ioctl.c --- a/gnulib/lib/ioctl.c +++ b/gnulib/lib/ioctl.c @@ -24,6 +24,7 @@ #define WIN32_LEAN_AND_MEAN /* Get winsock2.h. */ #include <sys/socket.h> +#include <sys/ioctl.h> /* Get set_winsock_errno, FD_TO_SOCKET etc. */ #include "w32sock.h" diff --git a/gnulib/lib/poll.c b/gnulib/lib/poll.c --- a/gnulib/lib/poll.c +++ b/gnulib/lib/poll.c @@ -404,11 +404,10 @@ poll (pfd, nfd, timeout) fd_set rfds, wfds, xfds; BOOL poll_again; MSG msg; - char sockbuf[256]; int rc = 0; nfds_t i; - if (nfd < 0 || timeout < -1) + if (timeout < -1) { errno = EINVAL; return -1; @@ -426,7 +425,6 @@ poll (pfd, nfd, timeout) /* Classify socket handles and create fd sets. */ for (i = 0; i < nfd; i++) { - size_t optlen = sizeof(sockbuf); pfd[i].revents = 0; if (pfd[i].fd < 0) continue; diff --git a/gnulib/lib/strerror.c b/gnulib/lib/strerror.c --- a/gnulib/lib/strerror.c +++ b/gnulib/lib/strerror.c @@ -45,89 +45,89 @@ rpl_strerror (int n) { # if GNULIB_defined_ETXTBSY case ETXTBSY: - return "Text file busy"; + return (char*)"Text file busy"; # endif # if GNULIB_defined_ESOCK /* native Windows platforms */ /* EWOULDBLOCK is the same as EAGAIN. */ case EINPROGRESS: - return "Operation now in progress"; + return (char*)"Operation now in progress"; case EALREADY: - return "Operation already in progress"; + return (char*)"Operation already in progress"; case ENOTSOCK: - return "Socket operation on non-socket"; + return (char*)"Socket operation on non-socket"; case EDESTADDRREQ: - return "Destination address required"; + return (char*)"Destination address required"; case EMSGSIZE: - return "Message too long"; + return (char*)"Message too long"; case EPROTOTYPE: - return "Protocol wrong type for socket"; + return (char*)"Protocol wrong type for socket"; case ENOPROTOOPT: - return "Protocol not available"; + return (char*)"Protocol not available"; case EPROTONOSUPPORT: - return "Protocol not supported"; + return (char*)"Protocol not supported"; case ESOCKTNOSUPPORT: - return "Socket type not supported"; + return (char*)"Socket type not supported"; case EOPNOTSUPP: - return "Operation not supported"; + return (char*)"Operation not supported"; case EPFNOSUPPORT: - return "Protocol family not supported"; + return (char*)"Protocol family not supported"; case EAFNOSUPPORT: - return "Address family not supported by protocol"; + return (char*)"Address family not supported by protocol"; case EADDRINUSE: - return "Address already in use"; + return (char*)"Address already in use"; case EADDRNOTAVAIL: - return "Cannot assign requested address"; + return (char*)"Cannot assign requested address"; case ENETDOWN: - return "Network is down"; + return (char*)"Network is down"; case ENETUNREACH: - return "Network is unreachable"; + return (char*)"Network is unreachable"; case ENETRESET: - return "Network dropped connection on reset"; + return (char*)"Network dropped connection on reset"; case ECONNABORTED: - return "Software caused connection abort"; + return (char*)"Software caused connection abort"; case ECONNRESET: - return "Connection reset by peer"; + return (char*)"Connection reset by peer"; case ENOBUFS: - return "No buffer space available"; + return (char*)"No buffer space available"; case EISCONN: - return "Transport endpoint is already connected"; + return (char*)"Transport endpoint is already connected"; case ENOTCONN: - return "Transport endpoint is not connected"; + return (char*)"Transport endpoint is not connected"; case ESHUTDOWN: - return "Cannot send after transport endpoint shutdown"; + return (char*)"Cannot send after transport endpoint shutdown"; case ETOOMANYREFS: - return "Too many references: cannot splice"; + return (char*)"Too many references: cannot splice"; case ETIMEDOUT: - return "Connection timed out"; + return (char*)"Connection timed out"; case ECONNREFUSED: - return "Connection refused"; + return (char*)"Connection refused"; case ELOOP: - return "Too many levels of symbolic links"; + return (char*)"Too many levels of symbolic links"; case EHOSTDOWN: - return "Host is down"; + return (char*)"Host is down"; case EHOSTUNREACH: - return "No route to host"; + return (char*)"No route to host"; case EPROCLIM: - return "Too many processes"; + return (char*)"Too many processes"; case EUSERS: - return "Too many users"; + return (char*)"Too many users"; case EDQUOT: - return "Disk quota exceeded"; + return (char*)"Disk quota exceeded"; case ESTALE: - return "Stale NFS file handle"; + return (char*)"Stale NFS file handle"; case EREMOTE: - return "Object is remote"; + return (char*)"Object is remote"; # if HAVE_WINSOCK2_H /* WSA_INVALID_HANDLE maps to EBADF */ /* WSA_NOT_ENOUGH_MEMORY maps to ENOMEM */ /* WSA_INVALID_PARAMETER maps to EINVAL */ case WSA_OPERATION_ABORTED: - return "Overlapped operation aborted"; + return (char*)"Overlapped operation aborted"; case WSA_IO_INCOMPLETE: - return "Overlapped I/O event object not in signaled state"; + return (char*)"Overlapped I/O event object not in signaled state"; case WSA_IO_PENDING: - return "Overlapped operations will complete later"; + return (char*)"Overlapped operations will complete later"; /* WSAEINTR maps to EINTR */ /* WSAEBADF maps to EBADF */ /* WSAEACCES maps to EACCES */ @@ -172,86 +172,86 @@ rpl_strerror (int n) /* WSAESTALE is ESTALE */ /* WSAEREMOTE is EREMOTE */ case WSASYSNOTREADY: - return "Network subsystem is unavailable"; + return (char*)"Network subsystem is unavailable"; case WSAVERNOTSUPPORTED: - return "Winsock.dll version out of range"; + return (char*)"Winsock.dll version out of range"; case WSANOTINITIALISED: - return "Successful WSAStartup not yet performed"; + return (char*)"Successful WSAStartup not yet performed"; case WSAEDISCON: - return "Graceful shutdown in progress"; + return (char*)"Graceful shutdown in progress"; case WSAENOMORE: case WSA_E_NO_MORE: - return "No more results"; + return (char*)"No more results"; case WSAECANCELLED: case WSA_E_CANCELLED: - return "Call was canceled"; + return (char*)"Call was canceled"; case WSAEINVALIDPROCTABLE: - return "Procedure call table is invalid"; + return (char*)"Procedure call table is invalid"; case WSAEINVALIDPROVIDER: - return "Service provider is invalid"; + return (char*)"Service provider is invalid"; case WSAEPROVIDERFAILEDINIT: - return "Service provider failed to initialize"; + return (char*)"Service provider failed to initialize"; case WSASYSCALLFAILURE: - return "System call failure"; + return (char*)"System call failure"; case WSASERVICE_NOT_FOUND: - return "Service not found"; + return (char*)"Service not found"; case WSATYPE_NOT_FOUND: - return "Class type not found"; + return (char*)"Class type not found"; case WSAEREFUSED: - return "Database query was refused"; + return (char*)"Database query was refused"; case WSAHOST_NOT_FOUND: - return "Host not found"; + return (char*)"Host not found"; case WSATRY_AGAIN: - return "Nonauthoritative host not found"; + return (char*)"Nonauthoritative host not found"; case WSANO_RECOVERY: - return "Nonrecoverable error"; + return (char*)"Nonrecoverable error"; case WSANO_DATA: - return "Valid name, no data record of requested type"; + return (char*)"Valid name, no data record of requested type"; /* WSA_QOS_* omitted */ # endif # endif # if GNULIB_defined_ENOMSG case ENOMSG: - return "No message of desired type"; + return (char*)"No message of desired type"; # endif # if GNULIB_defined_EIDRM case EIDRM: - return "Identifier removed"; + return (char*)"Identifier removed"; # endif # if GNULIB_defined_ENOLINK case ENOLINK: - return "Link has been severed"; + return (char*)"Link has been severed"; # endif # if GNULIB_defined_EPROTO case EPROTO: - return "Protocol error"; + return (char*)"Protocol error"; # endif # if GNULIB_defined_EMULTIHOP case EMULTIHOP: - return "Multihop attempted"; + return (char*)"Multihop attempted"; # endif # if GNULIB_defined_EBADMSG case EBADMSG: - return "Bad message"; + return (char*)"Bad message"; # endif # if GNULIB_defined_EOVERFLOW case EOVERFLOW: - return "Value too large for defined data type"; + return (char*)"Value too large for defined data type"; # endif # if GNULIB_defined_ENOTSUP case ENOTSUP: - return "Not supported"; + return (char*)"Not supported"; # endif # if GNULIB_defined_ case ECANCELED: - return "Operation canceled"; + return (char*)"Operation canceled"; # endif } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:45:14PM +0000, Daniel P. Berrange wrote:
The GNULIB Makefile.am doesn't set any particular compiler warning flags in the belief that upstream GNULIB developers sort out all warnings before committing their code. This is reasonable for builds on mainstream Linux platforms, but MinGW builds of GNULIB don't have nearly the same level of testing. So This patch adds the libvirt $(WARN_CFLAGS) to the gnulib Makefile.am and then fixes the problems this showed up. Nothing of consequence really. Leave it upto gnulib developers whether its worth fixing these.
Makefile.am | 2 gettimeofday.c | 5 +- ioctl.c | 1 poll.c | 4 - strerror.c | 128 ++++++++++++++++++++++++++++-----------------------------
I can't really comment, but it seems OK. Should we CC this patch to the bug-gnulib mailing list? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The GNULIB Makefile.am doesn't set any particular compiler warning flags in the belief that upstream GNULIB developers sort out all warnings before committing their code. This is reasonable for builds on mainstream Linux platforms, but MinGW builds of GNULIB don't have nearly the same level of testing. So This patch adds the libvirt $(WARN_CFLAGS) to the gnulib Makefile.am and then fixes the problems this showed up. Nothing of consequence really. Leave it upto gnulib developers whether its worth fixing these.
Makefile.am | 2 gettimeofday.c | 5 +- ioctl.c | 1 poll.c | 4 - strerror.c | 128 ++++++++++++++++++++++++++++-----------------------------
I've dealt with the poll.c problems upstream. 1) Your patch to remove unused var decls 2) added a pragma to avoid the warning about the unsigned_var < 0 tests All of this looks fine to commit here. I've posted an adjusted strerror.c change upstream, without so many casts. Dan, what warning does the ioctl one fix? Same for gettimeofday. Then I'll take care of those, too.
diff --git a/gnulib/lib/Makefile.am b/gnulib/lib/Makefile.am --- a/gnulib/lib/Makefile.am +++ b/gnulib/lib/Makefile.am @@ -26,7 +26,7 @@ DISTCLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES =
-AM_CPPFLAGS = +AM_CPPFLAGS = $(WARN_CFLAGS)
noinst_LTLIBRARIES += libgnu.la
diff --git a/gnulib/lib/gettimeofday.c b/gnulib/lib/gettimeofday.c --- a/gnulib/lib/gettimeofday.c +++ b/gnulib/lib/gettimeofday.c @@ -47,11 +47,12 @@ static struct tm *localtime_buffer_addr On the first call, record the address of the static buffer that localtime uses for its result. */
+#undef localtime +extern struct tm *localtime (time_t const *); + struct tm * rpl_localtime (time_t const *timep) { -#undef localtime - extern struct tm *localtime (time_t const *); struct tm *tm = localtime (timep);
if (localtime_buffer_addr == &tm_zero_buffer) diff --git a/gnulib/lib/ioctl.c b/gnulib/lib/ioctl.c --- a/gnulib/lib/ioctl.c +++ b/gnulib/lib/ioctl.c @@ -24,6 +24,7 @@ #define WIN32_LEAN_AND_MEAN /* Get winsock2.h. */ #include <sys/socket.h> +#include <sys/ioctl.h>
/* Get set_winsock_errno, FD_TO_SOCKET etc. */ #include "w32sock.h"

On Fri, Jan 16, 2009 at 12:14:51PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The GNULIB Makefile.am doesn't set any particular compiler warning flags in the belief that upstream GNULIB developers sort out all warnings before committing their code. This is reasonable for builds on mainstream Linux platforms, but MinGW builds of GNULIB don't have nearly the same level of testing. So This patch adds the libvirt $(WARN_CFLAGS) to the gnulib Makefile.am and then fixes the problems this showed up. Nothing of consequence really. Leave it upto gnulib developers whether its worth fixing these.
Makefile.am | 2 gettimeofday.c | 5 +- ioctl.c | 1 poll.c | 4 - strerror.c | 128 ++++++++++++++++++++++++++++-----------------------------
I've dealt with the poll.c problems upstream. 1) Your patch to remove unused var decls 2) added a pragma to avoid the warning about the unsigned_var < 0 tests
All of this looks fine to commit here. I've posted an adjusted strerror.c change upstream, without so many casts.
Dan, what warning does the ioctl one fix? Same for gettimeofday.
Here's the full set of warnings I got gettimeofday.c: In function 'rpl_localtime': gettimeofday.c:54: warning: nested extern declaration of 'localtime' ioctl.c:32: warning: no previous prototype for 'rpl_ioctl' poll.c: In function 'rpl_poll': poll.c:411: warning: comparison of unsigned expression < 0 is always false poll.c:429: warning: unused variable 'optlen' strerror.c: In function 'rpl_strerror': strerror.c:48: warning: return discards qualifiers from pointer target type strerror.c:54: warning: return discards qualifiers from pointer target type strerror.c:56: warning: return discards qualifiers from pointer target type strerror.c:58: warning: return discards qualifiers from pointer target type strerror.c:60: warning: return discards qualifiers from pointer target type [snip more strerror] Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The GNULIB Makefile.am doesn't set any particular compiler warning flags in the belief that upstream GNULIB developers sort out all warnings before committing their code. This is reasonable for builds on mainstream Linux platforms, but MinGW builds of GNULIB don't have nearly the same level of testing. So This patch adds the libvirt $(WARN_CFLAGS) to the gnulib Makefile.am and then fixes the problems this showed up. Nothing of consequence really. Leave it upto gnulib developers whether its worth fixing these.
Makefile.am | 2
The following or equivalent have all been applied in gnulib.
gettimeofday.c | 5 +- ioctl.c | 1 poll.c | 4 - strerror.c | 128 ++++++++++++++++++++++++++++-----------------------------

On Mon, Jan 19, 2009 at 07:41:18PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The GNULIB Makefile.am doesn't set any particular compiler warning flags in the belief that upstream GNULIB developers sort out all warnings before committing their code. This is reasonable for builds on mainstream Linux platforms, but MinGW builds of GNULIB don't have nearly the same level of testing. So This patch adds the libvirt $(WARN_CFLAGS) to the gnulib Makefile.am and then fixes the problems this showed up. Nothing of consequence really. Leave it upto gnulib developers whether its worth fixing these.
Makefile.am | 2
The following or equivalent have all been applied in gnulib.
Ok, since the later patch will run bootstrap again, I'll pull these in at that point. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Historically libvirtd was single threaded, serializing all requests across clients. An recent patch allowed multiple threads, so multiple clients could run in parallel. A single client was still serialized. This patch removes that final restriction, allowing a single client to have multiple in-flight RPC requests & replies. Each client now has 3 variables - rx: zero or one. If ready for, or in process of reading a message this will be non-null. If we're throttling the client requests, it'll be NULL. Once completely read, moved to the 'dx' queue. - dx: zero or many. Requests read off wire currently waiting to be picked up for processing by a worker thread. Once a worker is available the message is removed from the 'dx' queue for duration of processing. A reply is put on the 'tx' queue once a call is finished - tx: zero or many. Replies in process of, or ready to be, sent back to a client. Also includes any asynchronous event notifications to be sent. The 'max_client_requests' configuration parameter controls how many RPC request+reply calls can be processed in parallel. Once this limit is reached, no more requests will be read off the wire until a reply has been completed transmitted. Each request requires upto 256 KB of memory, thus memory usage for I/O is bounded by 'max_client_requests * max_clients * 256k' Compatability: - old client -> old server - everything serialized - old client -> new server - client never sends a new request until its first is finished, so effectively serialized, and no compatability problems - new client -> old server - client sends many requests without waiting for replies. The server will only read and proess one at a time, so effectively serialized, and no compatability problems - new client -> new server - fully parallelized The code has been stress tested by running 500 concurrent clients and fixing the crashes, deadlocks and memory leaks. Seems reasonably robust now. libvirtd.aug | 2 libvirtd.conf | 16 + qemud.c | 630 ++++++++++++++++++++++++++++++++---------------------- qemud.h | 66 +++-- remote.c | 48 +++- test_libvirtd.aug | 26 ++ 6 files changed, 502 insertions(+), 286 deletions(-) Daniel diff --git a/qemud/libvirtd.aug b/qemud/libvirtd.aug --- a/qemud/libvirtd.aug +++ b/qemud/libvirtd.aug @@ -53,6 +53,8 @@ module Libvirtd = let processing_entry = int_entry "min_workers" | int_entry "max_workers" | int_entry "max_clients" + | int_entry "max_requests" + | int_entry "max_client_requests" let logging_entry = int_entry "log_level" | str_entry "log_filters" diff --git a/qemud/libvirtd.conf b/qemud/libvirtd.conf --- a/qemud/libvirtd.conf +++ b/qemud/libvirtd.conf @@ -247,6 +247,22 @@ #min_workers = 5 #max_workers = 20 +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +# +# XXX this isn't actually enforced yet, only the per-client +# limit is used so far +#max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +#max_client_requests = 5 + ################################################################# # # Logging controls diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -138,6 +138,11 @@ 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; @@ -162,9 +167,36 @@ static void sig_handler(int sig, siginfo static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque); static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque); -static int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client, - int removeFirst); + + +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; + } +} + +static struct qemud_client_message * +qemudClientMessageQueuePop(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) + *queue = tmp->next; + else + *queue = NULL; + + tmp->next = NULL; + return tmp; +} static int remoteCheckCertFile(const char *type, const char *file) @@ -1042,6 +1074,8 @@ remoteCheckCertificate (gnutls_session_t static int remoteCheckAccess (struct qemud_client *client) { + struct qemud_client_message *confirm; + /* Verify client certificate. */ if (remoteCheckCertificate (client->tlssession) == -1) { VIR_ERROR0(_("remoteCheckCertificate: " @@ -1051,14 +1085,25 @@ remoteCheckAccess (struct qemud_client * "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). */ - client->bufferLength = 1; - client->bufferOffset = 0; - client->buffer[0] = '\1'; - client->mode = QEMUD_MODE_TX_PACKET; + confirm->async = 1; + confirm->bufferLength = 1; + confirm->bufferOffset = 0; + confirm->buffer[0] = '\1'; + + client->tx = confirm; return 0; } @@ -1084,6 +1129,7 @@ int qemudGetSocketIdentity(int fd, uid_t } #endif + static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) { int fd; struct sockaddr_storage addr; @@ -1099,7 +1145,7 @@ static int qemudDispatchServer(struct qe } if (server->nclients >= max_clients) { - VIR_ERROR0(_("Too many active clients, dropping connection")); + VIR_ERROR(_("Too many active clients (%d), dropping connection"), max_clients); close(fd); return -1; } @@ -1137,6 +1183,12 @@ static int qemudDispatchServer(struct qe client->addrlen = addrlen; client->server = server; + /* 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 @@ -1158,9 +1210,7 @@ static int qemudDispatchServer(struct qe #endif if (client->type != QEMUD_SOCK_TYPE_TLS) { - client->mode = QEMUD_MODE_RX_HEADER; - client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - + /* Plain socket, so prepare to read first message */ if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; } else { @@ -1180,12 +1230,12 @@ static int qemudDispatchServer(struct qe if (remoteCheckAccess (client) == -1) goto cleanup; + /* Handshake & cert check OK, so prepare to read first message */ if (qemudRegisterClientEvent(server, client, 0) < 0) goto cleanup; } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { - /* Most likely. */ - client->mode = QEMUD_MODE_TLS_HANDSHAKE; - client->bufferLength = -1; + /* Most likely, need to do more handshake data */ + client->handshake = 1; if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; @@ -1204,7 +1254,8 @@ static int qemudDispatchServer(struct qe if (client && client->tlssession) gnutls_deinit (client->tlssession); close (fd); - free (client); + VIR_FREE(client->rx); + VIR_FREE(client); return -1; } @@ -1216,8 +1267,7 @@ static int qemudDispatchServer(struct qe * We keep the libvirt connection open until any async * jobs have finished, then clean it up elsehwere */ -static void qemudDispatchClientFailure(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client) { +void qemudDispatchClientFailure(struct qemud_client *client) { virEventRemoveHandleImpl(client->watch); /* Deregister event delivery callback */ @@ -1242,7 +1292,7 @@ static struct qemud_client *qemudPending int i; for (i = 0 ; i < server->nclients ; i++) { virMutexLock(&server->clients[i]->lock); - if (server->clients[i]->mode == QEMUD_MODE_WAIT_DISPATCH) { + if (server->clients[i]->dx) { /* Delibrately don't unlock client - caller wants the lock */ return server->clients[i]; } @@ -1256,8 +1306,9 @@ static void *qemudWorker(void *data) struct qemud_server *server = data; while (1) { - struct qemud_client *client; - int len; + struct qemud_client *client = NULL; + struct qemud_client_message *reply; + virMutexLock(&server->lock); while ((client = qemudPendingJob(server)) == NULL) { if (virCondWait(&server->job, &server->lock) < 0) { @@ -1268,55 +1319,64 @@ static void *qemudWorker(void *data) virMutexUnlock(&server->lock); /* We own a locked client now... */ - client->mode = QEMUD_MODE_IN_DISPATCH; client->refs++; - if ((len = remoteDispatchClientRequest (server, client)) == 0) - qemudDispatchClientFailure(server, client); + /* Remove out message from dispatch queue while we use it */ + reply = qemudClientMessageQueuePop(&client->dx); - /* Set up the output buffer. */ - client->mode = QEMUD_MODE_TX_PACKET; - client->bufferLength = len; - client->bufferOffset = 0; + /* This function drops the lock during dispatch, + * and re-acquires it before returning */ + if (remoteDispatchClientRequest (server, client, reply) < 0) { + VIR_FREE(reply); + qemudDispatchClientFailure(client); + client->refs--; + virMutexUnlock(&client->lock); + continue; + } + + /* Put reply on end of tx queue to send out */ + qemudClientMessageQueuePush(&client->tx, reply); if (qemudRegisterClientEvent(server, client, 1) < 0) - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); client->refs--; virMutexUnlock(&client->lock); - virMutexUnlock(&server->lock); } } -static int qemudClientReadBuf(struct qemud_server *server, - struct qemud_client *client, +/* + * Read data into buffer using wire decoding (plain or TLS) + */ +static int qemudClientReadBuf(struct qemud_client *client, char *data, unsigned len) { int ret; /*qemudDebug ("qemudClientRead: len = %d", len);*/ if (!client->tlssession) { - if ((ret = read (client->fd, data, len)) <= 0) { - if (ret == 0 || errno != EAGAIN) { - if (ret != 0) - VIR_ERROR(_("read: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); - } + 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"), strerror (errno)); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_recv (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret <= 0) { - if (ret == 0 || (ret != GNUTLS_E_AGAIN && - ret != GNUTLS_E_INTERRUPTED)) { - if (ret != 0) - VIR_ERROR(_("gnutls_record_recv: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + + if (ret == -1 && (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; } } @@ -1324,21 +1384,26 @@ static int qemudClientReadBuf(struct qem return ret; } -static int qemudClientReadPlain(struct qemud_server *server, - struct qemud_client *client) { +/* + * Read data into buffer without decoding + */ +static int qemudClientReadPlain(struct qemud_client *client) { int ret; - ret = qemudClientReadBuf(server, client, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset); - if (ret < 0) - return ret; - client->bufferOffset += ret; - return 0; + 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 -static int qemudClientReadSASL(struct qemud_server *server, - struct qemud_client *client) { +/* + * Read data into buffer decoding with SASL + */ +static int qemudClientReadSASL(struct qemud_client *client) { int got, want; /* We're doing a SSF data read, so now its times to ensure @@ -1350,30 +1415,33 @@ static int qemudClientReadSASL(struct qe /* Need to read some more data off the wire */ if (client->saslDecoded == NULL) { + int ret; char encoded[8192]; int encodedLen = sizeof(encoded); - encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen); + encodedLen = qemudClientReadBuf(client, encoded, encodedLen); if (encodedLen < 0) return -1; - sasl_decode(client->saslconn, encoded, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); + ret = sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + if (ret != SASL_OK) + return -1; client->saslDecodedOffset = 0; } /* Some buffered decoded data to return now */ got = client->saslDecodedLength - client->saslDecodedOffset; - want = client->bufferLength - client->bufferOffset; + want = client->rx->bufferLength - client->rx->bufferOffset; if (want > got) want = got; - memcpy(client->buffer + client->bufferOffset, + memcpy(client->rx->buffer + client->rx->bufferOffset, client->saslDecoded + client->saslDecodedOffset, want); client->saslDecodedOffset += want; - client->bufferOffset += want; + client->rx->bufferOffset += want; if (client->saslDecodedOffset == client->saslDecodedLength) { client->saslDecoded = NULL; @@ -1384,132 +1452,125 @@ static int qemudClientReadSASL(struct qe } #endif -static int qemudClientRead(struct qemud_server *server, - struct qemud_client *client) { +/* + * Read as much data off wire as possible till we fill our + * buffer, or would block on I/O + */ +static int qemudClientRead(struct qemud_client *client) { #if HAVE_SASL if (client->saslSSF & QEMUD_SASL_SSF_READ) - return qemudClientReadSASL(server, client); + return qemudClientReadSASL(client); else #endif - return qemudClientReadPlain(server, client); + return qemudClientReadPlain(client); } -static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { - unsigned int len; +/* + * 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);*/ - switch (client->mode) { - case QEMUD_MODE_RX_HEADER: { +readmore: + if (qemudClientRead(client) < 0) + return; /* Error, or blocking */ + + if (client->rx->bufferOffset < client->rx->bufferLength) + return; /* Not read enough */ + + /* Either done with length word header */ + if (client->rx->bufferLength == REMOTE_MESSAGE_HEADER_XDR_LEN) { + int len; XDR x; - if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE); - if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - xdrmem_create(&x, client->buffer, client->bufferLength, XDR_DECODE); - - if (!xdr_u_int(&x, &len)) { + if (!xdr_int(&x, &len)) { xdr_destroy (&x); DEBUG0("Failed to decode packet length"); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } xdr_destroy (&x); + /* 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(server, client); + qemudDispatchClientFailure(client); return; } /* Length include length of the length field itself, so * check minimum size requirements */ - if (len <= REMOTE_MESSAGE_HEADER_XDR_LEN) { + if (len <= 0) { DEBUG("Packet length %u too small", len); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } - client->mode = QEMUD_MODE_RX_PAYLOAD; - client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN; - client->bufferOffset = 0; + /* Prepare to read rest of message */ + client->rx->bufferLength += len; if (qemudRegisterClientEvent(server, client, 1) < 0) { - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } - /* Fall through */ - } + /* Try and read payload immediately instead of going back + into poll() because chances are the data is already + waiting for us */ + goto readmore; + } else { + /* Move completed message to the end of the dispatch queue */ + qemudClientMessageQueuePush(&client->dx, client->rx); + client->rx = NULL; + client->nrequests++; - case QEMUD_MODE_RX_PAYLOAD: { - if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + /* 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; - if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - client->mode = QEMUD_MODE_WAIT_DISPATCH; - if (qemudRegisterClientEvent(server, client, 1) < 0) - qemudDispatchClientFailure(server, client); - - virCondSignal(&server->job); - - break; - } - - case QEMUD_MODE_TLS_HANDSHAKE: { - int ret; - - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure (server, client); - else if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - } else if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { - VIR_ERROR(_("TLS handshake failed: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } else { - if (qemudRegisterClientEvent (server ,client, 1) < 0) - qemudDispatchClientFailure (server, client); + if (qemudRegisterClientEvent(server, client, 1) < 0) + qemudDispatchClientFailure(client); + else + /* Tell one of the workers to get on with it... */ + virCondSignal(&server->job); } - - break; - } - - default: - DEBUG("Got unexpected data read while in %d mode", client->mode); - qemudDispatchClientFailure(server, client); } } -static int qemudClientWriteBuf(struct qemud_server *server, - struct qemud_client *client, +/* + * Send a chunk of data using wire encoding (plain or TLS) + */ +static int qemudClientWriteBuf(struct qemud_client *client, const char *data, int len) { int ret; if (!client->tlssession) { - if ((ret = safewrite(client->fd, data, len)) == -1) { + if ((ret = write(client->fd, data, len)) == -1) { + if (errno == EAGAIN || errno == EINTR) + return 0; VIR_ERROR(_("write: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_send (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret < 0) { - if (ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN) { - VIR_ERROR(_("gnutls_record_send: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + 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; } } @@ -1517,42 +1578,49 @@ static int qemudClientWriteBuf(struct qe } -static int qemudClientWritePlain(struct qemud_server *server, - struct qemud_client *client) { - int ret = qemudClientWriteBuf(server, client, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset); - if (ret < 0) - return -1; - client->bufferOffset += ret; - return 0; +/* + * Send client->tx using no encoding + */ +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 -static int qemudClientWriteSASL(struct qemud_server *server, - struct qemud_client *client) { +/* + * Send client->tx using SASL encoding + */ +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) { int err; err = sasl_encode(client->saslconn, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset, + client->tx->buffer + client->tx->bufferOffset, + client->tx->bufferLength - client->tx->bufferOffset, &client->saslEncoded, &client->saslEncodedLength); + if (err != SASL_OK) + return -1; + client->saslEncodedOffset = 0; } /* Send some of the encoded stuff out on the wire */ - ret = qemudClientWriteBuf(server, client, + ret = qemudClientWriteBuf(client, client->saslEncoded + client->saslEncodedOffset, client->saslEncodedLength - client->saslEncodedOffset); - if (ret < 0) - return -1; + if (ret <= 0) + return ret; /* -1 error, 0 == egain */ /* Note how much we sent */ client->saslEncodedOffset += ret; @@ -1561,77 +1629,101 @@ static int qemudClientWriteSASL(struct q if (client->saslEncodedOffset == client->saslEncodedLength) { client->saslEncoded = NULL; client->saslEncodedOffset = client->saslEncodedLength = 0; - client->bufferOffset = client->bufferLength; + + /* Mark as complete, so caller detects completion */ + client->tx->bufferOffset = client->tx->bufferLength; } - return 0; + return ret; } #endif -static int qemudClientWrite(struct qemud_server *server, - struct qemud_client *client) { +/* + * Send as much data in the client->tx as possible + */ +static int qemudClientWrite(struct qemud_client *client) { #if HAVE_SASL if (client->saslSSF & QEMUD_SASL_SSF_WRITE) - return qemudClientWriteSASL(server, client); + return qemudClientWriteSASL(client); else #endif - return qemudClientWritePlain(server, client); + return qemudClientWritePlain(client); } -void +/* + * Process all queued client->tx messages until + * we would block on I/O + */ +static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { - switch (client->mode) { - case QEMUD_MODE_TX_PACKET: { - if (qemudClientWrite(server, client) < 0) - return; - - if (client->bufferOffset == client->bufferLength) { - if (client->closing) { - qemudDispatchClientFailure (server, client); - } else { - /* Done writing, switch back to receive */ - client->mode = QEMUD_MODE_RX_HEADER; - client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - client->bufferOffset = 0; - - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - } - } - /* Still writing */ - break; - } - - case QEMUD_MODE_TLS_HANDSHAKE: { + while (client->tx) { int ret; - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure (server, client); - else if (qemudRegisterClientEvent (server, client, 1)) - qemudDispatchClientFailure (server, client); - } else if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { - VIR_ERROR(_("TLS handshake failed: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } else { - if (qemudRegisterClientEvent (server, client, 1)) - qemudDispatchClientFailure (server, client); + ret = qemudClientWrite(client); + if (ret < 0) { + qemudDispatchClientFailure(client); + return; } + if (ret == 0) + return; /* Would block on write EAGAIN */ - break; - } + if (client->tx->bufferOffset == client->tx->bufferLength) { + struct qemud_client_message *reply; - default: - DEBUG("Got unexpected data write while in %d mode", client->mode); - qemudDispatchClientFailure(server, client); + /* Get finished reply from head of tx queue */ + reply = qemudClientMessageQueuePop(&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 || + qemudRegisterClientEvent (server, client, 1) < 0) + qemudDispatchClientFailure(client); + } } } +static void +qemudDispatchClientHandshake(struct qemud_server *server, + struct qemud_client *client) { + int ret; + /* Continue the handshake. */ + ret = gnutls_handshake (client->tlssession); + if (ret == 0) { + /* Finished. Next step is to check the certificate. */ + if (remoteCheckAccess (client) == -1) + qemudDispatchClientFailure(client); + else if (qemudRegisterClientEvent (server, client, 1)) + qemudDispatchClientFailure(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 */ + if (qemudRegisterClientEvent (server, client, 1)) + qemudDispatchClientFailure(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) { @@ -1642,59 +1734,66 @@ qemudDispatchClientEvent(int watch, int 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) { - virMutexUnlock(&server->lock); return; } - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); + if (client->fd != fd) { + virMutexUnlock(&client->lock); + return; + } - if (client->fd != fd) - return; + if (events & (VIR_EVENT_HANDLE_WRITABLE | + VIR_EVENT_HANDLE_READABLE)) { + if (client->handshake) { + qemudDispatchClientHandshake(server, client); + } else { + if (events & VIR_EVENT_HANDLE_WRITABLE) + qemudDispatchClientWrite(server, client); + if (events == VIR_EVENT_HANDLE_READABLE) + qemudDispatchClientRead(server, client); + } + } - if (events == VIR_EVENT_HANDLE_WRITABLE) - qemudDispatchClientWrite(server, client); - else if (events == VIR_EVENT_HANDLE_READABLE) - qemudDispatchClientRead(server, client); - else - qemudDispatchClientFailure(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); } -static int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client, - int update) { - int mode; - switch (client->mode) { - case QEMUD_MODE_TLS_HANDSHAKE: +int qemudRegisterClientEvent(struct qemud_server *server, + struct qemud_client *client, + int update) { + int mode = 0; + + if (client->handshake) { if (gnutls_record_get_direction (client->tlssession) == 0) - mode = VIR_EVENT_HANDLE_READABLE; + mode |= VIR_EVENT_HANDLE_READABLE; else - mode = VIR_EVENT_HANDLE_WRITABLE; - break; + 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; - case QEMUD_MODE_RX_HEADER: - case QEMUD_MODE_RX_PAYLOAD: - mode = VIR_EVENT_HANDLE_READABLE; - break; - - case QEMUD_MODE_TX_PACKET: - mode = VIR_EVENT_HANDLE_WRITABLE; - break; - - case QEMUD_MODE_WAIT_DISPATCH: - mode = 0; - break; - - default: - return -1; + /* 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; } if (update) { @@ -1760,6 +1859,29 @@ static void qemudInactiveTimer(int timer } } +static void qemudFreeClient(struct qemud_client *client) { + while (client->rx) { + struct qemud_client_message *msg + = qemudClientMessageQueuePop(&client->rx); + VIR_FREE(msg); + } + while (client->dx) { + struct qemud_client_message *msg + = qemudClientMessageQueuePop(&client->dx); + VIR_FREE(msg); + } + while (client->tx) { + struct qemud_client_message *msg + = qemudClientMessageQueuePop(&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; @@ -1796,8 +1918,11 @@ static int qemudRunLoop(struct qemud_ser } virMutexUnlock(&server->lock); - if (qemudOneLoop() < 0) + if (qemudOneLoop() < 0) { + virMutexLock(&server->lock); + DEBUG0("Loop iteration error, exiting\n"); break; + } virMutexLock(&server->lock); reprocess: @@ -1808,17 +1933,18 @@ static int qemudRunLoop(struct qemud_ser && server->clients[i]->refs == 0; virMutexUnlock(&server->clients[i]->lock); if (inactive) { - if (server->clients[i]->conn) - virConnectClose(server->clients[i]->conn); - virMutexDestroy(&server->clients[i]->lock); - VIR_FREE(server->clients[i]); + qemudFreeClient(server->clients[i]); server->nclients--; - if (i < server->nclients) { + if (i < server->nclients) memmove(server->clients + i, server->clients + i + 1, - server->nclients - i); - goto reprocess; + sizeof (*server->clients) * (server->nclients - i)); + + if (VIR_REALLOC_N(server->clients, + server->nclients) < 0) { + ; /* ignore */ } + goto reprocess; } } @@ -1843,6 +1969,7 @@ static int qemudRunLoop(struct qemud_ser pthread_join(thread, NULL); virMutexLock(&server->lock); } + VIR_FREE(server->workers); free(server->workers); virMutexUnlock(&server->lock); @@ -2223,6 +2350,9 @@ remoteReadConfigFile (struct qemud_serve 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; @@ -2389,7 +2519,7 @@ int main(int argc, char **argv) { static const char *default_config_file = SYSCONF_DIR "/libvirt/libvirtd.conf"; remote_config_file = - (access(default_config_file, X_OK) == 0 + (access(default_config_file, R_OK) == 0 ? default_config_file : "/dev/null"); } diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -65,15 +65,6 @@ #define qemudDebug DEBUG -enum qemud_mode { - QEMUD_MODE_RX_HEADER, /* Receiving the fixed length RPC header data */ - QEMUD_MODE_RX_PAYLOAD, /* Receiving the variable length RPC payload data */ - QEMUD_MODE_WAIT_DISPATCH, /* Message received, waiting for worker to process */ - QEMUD_MODE_IN_DISPATCH, /* RPC call being processed */ - QEMUD_MODE_TX_PACKET, /* Transmitting reply to RPC call */ - QEMUD_MODE_TLS_HANDSHAKE, /* Performing TLS handshake */ -}; - /* Whether we're passing reads & writes through a sasl SSF */ enum qemud_sasl_ssf { QEMUD_SASL_SSF_NONE = 0, @@ -87,6 +78,18 @@ enum qemud_sock_type { QEMUD_SOCK_TYPE_TLS = 2, }; +struct qemud_client_message; + +struct qemud_client_message { + char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN]; + unsigned int bufferLength; + unsigned int bufferOffset; + + int async : 1; + + struct qemud_client_message *next; +}; + /* Stores the per-client connection state */ struct qemud_client { virMutex lock; @@ -97,7 +100,6 @@ struct qemud_client { int watch; int readonly:1; int closing:1; - enum qemud_mode mode; struct sockaddr_storage addr; socklen_t addrlen; @@ -105,6 +107,7 @@ struct qemud_client { 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; @@ -117,12 +120,20 @@ struct qemud_client { char *saslUsername; #endif - unsigned int incomingSerial; - unsigned int outgoingSerial; - - char buffer [REMOTE_MESSAGE_MAX]; - unsigned int bufferLength; - unsigned int bufferOffset; + /* 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; /* 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 @@ -181,16 +192,20 @@ void qemudLog(int priority, const char * int qemudSetCloseExec(int fd); int qemudSetNonBlock(int fd); -unsigned int +int remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client); + struct qemud_client *client, + struct qemud_client_message *req); -void qemudDispatchClientWrite(struct qemud_server *server, - struct qemud_client *client); +int qemudRegisterClientEvent(struct qemud_server *server, + struct qemud_client *client, + int update); -#if HAVE_POLKIT -int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); -#endif +void qemudDispatchClientFailure(struct qemud_client *client); + +void +qemudClientMessageQueuePush(struct qemud_client_message **queue, + struct qemud_client_message *msg); int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -198,4 +213,9 @@ int remoteRelayDomainEvent (virConnectPt int detail, void *opaque); + +#if HAVE_POLKIT +int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); #endif + +#endif diff --git a/qemud/remote.c b/qemud/remote.c --- a/qemud/remote.c +++ b/qemud/remote.c @@ -111,6 +111,7 @@ static const dispatch_data const dispatc /* Prototypes */ static void remoteDispatchDomainEventSend (struct qemud_client *client, + struct qemud_client_message *msg, virDomainPtr dom, int event, int detail); @@ -219,9 +220,10 @@ remoteDispatchConnError (remote_error *r * Server object is unlocked * Client object is locked */ -unsigned int +int remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client) + struct qemud_client *client, + struct qemud_client_message *msg) { XDR xdr; remote_message_header req, rep; @@ -237,7 +239,10 @@ remoteDispatchClientRequest (struct qemu memset(&rerr, 0, sizeof rerr); /* Parse the header. */ - xdrmem_create (&xdr, client->buffer, client->bufferLength, XDR_DECODE); + xdrmem_create (&xdr, + msg->buffer + REMOTE_MESSAGE_HEADER_XDR_LEN, + msg->bufferLength - REMOTE_MESSAGE_HEADER_XDR_LEN, + XDR_DECODE); if (!xdr_remote_message_header (&xdr, &req)) goto fatal_error; @@ -333,7 +338,7 @@ rpc_error: rep.status = rv < 0 ? REMOTE_ERROR : REMOTE_OK; /* Serialise the return header. */ - xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE); len = 0; /* We'll come back and write this later. */ if (!xdr_int (&xdr, &len)) { @@ -368,13 +373,17 @@ rpc_error: goto fatal_error; xdr_destroy (&xdr); - return len; + + msg->bufferLength = len; + msg->bufferOffset = 0; + + return 0; fatal_error: /* Seriously bad stuff happened, so we'll kill off this client and not send back any RPC error */ xdr_destroy (&xdr); - return 0; + return -1; } int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, @@ -386,9 +395,20 @@ int remoteRelayDomainEvent (virConnectPt struct qemud_client *client = opaque; REMOTE_DEBUG("Relaying domain event %d %d", event, detail); - if(client) { - remoteDispatchDomainEventSend (client, dom, event, detail); - qemudDispatchClientWrite(client->server,client); + if (client) { + struct qemud_client_message *ev; + + if (VIR_ALLOC(ev) < 0) + return -1; + + virMutexLock(&client->lock); + + remoteDispatchDomainEventSend (client, ev, dom, event, detail); + + if (qemudRegisterClientEvent(client->server, client, 1) < 0) + qemudDispatchClientFailure(client); + + virMutexUnlock(&client->lock); } return 0; } @@ -4202,6 +4222,7 @@ remoteDispatchDomainEventsDeregister (st static void remoteDispatchDomainEventSend (struct qemud_client *client, + struct qemud_client_message *msg, virDomainPtr dom, int event, int detail) @@ -4222,7 +4243,7 @@ remoteDispatchDomainEventSend (struct qe rep.status = REMOTE_OK; /* Serialise the return header and event. */ - xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE); len = 0; /* We'll come back and write this later. */ if (!xdr_int (&xdr, &len)) { @@ -4263,9 +4284,10 @@ remoteDispatchDomainEventSend (struct qe xdr_destroy (&xdr); /* Send it. */ - client->mode = QEMUD_MODE_TX_PACKET; - client->bufferLength = len; - client->bufferOffset = 0; + msg->async = 1; + msg->bufferLength = len; + msg->bufferOffset = 0; + qemudClientMessageQueuePush(&client->tx, msg); } /*----- Helpers. -----*/ diff --git a/qemud/test_libvirtd.aug b/qemud/test_libvirtd.aug --- a/qemud/test_libvirtd.aug +++ b/qemud/test_libvirtd.aug @@ -246,6 +246,19 @@ max_clients = 20 # of clients allowed min_workers = 5 max_workers = 20 + +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +max_client_requests = 5 " test Libvirtd.lns get conf = @@ -499,3 +512,16 @@ max_workers = 20 { "#comment" = "of clients allowed"} { "min_workers" = "5" } { "max_workers" = "20" } + { "#empty" } + { "#comment" = "Total global limit on concurrent RPC calls. Should be" } + { "#comment" = "at least as large as max_workers. Beyond this, RPC requests" } + { "#comment" = "will be read into memory and queued. This directly impact" } + { "#comment" = "memory usage, currently each request requires 256 KB of" } + { "#comment" = "memory. So by default upto 5 MB of memory is used" } + { "max_requests" = "20" } + { "#empty" } + { "#comment" = "Limit on concurrent requests from a single client" } + { "#comment" = "connection. To avoid one client monopolizing the server" } + { "#comment" = "this should be a small fraction of the global max_requests" } + { "#comment" = "and max_workers parameter" } + { "max_client_requests" = "5" } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:45:43PM +0000, Daniel P. Berrange wrote:
Historically libvirtd was single threaded, serializing all requests across clients. An recent patch allowed multiple threads, so multiple clients could run in parallel. A single client was still serialized.
This patch removes that final restriction, allowing a single client to have multiple in-flight RPC requests & replies. Each client now has 3 variables
- rx: zero or one. If ready for, or in process of reading a message this will be non-null. If we're throttling the client requests, it'll be NULL. Once completely read, moved to the 'dx' queue. - dx: zero or many. Requests read off wire currently waiting to be picked up for processing by a worker thread. Once a worker is available the message is removed from the 'dx' queue for duration of processing. A reply is put on the 'tx' queue once a call is finished - tx: zero or many. Replies in process of, or ready to be, sent back to a client. Also includes any asynchronous event notifications to be sent.
The 'max_client_requests' configuration parameter controls how many RPC request+reply calls can be processed in parallel. Once this limit is reached, no more requests will be read off the wire until a reply has been completed transmitted.
Each request requires upto 256 KB of memory, thus memory usage for I/O is bounded by 'max_client_requests * max_clients * 256k'
Compatability:
- old client -> old server - everything serialized - old client -> new server - client never sends a new request until its first is finished, so effectively serialized, and no compatability problems - new client -> old server - client sends many requests without waiting for replies. The server will only read and proess one at a time, so effectively serialized, and no compatability problems - new client -> new server - fully parallelized
The code has been stress tested by running 500 concurrent clients and fixing the crashes, deadlocks and memory leaks. Seems reasonably robust now.
libvirtd.aug | 2 libvirtd.conf | 16 + qemud.c | 630 ++++++++++++++++++++++++++++++++---------------------- qemud.h | 66 +++-- remote.c | 48 +++- test_libvirtd.aug | 26 ++ 6 files changed, 502 insertions(+), 286 deletions(-)
Updated patch to remove the s/X_OK/R_OK/ change that's now in CVS Daniel diff --git a/qemud/libvirtd.aug b/qemud/libvirtd.aug --- a/qemud/libvirtd.aug +++ b/qemud/libvirtd.aug @@ -53,6 +53,8 @@ module Libvirtd = let processing_entry = int_entry "min_workers" | int_entry "max_workers" | int_entry "max_clients" + | int_entry "max_requests" + | int_entry "max_client_requests" let logging_entry = int_entry "log_level" | str_entry "log_filters" diff --git a/qemud/libvirtd.conf b/qemud/libvirtd.conf --- a/qemud/libvirtd.conf +++ b/qemud/libvirtd.conf @@ -247,6 +247,22 @@ #min_workers = 5 #max_workers = 20 +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +# +# XXX this isn't actually enforced yet, only the per-client +# limit is used so far +#max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +#max_client_requests = 5 + ################################################################# # # Logging controls diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -138,6 +138,11 @@ 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; @@ -162,9 +167,36 @@ static void sig_handler(int sig, siginfo static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque); static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque); -static int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client, - int removeFirst); + + +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; + } +} + +static struct qemud_client_message * +qemudClientMessageQueuePop(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) + *queue = tmp->next; + else + *queue = NULL; + + tmp->next = NULL; + return tmp; +} static int remoteCheckCertFile(const char *type, const char *file) @@ -1042,6 +1074,8 @@ remoteCheckCertificate (gnutls_session_t static int remoteCheckAccess (struct qemud_client *client) { + struct qemud_client_message *confirm; + /* Verify client certificate. */ if (remoteCheckCertificate (client->tlssession) == -1) { VIR_ERROR0(_("remoteCheckCertificate: " @@ -1051,14 +1085,25 @@ remoteCheckAccess (struct qemud_client * "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). */ - client->bufferLength = 1; - client->bufferOffset = 0; - client->buffer[0] = '\1'; - client->mode = QEMUD_MODE_TX_PACKET; + confirm->async = 1; + confirm->bufferLength = 1; + confirm->bufferOffset = 0; + confirm->buffer[0] = '\1'; + + client->tx = confirm; return 0; } @@ -1084,6 +1129,7 @@ int qemudGetSocketIdentity(int fd, uid_t } #endif + static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) { int fd; struct sockaddr_storage addr; @@ -1099,7 +1145,7 @@ static int qemudDispatchServer(struct qe } if (server->nclients >= max_clients) { - VIR_ERROR0(_("Too many active clients, dropping connection")); + VIR_ERROR(_("Too many active clients (%d), dropping connection"), max_clients); close(fd); return -1; } @@ -1137,6 +1183,12 @@ static int qemudDispatchServer(struct qe client->addrlen = addrlen; client->server = server; + /* 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 @@ -1158,9 +1210,7 @@ static int qemudDispatchServer(struct qe #endif if (client->type != QEMUD_SOCK_TYPE_TLS) { - client->mode = QEMUD_MODE_RX_HEADER; - client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - + /* Plain socket, so prepare to read first message */ if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; } else { @@ -1180,12 +1230,12 @@ static int qemudDispatchServer(struct qe if (remoteCheckAccess (client) == -1) goto cleanup; + /* Handshake & cert check OK, so prepare to read first message */ if (qemudRegisterClientEvent(server, client, 0) < 0) goto cleanup; } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { - /* Most likely. */ - client->mode = QEMUD_MODE_TLS_HANDSHAKE; - client->bufferLength = -1; + /* Most likely, need to do more handshake data */ + client->handshake = 1; if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; @@ -1204,7 +1254,8 @@ static int qemudDispatchServer(struct qe if (client && client->tlssession) gnutls_deinit (client->tlssession); close (fd); - free (client); + VIR_FREE(client->rx); + VIR_FREE(client); return -1; } @@ -1216,8 +1267,7 @@ static int qemudDispatchServer(struct qe * We keep the libvirt connection open until any async * jobs have finished, then clean it up elsehwere */ -static void qemudDispatchClientFailure(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client) { +void qemudDispatchClientFailure(struct qemud_client *client) { virEventRemoveHandleImpl(client->watch); /* Deregister event delivery callback */ @@ -1242,7 +1292,7 @@ static struct qemud_client *qemudPending int i; for (i = 0 ; i < server->nclients ; i++) { virMutexLock(&server->clients[i]->lock); - if (server->clients[i]->mode == QEMUD_MODE_WAIT_DISPATCH) { + if (server->clients[i]->dx) { /* Delibrately don't unlock client - caller wants the lock */ return server->clients[i]; } @@ -1256,8 +1306,9 @@ static void *qemudWorker(void *data) struct qemud_server *server = data; while (1) { - struct qemud_client *client; - int len; + struct qemud_client *client = NULL; + struct qemud_client_message *reply; + virMutexLock(&server->lock); while ((client = qemudPendingJob(server)) == NULL) { if (virCondWait(&server->job, &server->lock) < 0) { @@ -1268,55 +1319,64 @@ static void *qemudWorker(void *data) virMutexUnlock(&server->lock); /* We own a locked client now... */ - client->mode = QEMUD_MODE_IN_DISPATCH; client->refs++; - if ((len = remoteDispatchClientRequest (server, client)) == 0) - qemudDispatchClientFailure(server, client); + /* Remove out message from dispatch queue while we use it */ + reply = qemudClientMessageQueuePop(&client->dx); - /* Set up the output buffer. */ - client->mode = QEMUD_MODE_TX_PACKET; - client->bufferLength = len; - client->bufferOffset = 0; + /* This function drops the lock during dispatch, + * and re-acquires it before returning */ + if (remoteDispatchClientRequest (server, client, reply) < 0) { + VIR_FREE(reply); + qemudDispatchClientFailure(client); + client->refs--; + virMutexUnlock(&client->lock); + continue; + } + + /* Put reply on end of tx queue to send out */ + qemudClientMessageQueuePush(&client->tx, reply); if (qemudRegisterClientEvent(server, client, 1) < 0) - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); client->refs--; virMutexUnlock(&client->lock); - virMutexUnlock(&server->lock); } } -static int qemudClientReadBuf(struct qemud_server *server, - struct qemud_client *client, +/* + * Read data into buffer using wire decoding (plain or TLS) + */ +static int qemudClientReadBuf(struct qemud_client *client, char *data, unsigned len) { int ret; /*qemudDebug ("qemudClientRead: len = %d", len);*/ if (!client->tlssession) { - if ((ret = read (client->fd, data, len)) <= 0) { - if (ret == 0 || errno != EAGAIN) { - if (ret != 0) - VIR_ERROR(_("read: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); - } + 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"), strerror (errno)); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_recv (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret <= 0) { - if (ret == 0 || (ret != GNUTLS_E_AGAIN && - ret != GNUTLS_E_INTERRUPTED)) { - if (ret != 0) - VIR_ERROR(_("gnutls_record_recv: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + + if (ret == -1 && (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; } } @@ -1324,21 +1384,26 @@ static int qemudClientReadBuf(struct qem return ret; } -static int qemudClientReadPlain(struct qemud_server *server, - struct qemud_client *client) { +/* + * Read data into buffer without decoding + */ +static int qemudClientReadPlain(struct qemud_client *client) { int ret; - ret = qemudClientReadBuf(server, client, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset); - if (ret < 0) - return ret; - client->bufferOffset += ret; - return 0; + 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 -static int qemudClientReadSASL(struct qemud_server *server, - struct qemud_client *client) { +/* + * Read data into buffer decoding with SASL + */ +static int qemudClientReadSASL(struct qemud_client *client) { int got, want; /* We're doing a SSF data read, so now its times to ensure @@ -1350,30 +1415,33 @@ static int qemudClientReadSASL(struct qe /* Need to read some more data off the wire */ if (client->saslDecoded == NULL) { + int ret; char encoded[8192]; int encodedLen = sizeof(encoded); - encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen); + encodedLen = qemudClientReadBuf(client, encoded, encodedLen); if (encodedLen < 0) return -1; - sasl_decode(client->saslconn, encoded, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); + ret = sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + if (ret != SASL_OK) + return -1; client->saslDecodedOffset = 0; } /* Some buffered decoded data to return now */ got = client->saslDecodedLength - client->saslDecodedOffset; - want = client->bufferLength - client->bufferOffset; + want = client->rx->bufferLength - client->rx->bufferOffset; if (want > got) want = got; - memcpy(client->buffer + client->bufferOffset, + memcpy(client->rx->buffer + client->rx->bufferOffset, client->saslDecoded + client->saslDecodedOffset, want); client->saslDecodedOffset += want; - client->bufferOffset += want; + client->rx->bufferOffset += want; if (client->saslDecodedOffset == client->saslDecodedLength) { client->saslDecoded = NULL; @@ -1384,132 +1452,125 @@ static int qemudClientReadSASL(struct qe } #endif -static int qemudClientRead(struct qemud_server *server, - struct qemud_client *client) { +/* + * Read as much data off wire as possible till we fill our + * buffer, or would block on I/O + */ +static int qemudClientRead(struct qemud_client *client) { #if HAVE_SASL if (client->saslSSF & QEMUD_SASL_SSF_READ) - return qemudClientReadSASL(server, client); + return qemudClientReadSASL(client); else #endif - return qemudClientReadPlain(server, client); + return qemudClientReadPlain(client); } -static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { - unsigned int len; +/* + * 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);*/ - switch (client->mode) { - case QEMUD_MODE_RX_HEADER: { +readmore: + if (qemudClientRead(client) < 0) + return; /* Error, or blocking */ + + if (client->rx->bufferOffset < client->rx->bufferLength) + return; /* Not read enough */ + + /* Either done with length word header */ + if (client->rx->bufferLength == REMOTE_MESSAGE_HEADER_XDR_LEN) { + int len; XDR x; - if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE); - if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - xdrmem_create(&x, client->buffer, client->bufferLength, XDR_DECODE); - - if (!xdr_u_int(&x, &len)) { + if (!xdr_int(&x, &len)) { xdr_destroy (&x); DEBUG0("Failed to decode packet length"); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } xdr_destroy (&x); + /* 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(server, client); + qemudDispatchClientFailure(client); return; } /* Length include length of the length field itself, so * check minimum size requirements */ - if (len <= REMOTE_MESSAGE_HEADER_XDR_LEN) { + if (len <= 0) { DEBUG("Packet length %u too small", len); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } - client->mode = QEMUD_MODE_RX_PAYLOAD; - client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN; - client->bufferOffset = 0; + /* Prepare to read rest of message */ + client->rx->bufferLength += len; if (qemudRegisterClientEvent(server, client, 1) < 0) { - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } - /* Fall through */ - } + /* Try and read payload immediately instead of going back + into poll() because chances are the data is already + waiting for us */ + goto readmore; + } else { + /* Move completed message to the end of the dispatch queue */ + qemudClientMessageQueuePush(&client->dx, client->rx); + client->rx = NULL; + client->nrequests++; - case QEMUD_MODE_RX_PAYLOAD: { - if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + /* 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; - if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - client->mode = QEMUD_MODE_WAIT_DISPATCH; - if (qemudRegisterClientEvent(server, client, 1) < 0) - qemudDispatchClientFailure(server, client); - - virCondSignal(&server->job); - - break; - } - - case QEMUD_MODE_TLS_HANDSHAKE: { - int ret; - - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure (server, client); - else if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - } else if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { - VIR_ERROR(_("TLS handshake failed: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } else { - if (qemudRegisterClientEvent (server ,client, 1) < 0) - qemudDispatchClientFailure (server, client); + if (qemudRegisterClientEvent(server, client, 1) < 0) + qemudDispatchClientFailure(client); + else + /* Tell one of the workers to get on with it... */ + virCondSignal(&server->job); } - - break; - } - - default: - DEBUG("Got unexpected data read while in %d mode", client->mode); - qemudDispatchClientFailure(server, client); } } -static int qemudClientWriteBuf(struct qemud_server *server, - struct qemud_client *client, +/* + * Send a chunk of data using wire encoding (plain or TLS) + */ +static int qemudClientWriteBuf(struct qemud_client *client, const char *data, int len) { int ret; if (!client->tlssession) { - if ((ret = safewrite(client->fd, data, len)) == -1) { + if ((ret = write(client->fd, data, len)) == -1) { + if (errno == EAGAIN || errno == EINTR) + return 0; VIR_ERROR(_("write: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_send (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret < 0) { - if (ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN) { - VIR_ERROR(_("gnutls_record_send: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + 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; } } @@ -1517,42 +1578,49 @@ static int qemudClientWriteBuf(struct qe } -static int qemudClientWritePlain(struct qemud_server *server, - struct qemud_client *client) { - int ret = qemudClientWriteBuf(server, client, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset); - if (ret < 0) - return -1; - client->bufferOffset += ret; - return 0; +/* + * Send client->tx using no encoding + */ +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 -static int qemudClientWriteSASL(struct qemud_server *server, - struct qemud_client *client) { +/* + * Send client->tx using SASL encoding + */ +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) { int err; err = sasl_encode(client->saslconn, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset, + client->tx->buffer + client->tx->bufferOffset, + client->tx->bufferLength - client->tx->bufferOffset, &client->saslEncoded, &client->saslEncodedLength); + if (err != SASL_OK) + return -1; + client->saslEncodedOffset = 0; } /* Send some of the encoded stuff out on the wire */ - ret = qemudClientWriteBuf(server, client, + ret = qemudClientWriteBuf(client, client->saslEncoded + client->saslEncodedOffset, client->saslEncodedLength - client->saslEncodedOffset); - if (ret < 0) - return -1; + if (ret <= 0) + return ret; /* -1 error, 0 == egain */ /* Note how much we sent */ client->saslEncodedOffset += ret; @@ -1561,77 +1629,101 @@ static int qemudClientWriteSASL(struct q if (client->saslEncodedOffset == client->saslEncodedLength) { client->saslEncoded = NULL; client->saslEncodedOffset = client->saslEncodedLength = 0; - client->bufferOffset = client->bufferLength; + + /* Mark as complete, so caller detects completion */ + client->tx->bufferOffset = client->tx->bufferLength; } - return 0; + return ret; } #endif -static int qemudClientWrite(struct qemud_server *server, - struct qemud_client *client) { +/* + * Send as much data in the client->tx as possible + */ +static int qemudClientWrite(struct qemud_client *client) { #if HAVE_SASL if (client->saslSSF & QEMUD_SASL_SSF_WRITE) - return qemudClientWriteSASL(server, client); + return qemudClientWriteSASL(client); else #endif - return qemudClientWritePlain(server, client); + return qemudClientWritePlain(client); } -void +/* + * Process all queued client->tx messages until + * we would block on I/O + */ +static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { - switch (client->mode) { - case QEMUD_MODE_TX_PACKET: { - if (qemudClientWrite(server, client) < 0) - return; - - if (client->bufferOffset == client->bufferLength) { - if (client->closing) { - qemudDispatchClientFailure (server, client); - } else { - /* Done writing, switch back to receive */ - client->mode = QEMUD_MODE_RX_HEADER; - client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - client->bufferOffset = 0; - - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - } - } - /* Still writing */ - break; - } - - case QEMUD_MODE_TLS_HANDSHAKE: { + while (client->tx) { int ret; - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure (server, client); - else if (qemudRegisterClientEvent (server, client, 1)) - qemudDispatchClientFailure (server, client); - } else if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { - VIR_ERROR(_("TLS handshake failed: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } else { - if (qemudRegisterClientEvent (server, client, 1)) - qemudDispatchClientFailure (server, client); + ret = qemudClientWrite(client); + if (ret < 0) { + qemudDispatchClientFailure(client); + return; } + if (ret == 0) + return; /* Would block on write EAGAIN */ - break; - } + if (client->tx->bufferOffset == client->tx->bufferLength) { + struct qemud_client_message *reply; - default: - DEBUG("Got unexpected data write while in %d mode", client->mode); - qemudDispatchClientFailure(server, client); + /* Get finished reply from head of tx queue */ + reply = qemudClientMessageQueuePop(&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 || + qemudRegisterClientEvent (server, client, 1) < 0) + qemudDispatchClientFailure(client); + } } } +static void +qemudDispatchClientHandshake(struct qemud_server *server, + struct qemud_client *client) { + int ret; + /* Continue the handshake. */ + ret = gnutls_handshake (client->tlssession); + if (ret == 0) { + /* Finished. Next step is to check the certificate. */ + if (remoteCheckAccess (client) == -1) + qemudDispatchClientFailure(client); + else if (qemudRegisterClientEvent (server, client, 1)) + qemudDispatchClientFailure(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 */ + if (qemudRegisterClientEvent (server, client, 1)) + qemudDispatchClientFailure(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) { @@ -1642,59 +1734,66 @@ qemudDispatchClientEvent(int watch, int 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) { - virMutexUnlock(&server->lock); return; } - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); + if (client->fd != fd) { + virMutexUnlock(&client->lock); + return; + } - if (client->fd != fd) - return; + if (events & (VIR_EVENT_HANDLE_WRITABLE | + VIR_EVENT_HANDLE_READABLE)) { + if (client->handshake) { + qemudDispatchClientHandshake(server, client); + } else { + if (events & VIR_EVENT_HANDLE_WRITABLE) + qemudDispatchClientWrite(server, client); + if (events == VIR_EVENT_HANDLE_READABLE) + qemudDispatchClientRead(server, client); + } + } - if (events == VIR_EVENT_HANDLE_WRITABLE) - qemudDispatchClientWrite(server, client); - else if (events == VIR_EVENT_HANDLE_READABLE) - qemudDispatchClientRead(server, client); - else - qemudDispatchClientFailure(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); } -static int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client, - int update) { - int mode; - switch (client->mode) { - case QEMUD_MODE_TLS_HANDSHAKE: +int qemudRegisterClientEvent(struct qemud_server *server, + struct qemud_client *client, + int update) { + int mode = 0; + + if (client->handshake) { if (gnutls_record_get_direction (client->tlssession) == 0) - mode = VIR_EVENT_HANDLE_READABLE; + mode |= VIR_EVENT_HANDLE_READABLE; else - mode = VIR_EVENT_HANDLE_WRITABLE; - break; + 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; - case QEMUD_MODE_RX_HEADER: - case QEMUD_MODE_RX_PAYLOAD: - mode = VIR_EVENT_HANDLE_READABLE; - break; - - case QEMUD_MODE_TX_PACKET: - mode = VIR_EVENT_HANDLE_WRITABLE; - break; - - case QEMUD_MODE_WAIT_DISPATCH: - mode = 0; - break; - - default: - return -1; + /* 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; } if (update) { @@ -1760,6 +1859,29 @@ static void qemudInactiveTimer(int timer } } +static void qemudFreeClient(struct qemud_client *client) { + while (client->rx) { + struct qemud_client_message *msg + = qemudClientMessageQueuePop(&client->rx); + VIR_FREE(msg); + } + while (client->dx) { + struct qemud_client_message *msg + = qemudClientMessageQueuePop(&client->dx); + VIR_FREE(msg); + } + while (client->tx) { + struct qemud_client_message *msg + = qemudClientMessageQueuePop(&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; @@ -1796,8 +1918,11 @@ static int qemudRunLoop(struct qemud_ser } virMutexUnlock(&server->lock); - if (qemudOneLoop() < 0) + if (qemudOneLoop() < 0) { + virMutexLock(&server->lock); + DEBUG0("Loop iteration error, exiting\n"); break; + } virMutexLock(&server->lock); reprocess: @@ -1808,17 +1933,18 @@ static int qemudRunLoop(struct qemud_ser && server->clients[i]->refs == 0; virMutexUnlock(&server->clients[i]->lock); if (inactive) { - if (server->clients[i]->conn) - virConnectClose(server->clients[i]->conn); - virMutexDestroy(&server->clients[i]->lock); - VIR_FREE(server->clients[i]); + qemudFreeClient(server->clients[i]); server->nclients--; - if (i < server->nclients) { + if (i < server->nclients) memmove(server->clients + i, server->clients + i + 1, - server->nclients - i); - goto reprocess; + sizeof (*server->clients) * (server->nclients - i)); + + if (VIR_REALLOC_N(server->clients, + server->nclients) < 0) { + ; /* ignore */ } + goto reprocess; } } @@ -1843,6 +1969,7 @@ static int qemudRunLoop(struct qemud_ser pthread_join(thread, NULL); virMutexLock(&server->lock); } + VIR_FREE(server->workers); free(server->workers); virMutexUnlock(&server->lock); @@ -2223,6 +2350,9 @@ remoteReadConfigFile (struct qemud_serve 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; diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -65,15 +65,6 @@ #define qemudDebug DEBUG -enum qemud_mode { - QEMUD_MODE_RX_HEADER, /* Receiving the fixed length RPC header data */ - QEMUD_MODE_RX_PAYLOAD, /* Receiving the variable length RPC payload data */ - QEMUD_MODE_WAIT_DISPATCH, /* Message received, waiting for worker to process */ - QEMUD_MODE_IN_DISPATCH, /* RPC call being processed */ - QEMUD_MODE_TX_PACKET, /* Transmitting reply to RPC call */ - QEMUD_MODE_TLS_HANDSHAKE, /* Performing TLS handshake */ -}; - /* Whether we're passing reads & writes through a sasl SSF */ enum qemud_sasl_ssf { QEMUD_SASL_SSF_NONE = 0, @@ -87,6 +78,18 @@ enum qemud_sock_type { QEMUD_SOCK_TYPE_TLS = 2, }; +struct qemud_client_message; + +struct qemud_client_message { + char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN]; + unsigned int bufferLength; + unsigned int bufferOffset; + + int async : 1; + + struct qemud_client_message *next; +}; + /* Stores the per-client connection state */ struct qemud_client { virMutex lock; @@ -97,7 +100,6 @@ struct qemud_client { int watch; int readonly:1; int closing:1; - enum qemud_mode mode; struct sockaddr_storage addr; socklen_t addrlen; @@ -105,6 +107,7 @@ struct qemud_client { 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; @@ -117,12 +120,20 @@ struct qemud_client { char *saslUsername; #endif - unsigned int incomingSerial; - unsigned int outgoingSerial; - - char buffer [REMOTE_MESSAGE_MAX]; - unsigned int bufferLength; - unsigned int bufferOffset; + /* 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; /* 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 @@ -181,16 +192,20 @@ void qemudLog(int priority, const char * int qemudSetCloseExec(int fd); int qemudSetNonBlock(int fd); -unsigned int +int remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client); + struct qemud_client *client, + struct qemud_client_message *req); -void qemudDispatchClientWrite(struct qemud_server *server, - struct qemud_client *client); +int qemudRegisterClientEvent(struct qemud_server *server, + struct qemud_client *client, + int update); -#if HAVE_POLKIT -int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); -#endif +void qemudDispatchClientFailure(struct qemud_client *client); + +void +qemudClientMessageQueuePush(struct qemud_client_message **queue, + struct qemud_client_message *msg); int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -198,4 +213,9 @@ int remoteRelayDomainEvent (virConnectPt int detail, void *opaque); + +#if HAVE_POLKIT +int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); #endif + +#endif diff --git a/qemud/remote.c b/qemud/remote.c --- a/qemud/remote.c +++ b/qemud/remote.c @@ -111,6 +111,7 @@ static const dispatch_data const dispatc /* Prototypes */ static void remoteDispatchDomainEventSend (struct qemud_client *client, + struct qemud_client_message *msg, virDomainPtr dom, int event, int detail); @@ -219,9 +220,10 @@ remoteDispatchConnError (remote_error *r * Server object is unlocked * Client object is locked */ -unsigned int +int remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client) + struct qemud_client *client, + struct qemud_client_message *msg) { XDR xdr; remote_message_header req, rep; @@ -237,7 +239,10 @@ remoteDispatchClientRequest (struct qemu memset(&rerr, 0, sizeof rerr); /* Parse the header. */ - xdrmem_create (&xdr, client->buffer, client->bufferLength, XDR_DECODE); + xdrmem_create (&xdr, + msg->buffer + REMOTE_MESSAGE_HEADER_XDR_LEN, + msg->bufferLength - REMOTE_MESSAGE_HEADER_XDR_LEN, + XDR_DECODE); if (!xdr_remote_message_header (&xdr, &req)) goto fatal_error; @@ -333,7 +338,7 @@ rpc_error: rep.status = rv < 0 ? REMOTE_ERROR : REMOTE_OK; /* Serialise the return header. */ - xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE); len = 0; /* We'll come back and write this later. */ if (!xdr_int (&xdr, &len)) { @@ -368,13 +373,17 @@ rpc_error: goto fatal_error; xdr_destroy (&xdr); - return len; + + msg->bufferLength = len; + msg->bufferOffset = 0; + + return 0; fatal_error: /* Seriously bad stuff happened, so we'll kill off this client and not send back any RPC error */ xdr_destroy (&xdr); - return 0; + return -1; } int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, @@ -386,9 +395,20 @@ int remoteRelayDomainEvent (virConnectPt struct qemud_client *client = opaque; REMOTE_DEBUG("Relaying domain event %d %d", event, detail); - if(client) { - remoteDispatchDomainEventSend (client, dom, event, detail); - qemudDispatchClientWrite(client->server,client); + if (client) { + struct qemud_client_message *ev; + + if (VIR_ALLOC(ev) < 0) + return -1; + + virMutexLock(&client->lock); + + remoteDispatchDomainEventSend (client, ev, dom, event, detail); + + if (qemudRegisterClientEvent(client->server, client, 1) < 0) + qemudDispatchClientFailure(client); + + virMutexUnlock(&client->lock); } return 0; } @@ -4202,6 +4222,7 @@ remoteDispatchDomainEventsDeregister (st static void remoteDispatchDomainEventSend (struct qemud_client *client, + struct qemud_client_message *msg, virDomainPtr dom, int event, int detail) @@ -4222,7 +4243,7 @@ remoteDispatchDomainEventSend (struct qe rep.status = REMOTE_OK; /* Serialise the return header and event. */ - xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE); len = 0; /* We'll come back and write this later. */ if (!xdr_int (&xdr, &len)) { @@ -4263,9 +4284,10 @@ remoteDispatchDomainEventSend (struct qe xdr_destroy (&xdr); /* Send it. */ - client->mode = QEMUD_MODE_TX_PACKET; - client->bufferLength = len; - client->bufferOffset = 0; + msg->async = 1; + msg->bufferLength = len; + msg->bufferOffset = 0; + qemudClientMessageQueuePush(&client->tx, msg); } /*----- Helpers. -----*/ diff --git a/qemud/test_libvirtd.aug b/qemud/test_libvirtd.aug --- a/qemud/test_libvirtd.aug +++ b/qemud/test_libvirtd.aug @@ -246,6 +246,19 @@ max_clients = 20 # of clients allowed min_workers = 5 max_workers = 20 + +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +max_client_requests = 5 " test Libvirtd.lns get conf = @@ -499,3 +512,16 @@ max_workers = 20 { "#comment" = "of clients allowed"} { "min_workers" = "5" } { "max_workers" = "20" } + { "#empty" } + { "#comment" = "Total global limit on concurrent RPC calls. Should be" } + { "#comment" = "at least as large as max_workers. Beyond this, RPC requests" } + { "#comment" = "will be read into memory and queued. This directly impact" } + { "#comment" = "memory usage, currently each request requires 256 KB of" } + { "#comment" = "memory. So by default upto 5 MB of memory is used" } + { "max_requests" = "20" } + { "#empty" } + { "#comment" = "Limit on concurrent requests from a single client" } + { "#comment" = "connection. To avoid one client monopolizing the server" } + { "#comment" = "this should be a small fraction of the global max_requests" } + { "#comment" = "and max_workers parameter" } + { "max_client_requests" = "5" } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 09:51:00PM +0000, Daniel P. Berrange wrote: [...]
From a "peephole" view of the patch, it looks sane enough, and I think we should commit it and debug any fallout later. One tiny comment:
+struct qemud_client_message; + +struct qemud_client_message { + char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN]; + unsigned int bufferLength; + unsigned int bufferOffset; + + int async : 1; + + struct qemud_client_message *next; +};
(1) You don't need to predeclare the struct. (2) This means the struct is always the same size as the maximum-sized message. Shouldn't we malloc the buffer? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

On Fri, Jan 16, 2009 at 12:01:16PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 09:51:00PM +0000, Daniel P. Berrange wrote: [...]
From a "peephole" view of the patch, it looks sane enough, and I think we should commit it and debug any fallout later. One tiny comment:
+struct qemud_client_message; + +struct qemud_client_message { + char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN]; + unsigned int bufferLength; + unsigned int bufferOffset; + + int async : 1; + + struct qemud_client_message *next; +};
(1) You don't need to predeclare the struct. (2) This means the struct is always the same size as the maximum-sized message. Shouldn't we malloc the buffer?
I did consider whether we should malloc the buffer, but this only helps when receiving the RPC request. We know there what the size of the incoming message is, from the length field in the header. When we are generating the reply message though, we don't know how big it'll be until we've made the XDR calls to serialize the struct into the XDR. So we'd need to have the full sized buffer for filling out the reply. To avoid having to malloc() a 'struct qemud_client_message' for the RPC reply, I currently just re-use the existing malloc()'d one we had for the RPC request, since that's now unused. If we only malloc'd the buffer that we needed for the request, then we'd need to realloc it to the full size for the reply anyway. So I think just declaring the struct to the full buffer size is an acceptable approach - particularly since we never have any of these structs on the stack - they're all on the heap. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Jan 16, 2009 at 12:15:42PM +0000, Daniel P. Berrange wrote:
When we are generating the reply message though, we don't know how big it'll be until we've made the XDR calls to serialize the struct into the XDR. So we'd need to have the full sized buffer for filling out the reply.
Fair enough. PortableXDR has been taking liberties with the 'traditional' xdr*_create backends, and one that I mean to implement is a variant of xdrmem which dynamically allocates the buffer. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:45:43PM +0000, Daniel P. Berrange wrote:
Historically libvirtd was single threaded, serializing all requests across clients. An recent patch allowed multiple threads, so multiple clients could run in parallel. A single client was still serialized. ...
I haven't finished this one, but here's partial feedback:
+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; + } +} + +static struct qemud_client_message * +qemudClientMessageQueuePop(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) + *queue = tmp->next; + else + *queue = NULL;
If tmp really can be NULL (tested for above), then you can't dereference it below. Also, since ...QueuePush appends, I would have expected ...QueuePop to remove from the end.
+ tmp->next = NULL; + return tmp; +}
...
@@ -1268,55 +1319,64 @@ static void *qemudWorker(void *data) virMutexUnlock(&server->lock);
/* We own a locked client now... */ - client->mode = QEMUD_MODE_IN_DISPATCH; client->refs++;
- if ((len = remoteDispatchClientRequest (server, client)) == 0) - qemudDispatchClientFailure(server, client); + /* Remove out message from dispatch queue while we use it */
s/out/our/ I'll finish tomorrow.

On Mon, Jan 19, 2009 at 10:56:18PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:45:43PM +0000, Daniel P. Berrange wrote:
Historically libvirtd was single threaded, serializing all requests across clients. An recent patch allowed multiple threads, so multiple clients could run in parallel. A single client was still serialized. ...
I haven't finished this one, but here's partial feedback:
+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; + } +} + +static struct qemud_client_message * +qemudClientMessageQueuePop(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) + *queue = tmp->next; + else + *queue = NULL;
If tmp really can be NULL (tested for above), then you can't dereference it below.
Also, since ...QueuePush appends, I would have expected ...QueuePop to remove from the end.
The daemon needs a FIFO queue for fairness, so removing from head is correct. To avoid confusion with Perl's push/pop/shift/unshift semantics I should rename this Pop method to Shift instead. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
+qemudClientMessageQueuePop(struct qemud_client_message **queue)
The daemon needs a FIFO queue for fairness, so removing from head is correct. To avoid confusion with Perl's push/pop/shift/unshift semantics I should rename this Pop method to Shift instead.
Just my $.02, how about qemudClientMessageEnqueue and qemudClientMessageDequeue? I associate push and pop with stacks, not queues. Dave

On Mon, Jan 19, 2009 at 05:13:35PM -0500, Dave Allan wrote:
Daniel P. Berrange wrote:
+qemudClientMessageQueuePop(struct qemud_client_message **queue)
The daemon needs a FIFO queue for fairness, so removing from head is correct. To avoid confusion with Perl's push/pop/shift/unshift semantics I should rename this Pop method to Shift instead.
Just my $.02, how about qemudClientMessageEnqueue and qemudClientMessageDequeue? I associate push and pop with stacks, not queues.
Well this is just my perl mind thinking, where stacks & queues are one and the same - the 4 core operators allow either model, or a combo of both. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Jan 19, 2009 at 10:56:18PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:45:43PM +0000, Daniel P. Berrange wrote:
Historically libvirtd was single threaded, serializing all requests across clients. An recent patch allowed multiple threads, so multiple clients could run in parallel. A single client was still serialized. ...
I haven't finished this one, but here's partial feedback:
+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; + } +} + +static struct qemud_client_message * +qemudClientMessageQueuePop(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) + *queue = tmp->next; + else + *queue = NULL;
If tmp really can be NULL (tested for above), then you can't dereference it below.
This was fixed in the update I just posted
Also, since ...QueuePush appends, I would have expected ...QueuePop to remove from the end.
I renamed it to QueueServe in the end Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:45:43PM +0000, Daniel P. Berrange wrote:
Historically libvirtd was single threaded, serializing all requests across clients. An recent patch allowed multiple threads, so multiple clients could run in parallel. A single client was still serialized.
This patch removes that final restriction, allowing a single client to have multiple in-flight RPC requests & replies. Each client now has 3 variables
- rx: zero or one. If ready for, or in process of reading a message this will be non-null. If we're throttling the client requests, it'll be NULL. Once completely read, moved to the 'dx' queue. - dx: zero or many. Requests read off wire currently waiting to be picked up for processing by a worker thread. Once a worker is available the message is removed from the 'dx' queue for duration of processing. A reply is put on the 'tx' queue once a call is finished - tx: zero or many. Replies in process of, or ready to be, sent back to a client. Also includes any asynchronous event notifications to be sent. ...
Phew. That took nearly 2 hours. And the review was relatively superficial in that sometimes I skimmed past things in context that I'm not familiar with.
diff --git a/qemud/qemud.c b/qemud/qemud.c ... -static int qemudClientReadBuf(struct qemud_server *server, - struct qemud_client *client, +/* + * Read data into buffer using wire decoding (plain or TLS) + */ +static int qemudClientReadBuf(struct qemud_client *client, char *data, unsigned len) { int ret;
Probably doesn't affect correctness, but ret should be of type ssize_t, to match types used by read and gnutls_record_recv. Might as well declare "len" to be ssize_t, too, and then assert that it's always positive. The assertion is nice to catch a reversed difference in the caller -- nicer to debug than a segfault.
/*qemudDebug ("qemudClientRead: len = %d", len);*/
if (!client->tlssession) { - if ((ret = read (client->fd, data, len)) <= 0) { - if (ret == 0 || errno != EAGAIN) { - if (ret != 0) - VIR_ERROR(_("read: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); - } + 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"), strerror (errno)); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_recv (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret <= 0) { - if (ret == 0 || (ret != GNUTLS_E_AGAIN && - ret != GNUTLS_E_INTERRUPTED)) { - if (ret != 0) - VIR_ERROR(_("gnutls_record_recv: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + + if (ret == -1 && (ret == GNUTLS_E_AGAIN && + ret == GNUTLS_E_INTERRUPTED)) + return 0;
The above is dead code, since the condition can never be true. It should be testing "ret < 0", not ret == -1. Also, the 2nd "&&" should be "||". 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; } ... @@ -1350,30 +1415,33 @@ static int qemudClientReadSASL(struct qe
/* Need to read some more data off the wire */ if (client->saslDecoded == NULL) { + int ret;
ssize_t, to match sasl_decode return type. no big deal
char encoded[8192]; int encodedLen = sizeof(encoded);
Likewise, "unsigned". also no big deal
- encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen); + encodedLen = qemudClientReadBuf(client, encoded, encodedLen);
if (encodedLen < 0) return -1;
- sasl_decode(client->saslconn, encoded, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); + ret = sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + if (ret != SASL_OK) + return -1;
If this ever fails, it's sure be nice to log why, but I don't see a strerror analog for SASL_* values. At least log the integer value. ...
+/* + * 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);*/
- switch (client->mode) { - case QEMUD_MODE_RX_HEADER: { +readmore: + if (qemudClientRead(client) < 0) + return; /* Error, or blocking */ + + if (client->rx->bufferOffset < client->rx->bufferLength) + return; /* Not read enough */ + + /* Either done with length word header */ + if (client->rx->bufferLength == REMOTE_MESSAGE_HEADER_XDR_LEN) { + int len; XDR x;
- if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE);
- if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - xdrmem_create(&x, client->buffer, client->bufferLength, XDR_DECODE); - - if (!xdr_u_int(&x, &len)) { + if (!xdr_int(&x, &len)) { xdr_destroy (&x); DEBUG0("Failed to decode packet length"); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } xdr_destroy (&x);
+ /* 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(server, client); + qemudDispatchClientFailure(client); return; }
/* Length include length of the length field itself, so * check minimum size requirements */ - if (len <= REMOTE_MESSAGE_HEADER_XDR_LEN) { + if (len <= 0) { DEBUG("Packet length %u too small", len);
"len" may be negative here, so printing with %u will give a misleading diagnostic. Better use the original value, "len + REMOTE_MESSAGE_HEADER_XDR_LEN", which is more likely to be non-negative. Might as well use %d, in case even the original value is negative.
- qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return;
...
static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque) { @@ -1642,59 +1734,66 @@ qemudDispatchClientEvent(int watch, int 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) { - virMutexUnlock(&server->lock); return; }
- virMutexLock(&client->lock); - virMutexUnlock(&server->lock); + if (client->fd != fd) { + virMutexUnlock(&client->lock); + return; + }
- if (client->fd != fd) - return; + if (events & (VIR_EVENT_HANDLE_WRITABLE | + VIR_EVENT_HANDLE_READABLE)) { + if (client->handshake) { + qemudDispatchClientHandshake(server, client); + } else { + if (events & VIR_EVENT_HANDLE_WRITABLE) + qemudDispatchClientWrite(server, client); + if (events == VIR_EVENT_HANDLE_READABLE) + qemudDispatchClientRead(server, client);
Why test with & to write, and then with "==" to read? That makes it so we don't read when we've just written (i.e., if both read and write bits were set). Hmm... looking at the old code, it seems the intent is to do only one of read/write operations at a time. In that case, just use an "else": if (events & VIR_EVENT_HANDLE_WRITABLE) qemudDispatchClientWrite(server, client); else qemudDispatchClientRead(server, client); ...
+ /* NB, will get HANGUP + READABLE at same time upon + * disconnect */
Oh. Now I suspect that the preceding "events == VIR_EVENT_HANDLE_READABLE" test is to avoid reading upon HANGUP. But it also inhibits reading when/if READABLE and WRITABLE are both set (probably doesn't matter). Please add a comment.
+ if (events & (VIR_EVENT_HANDLE_ERROR | + VIR_EVENT_HANDLE_HANGUP)) + qemudDispatchClientFailure(client); + virMutexUnlock(&client->lock); }
...
diff --git a/qemud/qemud.h b/qemud/qemud.h ... +struct qemud_client_message { ... + int nrequests;
Logically, this should be an unsigned type. But that means max_clients should be, too, since they're compared, but max_clients comes from the config file, which currently uses "int" (via GET_CONF_INT). Maybe we need GET_CONF_UINT? I wonder if a bogus config "int" value of 2^32 or 2^64 maps back to 0.

On Tue, Jan 20, 2009 at 10:12:04AM +0100, Jim Meyering wrote:
} else { ret = gnutls_record_recv (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret <= 0) { - if (ret == 0 || (ret != GNUTLS_E_AGAIN && - ret != GNUTLS_E_INTERRUPTED)) { - if (ret != 0) - VIR_ERROR(_("gnutls_record_recv: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + + if (ret == -1 && (ret == GNUTLS_E_AGAIN && + ret == GNUTLS_E_INTERRUPTED)) + return 0;
The above is dead code, since the condition can never be true. It should be testing "ret < 0", not ret == -1. Also, the 2nd "&&" should be "||".
if (ret < 0 && (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)) return 0;
Yes, really not sure why I change it like this. Clearly wrong
if (encodedLen < 0) return -1;
- sasl_decode(client->saslconn, encoded, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); + ret = sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + if (ret != SASL_OK) + return -1;
If this ever fails, it's sure be nice to log why, but I don't see a strerror analog for SASL_* values.
Actually there are two options sasl_errstring / sasl_errdetail both giving suitable output.
- if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE);
- if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - xdrmem_create(&x, client->buffer, client->bufferLength, XDR_DECODE); - - if (!xdr_u_int(&x, &len)) { + if (!xdr_int(&x, &len)) { xdr_destroy (&x); DEBUG0("Failed to decode packet length"); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } xdr_destroy (&x);
+ /* 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(server, client); + qemudDispatchClientFailure(client); return; }
/* Length include length of the length field itself, so * check minimum size requirements */ - if (len <= REMOTE_MESSAGE_HEADER_XDR_LEN) { + if (len <= 0) { DEBUG("Packet length %u too small", len);
"len" may be negative here, so printing with %u will give a misleading diagnostic. Better use the original value, "len + REMOTE_MESSAGE_HEADER_XDR_LEN", which is more likely to be non-negative. Might as well use %d, in case even the original value is negative.
SOmething odd here - I don't think I should have been changing it to signed in the first place. The original code used unsigned int and this is on the wire. Oddly the original client code used a signed int, so we had a mis-match of client & server. I think it is better to fix the client though. So I'll re-visit this whole chunk
- if (client->fd != fd) - return; + if (events & (VIR_EVENT_HANDLE_WRITABLE | + VIR_EVENT_HANDLE_READABLE)) { + if (client->handshake) { + qemudDispatchClientHandshake(server, client); + } else { + if (events & VIR_EVENT_HANDLE_WRITABLE) + qemudDispatchClientWrite(server, client); + if (events == VIR_EVENT_HANDLE_READABLE) + qemudDispatchClientRead(server, client);
Why test with & to write, and then with "==" to read? That makes it so we don't read when we've just written (i.e., if both read and write bits were set).
Bug, it should be '&' and not '=='.
diff --git a/qemud/qemud.h b/qemud/qemud.h ... +struct qemud_client_message { ... + int nrequests;
Logically, this should be an unsigned type. But that means max_clients should be, too, since they're compared, but max_clients comes from the config file, which currently uses "int" (via GET_CONF_INT). Maybe we need GET_CONF_UINT? I wonder if a bogus config "int" value of 2^32 or 2^64 maps back to 0.
Checking for 2^32/64 is not really helpful in this context, because both are totally inappropriate for nrequests - we should check for a more reasonable lower limit on nrequests, perhaps in range 1 -> 100. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 20, 2009 at 10:27:56AM +0000, Daniel P. Berrange wrote:
The above is dead code, since the condition can never be true. It should be testing "ret < 0", not ret == -1. Also, the 2nd "&&" should be "||".
if (ret < 0 && (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)) return 0;
Yes, really not sure why I change it like this. Clearly wrong
if (encodedLen < 0) return -1;
- sasl_decode(client->saslconn, encoded, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); + ret = sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + if (ret != SASL_OK) + return -1;
If this ever fails, it's sure be nice to log why, but I don't see a strerror analog for SASL_* values.
Actually there are two options sasl_errstring / sasl_errdetail both giving suitable output.
- if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE);
- if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - xdrmem_create(&x, client->buffer, client->bufferLength, XDR_DECODE); - - if (!xdr_u_int(&x, &len)) { + if (!xdr_int(&x, &len)) { xdr_destroy (&x); DEBUG0("Failed to decode packet length"); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } xdr_destroy (&x);
+ /* 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(server, client); + qemudDispatchClientFailure(client); return; }
/* Length include length of the length field itself, so * check minimum size requirements */ - if (len <= REMOTE_MESSAGE_HEADER_XDR_LEN) { + if (len <= 0) { DEBUG("Packet length %u too small", len);
"len" may be negative here, so printing with %u will give a misleading diagnostic. Better use the original value, "len + REMOTE_MESSAGE_HEADER_XDR_LEN", which is more likely to be non-negative. Might as well use %d, in case even the original value is negative.
SOmething odd here - I don't think I should have been changing it to signed in the first place. The original code used unsigned int and this is on the wire. Oddly the original client code used a signed int, so we had a mis-match of client & server. I think it is better to fix the client though. So I'll re-visit this whole chunk
I have put it back to 'xdr_u_int' and fixed the client code to match this. There's no compatability problem because neither old or new code was sending length field thats long enough to differ in signed vs unsigned. I also made the range checks moire paranoid.
- if (client->fd != fd) - return; + if (events & (VIR_EVENT_HANDLE_WRITABLE | + VIR_EVENT_HANDLE_READABLE)) { + if (client->handshake) { + qemudDispatchClientHandshake(server, client); + } else { + if (events & VIR_EVENT_HANDLE_WRITABLE) + qemudDispatchClientWrite(server, client); + if (events == VIR_EVENT_HANDLE_READABLE) + qemudDispatchClientRead(server, client);
Why test with & to write, and then with "==" to read? That makes it so we don't read when we've just written (i.e., if both read and write bits were set).
Bug, it should be '&' and not '=='.
diff --git a/qemud/qemud.h b/qemud/qemud.h ... +struct qemud_client_message { ... + int nrequests;
Logically, this should be an unsigned type. But that means max_clients should be, too, since they're compared, but max_clients comes from the config file, which currently uses "int" (via GET_CONF_INT). Maybe we need GET_CONF_UINT? I wonder if a bogus config "int" value of 2^32 or 2^64 maps back to 0.
Checking for 2^32/64 is not really helpful in this context, because both are totally inappropriate for nrequests - we should check for a more reasonable lower limit on nrequests, perhaps in range 1 -> 100.
I've left nrequests as a signed int, because its compared to max_Clients which is also signed & it'd be better to change them all at once, rather than just nrequests. Finally I've also made use of ssize_t as suggested, this also made me notice one place where we didn't propagate the EAGAIN case of ret=0 correctly. Daniel diff --git a/qemud/libvirtd.aug b/qemud/libvirtd.aug --- a/qemud/libvirtd.aug +++ b/qemud/libvirtd.aug @@ -53,6 +53,8 @@ module Libvirtd = let processing_entry = int_entry "min_workers" | int_entry "max_workers" | int_entry "max_clients" + | int_entry "max_requests" + | int_entry "max_client_requests" let logging_entry = int_entry "log_level" | str_entry "log_filters" diff --git a/qemud/libvirtd.conf b/qemud/libvirtd.conf --- a/qemud/libvirtd.conf +++ b/qemud/libvirtd.conf @@ -247,6 +247,22 @@ #min_workers = 5 #max_workers = 20 +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +# +# XXX this isn't actually enforced yet, only the per-client +# limit is used so far +#max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +#max_client_requests = 5 + ################################################################# # # Logging controls diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -138,6 +138,11 @@ 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; @@ -162,9 +167,37 @@ static void sig_handler(int sig, siginfo static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque); static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque); -static int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client, - int removeFirst); + + +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; + } +} + +static struct qemud_client_message * +qemudClientMessageQueueServe(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) { + *queue = tmp->next; + tmp->next = NULL; + } else { + *queue = NULL; + } + + return tmp; +} static int remoteCheckCertFile(const char *type, const char *file) @@ -1042,6 +1075,8 @@ remoteCheckCertificate (gnutls_session_t static int remoteCheckAccess (struct qemud_client *client) { + struct qemud_client_message *confirm; + /* Verify client certificate. */ if (remoteCheckCertificate (client->tlssession) == -1) { VIR_ERROR0(_("remoteCheckCertificate: " @@ -1051,14 +1086,25 @@ remoteCheckAccess (struct qemud_client * "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). */ - client->bufferLength = 1; - client->bufferOffset = 0; - client->buffer[0] = '\1'; - client->mode = QEMUD_MODE_TX_PACKET; + confirm->async = 1; + confirm->bufferLength = 1; + confirm->bufferOffset = 0; + confirm->buffer[0] = '\1'; + + client->tx = confirm; return 0; } @@ -1084,6 +1130,7 @@ int qemudGetSocketIdentity(int fd, uid_t } #endif + static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) { int fd; struct sockaddr_storage addr; @@ -1099,7 +1146,7 @@ static int qemudDispatchServer(struct qe } if (server->nclients >= max_clients) { - VIR_ERROR0(_("Too many active clients, dropping connection")); + VIR_ERROR(_("Too many active clients (%d), dropping connection"), max_clients); close(fd); return -1; } @@ -1137,6 +1184,12 @@ static int qemudDispatchServer(struct qe client->addrlen = addrlen; client->server = server; + /* 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 @@ -1158,9 +1211,7 @@ static int qemudDispatchServer(struct qe #endif if (client->type != QEMUD_SOCK_TYPE_TLS) { - client->mode = QEMUD_MODE_RX_HEADER; - client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - + /* Plain socket, so prepare to read first message */ if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; } else { @@ -1180,12 +1231,12 @@ static int qemudDispatchServer(struct qe if (remoteCheckAccess (client) == -1) goto cleanup; + /* Handshake & cert check OK, so prepare to read first message */ if (qemudRegisterClientEvent(server, client, 0) < 0) goto cleanup; } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { - /* Most likely. */ - client->mode = QEMUD_MODE_TLS_HANDSHAKE; - client->bufferLength = -1; + /* Most likely, need to do more handshake data */ + client->handshake = 1; if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; @@ -1204,7 +1255,8 @@ static int qemudDispatchServer(struct qe if (client && client->tlssession) gnutls_deinit (client->tlssession); close (fd); - free (client); + VIR_FREE(client->rx); + VIR_FREE(client); return -1; } @@ -1216,8 +1268,7 @@ static int qemudDispatchServer(struct qe * We keep the libvirt connection open until any async * jobs have finished, then clean it up elsehwere */ -static void qemudDispatchClientFailure(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client) { +void qemudDispatchClientFailure(struct qemud_client *client) { virEventRemoveHandleImpl(client->watch); /* Deregister event delivery callback */ @@ -1242,7 +1293,7 @@ static struct qemud_client *qemudPending int i; for (i = 0 ; i < server->nclients ; i++) { virMutexLock(&server->clients[i]->lock); - if (server->clients[i]->mode == QEMUD_MODE_WAIT_DISPATCH) { + if (server->clients[i]->dx) { /* Delibrately don't unlock client - caller wants the lock */ return server->clients[i]; } @@ -1256,8 +1307,9 @@ static void *qemudWorker(void *data) struct qemud_server *server = data; while (1) { - struct qemud_client *client; - int len; + struct qemud_client *client = NULL; + struct qemud_client_message *reply; + virMutexLock(&server->lock); while ((client = qemudPendingJob(server)) == NULL) { if (virCondWait(&server->job, &server->lock) < 0) { @@ -1268,55 +1320,75 @@ static void *qemudWorker(void *data) virMutexUnlock(&server->lock); /* We own a locked client now... */ - client->mode = QEMUD_MODE_IN_DISPATCH; client->refs++; - if ((len = remoteDispatchClientRequest (server, client)) == 0) - qemudDispatchClientFailure(server, client); + /* Remove our message from dispatch queue while we use it */ + reply = qemudClientMessageQueueServe(&client->dx); - /* Set up the output buffer. */ - client->mode = QEMUD_MODE_TX_PACKET; - client->bufferLength = len; - client->bufferOffset = 0; + /* This function drops the lock during dispatch, + * and re-acquires it before returning */ + if (remoteDispatchClientRequest (server, client, reply) < 0) { + VIR_FREE(reply); + qemudDispatchClientFailure(client); + client->refs--; + virMutexUnlock(&client->lock); + continue; + } + + /* Put reply on end of tx queue to send out */ + qemudClientMessageQueuePush(&client->tx, reply); if (qemudRegisterClientEvent(server, client, 1) < 0) - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); client->refs--; virMutexUnlock(&client->lock); - virMutexUnlock(&server->lock); } } -static int qemudClientReadBuf(struct qemud_server *server, - struct qemud_client *client, - char *data, unsigned len) { - int ret; +/* + * 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 %d"), len); + qemudDispatchClientFailure(client); + return -1; + } /*qemudDebug ("qemudClientRead: len = %d", len);*/ if (!client->tlssession) { - if ((ret = read (client->fd, data, len)) <= 0) { - if (ret == 0 || errno != EAGAIN) { - if (ret != 0) - VIR_ERROR(_("read: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); - } + 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"), strerror (errno)); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_recv (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret <= 0) { - if (ret == 0 || (ret != GNUTLS_E_AGAIN && - ret != GNUTLS_E_INTERRUPTED)) { - if (ret != 0) - VIR_ERROR(_("gnutls_record_recv: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + + 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; } } @@ -1324,22 +1396,37 @@ static int qemudClientReadBuf(struct qem return ret; } -static int qemudClientReadPlain(struct qemud_server *server, - struct qemud_client *client) { - int ret; - ret = qemudClientReadBuf(server, client, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset); - if (ret < 0) - return ret; - client->bufferOffset += ret; - return 0; +/* + * 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 -static int qemudClientReadSASL(struct qemud_server *server, - struct qemud_client *client) { - int got, want; +/* + * 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. @@ -1350,166 +1437,176 @@ static int qemudClientReadSASL(struct qe /* Need to read some more data off the wire */ if (client->saslDecoded == NULL) { + int ret; char encoded[8192]; - int encodedLen = sizeof(encoded); - encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen); + ssize_t encodedLen = sizeof(encoded); + encodedLen = qemudClientReadBuf(client, encoded, encodedLen); - if (encodedLen < 0) + 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; - - sasl_decode(client->saslconn, encoded, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); + } client->saslDecodedOffset = 0; } /* Some buffered decoded data to return now */ got = client->saslDecodedLength - client->saslDecodedOffset; - want = client->bufferLength - client->bufferOffset; + want = client->rx->bufferLength - client->rx->bufferOffset; if (want > got) want = got; - memcpy(client->buffer + client->bufferOffset, + memcpy(client->rx->buffer + client->rx->bufferOffset, client->saslDecoded + client->saslDecodedOffset, want); client->saslDecodedOffset += want; - client->bufferOffset += want; + client->rx->bufferOffset += want; if (client->saslDecodedOffset == client->saslDecodedLength) { client->saslDecoded = NULL; client->saslDecodedOffset = client->saslDecodedLength = 0; } - return 0; + return want; } #endif -static int qemudClientRead(struct qemud_server *server, - struct qemud_client *client) { +/* + * 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(server, client); + return qemudClientReadSASL(client); else #endif - return qemudClientReadPlain(server, client); + return qemudClientReadPlain(client); } -static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { - unsigned int len; +/* + * 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);*/ - switch (client->mode) { - case QEMUD_MODE_RX_HEADER: { +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; - if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ - - if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - xdrmem_create(&x, client->buffer, client->bufferLength, XDR_DECODE); + 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(server, client); + qemudDispatchClientFailure(client); return; } xdr_destroy (&x); - if (len > REMOTE_MESSAGE_MAX) { - DEBUG("Packet length %u too large", len); - qemudDispatchClientFailure(server, client); + if (len < REMOTE_MESSAGE_HEADER_XDR_LEN) { + DEBUG("Packet length %u too small", len); + qemudDispatchClientFailure(client); return; } - /* Length include length of the length field itself, so - * check minimum size requirements */ - if (len <= REMOTE_MESSAGE_HEADER_XDR_LEN) { - DEBUG("Packet length %u too small", len); - qemudDispatchClientFailure(server, client); + /* 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; } - client->mode = QEMUD_MODE_RX_PAYLOAD; - client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN; - client->bufferOffset = 0; + /* Prepare to read rest of message */ + client->rx->bufferLength += len; if (qemudRegisterClientEvent(server, client, 1) < 0) { - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return; } - /* Fall through */ - } + /* Try and read payload immediately instead of going back + into poll() because chances are the data is already + waiting for us */ + goto readmore; + } else { + /* Move completed message to the end of the dispatch queue */ + qemudClientMessageQueuePush(&client->dx, client->rx); + client->rx = NULL; + client->nrequests++; - case QEMUD_MODE_RX_PAYLOAD: { - if (qemudClientRead(server, client) < 0) - return; /* Error, or blocking */ + /* 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; - if (client->bufferOffset < client->bufferLength) - return; /* Not read enough */ - - client->mode = QEMUD_MODE_WAIT_DISPATCH; - if (qemudRegisterClientEvent(server, client, 1) < 0) - qemudDispatchClientFailure(server, client); - - virCondSignal(&server->job); - - break; - } - - case QEMUD_MODE_TLS_HANDSHAKE: { - int ret; - - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure (server, client); - else if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - } else if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { - VIR_ERROR(_("TLS handshake failed: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } else { - if (qemudRegisterClientEvent (server ,client, 1) < 0) - qemudDispatchClientFailure (server, client); + if (qemudRegisterClientEvent(server, client, 1) < 0) + qemudDispatchClientFailure(client); + else + /* Tell one of the workers to get on with it... */ + virCondSignal(&server->job); } - - break; - } - - default: - DEBUG("Got unexpected data read while in %d mode", client->mode); - qemudDispatchClientFailure(server, client); } } -static int qemudClientWriteBuf(struct qemud_server *server, - struct qemud_client *client, - const char *data, int len) { - int ret; +/* + * 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 %d"), len); + qemudDispatchClientFailure(client); + return -1; + } + if (!client->tlssession) { - if ((ret = safewrite(client->fd, data, len)) == -1) { + if ((ret = write(client->fd, data, len)) == -1) { + if (errno == EAGAIN || errno == EINTR) + return 0; VIR_ERROR(_("write: %s"), strerror (errno)); - qemudDispatchClientFailure(server, client); + qemudDispatchClientFailure(client); return -1; } } else { ret = gnutls_record_send (client->tlssession, data, len); - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - else if (ret < 0) { - if (ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN) { - VIR_ERROR(_("gnutls_record_send: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } + 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; } } @@ -1517,42 +1614,62 @@ static int qemudClientWriteBuf(struct qe } -static int qemudClientWritePlain(struct qemud_server *server, - struct qemud_client *client) { - int ret = qemudClientWriteBuf(server, client, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset); - if (ret < 0) - return -1; - client->bufferOffset += ret; - return 0; +/* + * 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 -static int qemudClientWriteSASL(struct qemud_server *server, - struct qemud_client *client) { +/* + * 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) { - int err; - err = sasl_encode(client->saslconn, - client->buffer + client->bufferOffset, - client->bufferLength - client->bufferOffset, + 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(server, client, + ret = qemudClientWriteBuf(client, client->saslEncoded + client->saslEncodedOffset, client->saslEncodedLength - client->saslEncodedOffset); - if (ret < 0) - return -1; + if (ret <= 0) + return ret; /* -1 error, 0 == egain */ /* Note how much we sent */ client->saslEncodedOffset += ret; @@ -1561,77 +1678,106 @@ static int qemudClientWriteSASL(struct q if (client->saslEncodedOffset == client->saslEncodedLength) { client->saslEncoded = NULL; client->saslEncodedOffset = client->saslEncodedLength = 0; - client->bufferOffset = client->bufferLength; + + /* Mark as complete, so caller detects completion */ + client->tx->bufferOffset = client->tx->bufferLength; } - return 0; + return ret; } #endif -static int qemudClientWrite(struct qemud_server *server, - struct qemud_client *client) { +/* + * 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(server, client); + return qemudClientWriteSASL(client); else #endif - return qemudClientWritePlain(server, client); + return qemudClientWritePlain(client); } -void +/* + * Process all queued client->tx messages until + * we would block on I/O + */ +static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { - switch (client->mode) { - case QEMUD_MODE_TX_PACKET: { - if (qemudClientWrite(server, client) < 0) + 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->bufferOffset == client->bufferLength) { - if (client->closing) { - qemudDispatchClientFailure (server, client); + 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 { - /* Done writing, switch back to receive */ - client->mode = QEMUD_MODE_RX_HEADER; - client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - client->bufferOffset = 0; + VIR_FREE(reply); + } - if (qemudRegisterClientEvent (server, client, 1) < 0) - qemudDispatchClientFailure (server, client); - } - } - /* Still writing */ - break; - } - - case QEMUD_MODE_TLS_HANDSHAKE: { - int ret; - - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure (server, client); - else if (qemudRegisterClientEvent (server, client, 1)) - qemudDispatchClientFailure (server, client); - } else if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) { - VIR_ERROR(_("TLS handshake failed: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure (server, client); - } else { - if (qemudRegisterClientEvent (server, client, 1)) - qemudDispatchClientFailure (server, client); - } - - break; - } - - default: - DEBUG("Got unexpected data write while in %d mode", client->mode); - qemudDispatchClientFailure(server, client); + if (client->closing || + qemudRegisterClientEvent (server, client, 1) < 0) + qemudDispatchClientFailure(client); + } } } +static void +qemudDispatchClientHandshake(struct qemud_server *server, + struct qemud_client *client) { + int ret; + /* Continue the handshake. */ + ret = gnutls_handshake (client->tlssession); + if (ret == 0) { + /* Finished. Next step is to check the certificate. */ + if (remoteCheckAccess (client) == -1) + qemudDispatchClientFailure(client); + else if (qemudRegisterClientEvent (server, client, 1)) + qemudDispatchClientFailure(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 */ + if (qemudRegisterClientEvent (server, client, 1)) + qemudDispatchClientFailure(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) { @@ -1642,59 +1788,66 @@ qemudDispatchClientEvent(int watch, int 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) { - virMutexUnlock(&server->lock); return; } - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); + if (client->fd != fd) { + virMutexUnlock(&client->lock); + return; + } - if (client->fd != fd) - return; + if (events & (VIR_EVENT_HANDLE_WRITABLE | + VIR_EVENT_HANDLE_READABLE)) { + if (client->handshake) { + qemudDispatchClientHandshake(server, client); + } else { + if (events & VIR_EVENT_HANDLE_WRITABLE) + qemudDispatchClientWrite(server, client); + if (events & VIR_EVENT_HANDLE_READABLE) + qemudDispatchClientRead(server, client); + } + } - if (events == VIR_EVENT_HANDLE_WRITABLE) - qemudDispatchClientWrite(server, client); - else if (events == VIR_EVENT_HANDLE_READABLE) - qemudDispatchClientRead(server, client); - else - qemudDispatchClientFailure(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); } -static int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client, - int update) { - int mode; - switch (client->mode) { - case QEMUD_MODE_TLS_HANDSHAKE: +int qemudRegisterClientEvent(struct qemud_server *server, + struct qemud_client *client, + int update) { + int mode = 0; + + if (client->handshake) { if (gnutls_record_get_direction (client->tlssession) == 0) - mode = VIR_EVENT_HANDLE_READABLE; + mode |= VIR_EVENT_HANDLE_READABLE; else - mode = VIR_EVENT_HANDLE_WRITABLE; - break; + 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; - case QEMUD_MODE_RX_HEADER: - case QEMUD_MODE_RX_PAYLOAD: - mode = VIR_EVENT_HANDLE_READABLE; - break; - - case QEMUD_MODE_TX_PACKET: - mode = VIR_EVENT_HANDLE_WRITABLE; - break; - - case QEMUD_MODE_WAIT_DISPATCH: - mode = 0; - break; - - default: - return -1; + /* 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; } if (update) { @@ -1760,6 +1913,29 @@ static void qemudInactiveTimer(int timer } } +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; @@ -1796,8 +1972,11 @@ static int qemudRunLoop(struct qemud_ser } virMutexUnlock(&server->lock); - if (qemudOneLoop() < 0) + if (qemudOneLoop() < 0) { + virMutexLock(&server->lock); + DEBUG0("Loop iteration error, exiting\n"); break; + } virMutexLock(&server->lock); reprocess: @@ -1808,17 +1987,18 @@ static int qemudRunLoop(struct qemud_ser && server->clients[i]->refs == 0; virMutexUnlock(&server->clients[i]->lock); if (inactive) { - if (server->clients[i]->conn) - virConnectClose(server->clients[i]->conn); - virMutexDestroy(&server->clients[i]->lock); - VIR_FREE(server->clients[i]); + qemudFreeClient(server->clients[i]); server->nclients--; - if (i < server->nclients) { + if (i < server->nclients) memmove(server->clients + i, server->clients + i + 1, - server->nclients - i); - goto reprocess; + sizeof (*server->clients) * (server->nclients - i)); + + if (VIR_REALLOC_N(server->clients, + server->nclients) < 0) { + ; /* ignore */ } + goto reprocess; } } @@ -1843,6 +2023,7 @@ static int qemudRunLoop(struct qemud_ser pthread_join(thread, NULL); virMutexLock(&server->lock); } + VIR_FREE(server->workers); free(server->workers); virMutexUnlock(&server->lock); @@ -2223,6 +2404,9 @@ remoteReadConfigFile (struct qemud_serve 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; diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -65,15 +65,6 @@ #define qemudDebug DEBUG -enum qemud_mode { - QEMUD_MODE_RX_HEADER, /* Receiving the fixed length RPC header data */ - QEMUD_MODE_RX_PAYLOAD, /* Receiving the variable length RPC payload data */ - QEMUD_MODE_WAIT_DISPATCH, /* Message received, waiting for worker to process */ - QEMUD_MODE_IN_DISPATCH, /* RPC call being processed */ - QEMUD_MODE_TX_PACKET, /* Transmitting reply to RPC call */ - QEMUD_MODE_TLS_HANDSHAKE, /* Performing TLS handshake */ -}; - /* Whether we're passing reads & writes through a sasl SSF */ enum qemud_sasl_ssf { QEMUD_SASL_SSF_NONE = 0, @@ -87,6 +78,16 @@ enum qemud_sock_type { 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; + + struct qemud_client_message *next; +}; + /* Stores the per-client connection state */ struct qemud_client { virMutex lock; @@ -97,7 +98,6 @@ struct qemud_client { int watch; int readonly:1; int closing:1; - enum qemud_mode mode; struct sockaddr_storage addr; socklen_t addrlen; @@ -105,6 +105,7 @@ struct qemud_client { 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; @@ -117,12 +118,20 @@ struct qemud_client { char *saslUsername; #endif - unsigned int incomingSerial; - unsigned int outgoingSerial; - - char buffer [REMOTE_MESSAGE_MAX]; - unsigned int bufferLength; - unsigned int bufferOffset; + /* 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; /* 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 @@ -181,16 +190,20 @@ void qemudLog(int priority, const char * int qemudSetCloseExec(int fd); int qemudSetNonBlock(int fd); -unsigned int +int remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client); + struct qemud_client *client, + struct qemud_client_message *req); -void qemudDispatchClientWrite(struct qemud_server *server, - struct qemud_client *client); +int qemudRegisterClientEvent(struct qemud_server *server, + struct qemud_client *client, + int update); -#if HAVE_POLKIT -int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); -#endif +void qemudDispatchClientFailure(struct qemud_client *client); + +void +qemudClientMessageQueuePush(struct qemud_client_message **queue, + struct qemud_client_message *msg); int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -198,4 +211,9 @@ int remoteRelayDomainEvent (virConnectPt int detail, void *opaque); + +#if HAVE_POLKIT +int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); #endif + +#endif diff --git a/qemud/remote.c b/qemud/remote.c --- a/qemud/remote.c +++ b/qemud/remote.c @@ -111,6 +111,7 @@ static const dispatch_data const dispatc /* Prototypes */ static void remoteDispatchDomainEventSend (struct qemud_client *client, + struct qemud_client_message *msg, virDomainPtr dom, int event, int detail); @@ -219,9 +220,10 @@ remoteDispatchConnError (remote_error *r * Server object is unlocked * Client object is locked */ -unsigned int +int remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client) + struct qemud_client *client, + struct qemud_client_message *msg) { XDR xdr; remote_message_header req, rep; @@ -229,7 +231,8 @@ remoteDispatchClientRequest (struct qemu dispatch_args args; dispatch_ret ret; const dispatch_data *data = NULL; - int rv = -1, len; + int rv = -1; + unsigned int len; virConnectPtr conn = NULL; memset(&args, 0, sizeof args); @@ -237,7 +240,10 @@ remoteDispatchClientRequest (struct qemu memset(&rerr, 0, sizeof rerr); /* Parse the header. */ - xdrmem_create (&xdr, client->buffer, client->bufferLength, XDR_DECODE); + xdrmem_create (&xdr, + msg->buffer + REMOTE_MESSAGE_HEADER_XDR_LEN, + msg->bufferLength - REMOTE_MESSAGE_HEADER_XDR_LEN, + XDR_DECODE); if (!xdr_remote_message_header (&xdr, &req)) goto fatal_error; @@ -333,10 +339,10 @@ rpc_error: rep.status = rv < 0 ? REMOTE_ERROR : REMOTE_OK; /* Serialise the return header. */ - xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE); len = 0; /* We'll come back and write this later. */ - if (!xdr_int (&xdr, &len)) { + if (!xdr_u_int (&xdr, &len)) { if (rv == 0) xdr_free (data->ret_filter, (char*)&ret); goto fatal_error; } @@ -364,17 +370,21 @@ rpc_error: if (xdr_setpos (&xdr, 0) == 0) goto fatal_error; - if (!xdr_int (&xdr, &len)) + if (!xdr_u_int (&xdr, &len)) goto fatal_error; xdr_destroy (&xdr); - return len; + + msg->bufferLength = len; + msg->bufferOffset = 0; + + return 0; fatal_error: /* Seriously bad stuff happened, so we'll kill off this client and not send back any RPC error */ xdr_destroy (&xdr); - return 0; + return -1; } int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, @@ -386,9 +396,20 @@ int remoteRelayDomainEvent (virConnectPt struct qemud_client *client = opaque; REMOTE_DEBUG("Relaying domain event %d %d", event, detail); - if(client) { - remoteDispatchDomainEventSend (client, dom, event, detail); - qemudDispatchClientWrite(client->server,client); + if (client) { + struct qemud_client_message *ev; + + if (VIR_ALLOC(ev) < 0) + return -1; + + virMutexLock(&client->lock); + + remoteDispatchDomainEventSend (client, ev, dom, event, detail); + + if (qemudRegisterClientEvent(client->server, client, 1) < 0) + qemudDispatchClientFailure(client); + + virMutexUnlock(&client->lock); } return 0; } @@ -4202,13 +4223,14 @@ remoteDispatchDomainEventsDeregister (st static void remoteDispatchDomainEventSend (struct qemud_client *client, + struct qemud_client_message *msg, virDomainPtr dom, int event, int detail) { remote_message_header rep; XDR xdr; - int len; + unsigned int len; remote_domain_event_ret data; if (!client) @@ -4222,11 +4244,11 @@ remoteDispatchDomainEventSend (struct qe rep.status = REMOTE_OK; /* Serialise the return header and event. */ - xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE); len = 0; /* We'll come back and write this later. */ - if (!xdr_int (&xdr, &len)) { - /*remoteDispatchError (client, NULL, "%s", _("xdr_int failed (1)"));*/ + if (!xdr_u_int (&xdr, &len)) { + /*remoteDispatchError (client, NULL, "%s", _("xdr_u_int failed (1)"));*/ xdr_destroy (&xdr); return; } @@ -4254,8 +4276,8 @@ remoteDispatchDomainEventSend (struct qe return; } - if (!xdr_int (&xdr, &len)) { - /*remoteDispatchError (client, NULL, "%s", _("xdr_int failed (2)"));*/ + if (!xdr_u_int (&xdr, &len)) { + /*remoteDispatchError (client, NULL, "%s", _("xdr_u_int failed (2)"));*/ xdr_destroy (&xdr); return; } @@ -4263,9 +4285,10 @@ remoteDispatchDomainEventSend (struct qe xdr_destroy (&xdr); /* Send it. */ - client->mode = QEMUD_MODE_TX_PACKET; - client->bufferLength = len; - client->bufferOffset = 0; + msg->async = 1; + msg->bufferLength = len; + msg->bufferOffset = 0; + qemudClientMessageQueuePush(&client->tx, msg); } /*----- Helpers. -----*/ diff --git a/qemud/test_libvirtd.aug b/qemud/test_libvirtd.aug --- a/qemud/test_libvirtd.aug +++ b/qemud/test_libvirtd.aug @@ -246,6 +246,19 @@ max_clients = 20 # of clients allowed min_workers = 5 max_workers = 20 + +# Total global limit on concurrent RPC calls. Should be +# at least as large as max_workers. Beyond this, RPC requests +# will be read into memory and queued. This directly impact +# memory usage, currently each request requires 256 KB of +# memory. So by default upto 5 MB of memory is used +max_requests = 20 + +# Limit on concurrent requests from a single client +# connection. To avoid one client monopolizing the server +# this should be a small fraction of the global max_requests +# and max_workers parameter +max_client_requests = 5 " test Libvirtd.lns get conf = @@ -499,3 +512,16 @@ max_workers = 20 { "#comment" = "of clients allowed"} { "min_workers" = "5" } { "max_workers" = "20" } + { "#empty" } + { "#comment" = "Total global limit on concurrent RPC calls. Should be" } + { "#comment" = "at least as large as max_workers. Beyond this, RPC requests" } + { "#comment" = "will be read into memory and queued. This directly impact" } + { "#comment" = "memory usage, currently each request requires 256 KB of" } + { "#comment" = "memory. So by default upto 5 MB of memory is used" } + { "max_requests" = "20" } + { "#empty" } + { "#comment" = "Limit on concurrent requests from a single client" } + { "#comment" = "connection. To avoid one client monopolizing the server" } + { "#comment" = "this should be a small fraction of the global max_requests" } + { "#comment" = "and max_workers parameter" } + { "max_client_requests" = "5" } diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -5663,13 +5663,13 @@ prepareCall(virConnectPtr conn, /* Length must include the length word itself (always encoded in * 4 bytes as per RFC 4506). */ - rv->bufferLength += 4; + rv->bufferLength += REMOTE_MESSAGE_HEADER_XDR_LEN; /* Encode the length word. */ - xdrmem_create (&xdr, rv->buffer, 4, XDR_ENCODE); - if (!xdr_int (&xdr, (int *)&rv->bufferLength)) { + xdrmem_create (&xdr, rv->buffer, REMOTE_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE); + if (!xdr_u_int (&xdr, &rv->bufferLength)) { error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, - _("xdr_int (length word)")); + _("xdr_u_int (length word)")); goto error; } xdr_destroy (&xdr); @@ -5965,20 +5965,26 @@ static int processCallRecvLen(virConnectPtr conn, struct private_data *priv, int in_open) { XDR xdr; - int len; + unsigned int len; xdrmem_create (&xdr, priv->buffer, priv->bufferLength, XDR_DECODE); - if (!xdr_int (&xdr, &len)) { + if (!xdr_u_int (&xdr, &len)) { error (in_open ? NULL : conn, - VIR_ERR_RPC, _("xdr_int (length word, reply)")); + VIR_ERR_RPC, _("xdr_u_int (length word, reply)")); return -1; } xdr_destroy (&xdr); + if (len < REMOTE_MESSAGE_HEADER_XDR_LEN) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("packet received from server too small")); + return -1; + } + /* Length includes length word - adjust to real length to read. */ - len -= 4; - - if (len < 0 || len > REMOTE_MESSAGE_MAX) { + len -= REMOTE_MESSAGE_HEADER_XDR_LEN; + + if (len > REMOTE_MESSAGE_MAX) { error (in_open ? NULL : conn, VIR_ERR_RPC, _("packet received from server too large")); return -1; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
Finally I've also made use of ssize_t as suggested, this also made me notice one place where we didn't propagate the EAGAIN case of ret=0 correctly.
I compared our two patched versions and it all looks fine. It's good that you made those s/4/REMOTE_MESSAGE_HEADER_XDR_LEN/ changes, too. One final nit: ...
+static struct qemud_client_message * +qemudClientMessageQueueServe(struct qemud_client_message **queue) +{ + struct qemud_client_message *tmp = *queue; + + if (tmp) { + *queue = tmp->next; + tmp->next = NULL; + } else { + *queue = NULL; + }
You can remove that "else" block, since we already know *queue is NULL there.

The libvirtd.conf file has three parameters max_clients min_workers max_workers When the daemon starts up it spawns min_workers threads. It accepts connections from upto max_clients. I never implemented the logic to auto-spawn more threads upto max_workers though. This patch addresses that. Upon accept()ing a client connection if the number of clients is greater than the number of active worker threads, we spawn another worker, unless we've hit the max workers limit. If the number of clients is greater than the max_workers, this means some clients may have to wait for other clients requests to finish before eing processed. No great problem This also fixes a shutdown problem. We were marking the threads as detached, but still calling pthread_join() on them. This gives an error on Linux, but just hangs on Solaris while it tries to join a thread that has no intention of exiting. So during shutdown we set a 'quit' flag on the worker, and then broadcast a signal to wake it up from its condition variable sleep. Upon wakup it notices the quit flag and exits, allowing us to join & cleanup. qemud.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- qemud.h | 13 +++++++ 2 files changed, 106 insertions(+), 16 deletions(-) Daniel diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -167,7 +167,7 @@ static void sig_handler(int sig, siginfo 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, @@ -1248,6 +1248,20 @@ static int qemudDispatchServer(struct qe 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].active) { + if (qemudStartWorker(server, &server->workers[i]) < 0) + return -1; + server->nactiveworkers++; + break; + } + } + } + + return 0; cleanup: @@ -1303,19 +1317,28 @@ static struct qemud_client *qemudPending static void *qemudWorker(void *data) { - struct qemud_server *server = data; + struct qemud_worker *worker = data; + struct qemud_server *server = worker->server; while (1) { struct qemud_client *client = NULL; struct qemud_client_message *reply; virMutexLock(&server->lock); - while ((client = qemudPendingJob(server)) == NULL) { + while (((client = qemudPendingJob(server)) == NULL) && + !worker->quit) { if (virCondWait(&server->job, &server->lock) < 0) { virMutexUnlock(&server->lock); return NULL; } } + if (worker->quit) { + if (client) + virMutexUnlock(&client->lock); + virMutexUnlock(&server->lock); + return NULL; + } + worker->processing = 1; virMutexUnlock(&server->lock); /* We own a locked client now... */ @@ -1342,9 +1365,40 @@ static void *qemudWorker(void *data) client->refs--; virMutexUnlock(&client->lock); + + virMutexLock(&server->lock); + worker->processing = 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->active) + return -1; + + worker->server = server; + worker->active = 1; + worker->quit = 0; + worker->processing = 0; + + if (pthread_create(&worker->thread, + &attr, + qemudWorker, + worker) != 0) { + worker->active = 0; + worker->server = NULL; + return -1; + } + + return 0; +} + /* * Read data into buffer using wire decoding (plain or TLS) @@ -1888,21 +1942,19 @@ static int qemudRunLoop(struct qemud_ser virMutexLock(&server->lock); - server->nworkers = min_workers; + 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 < server->nworkers ; i++) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, 1); - - pthread_create(&server->workers[i], - &attr, - qemudWorker, - server); + for (i = 0 ; i < min_workers ; i++) { + if (qemudStartWorker(server, &server->workers[i]) < 0) + goto cleanup; + server->nactiveworkers++; } for (;;) { @@ -1948,6 +2000,26 @@ static int qemudRunLoop(struct qemud_ser } } + /* 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].active && + !server->workers[i].processing) { + server->workers[i].quit = 1; + + virCondBroadcast(&server->job); + virMutexUnlock(&server->lock); + pthread_join(server->workers[i].thread, NULL); + virMutexLock(&server->lock); + server->workers[i].active = 0; + server->nactiveworkers--; + } + } + /* Unregister any timeout that's active, since we * just had an event processed */ @@ -1963,11 +2035,18 @@ static int qemudRunLoop(struct qemud_ser } } +cleanup: for (i = 0 ; i < server->nworkers ; i++) { - pthread_t thread = server->workers[i]; + if (!server->workers[i].active) + continue; + + server->workers[i].quit = 1; + virCondBroadcast(&server->job); + virMutexUnlock(&server->lock); - pthread_join(thread, NULL); + pthread_join(server->workers[i].thread, NULL); virMutexLock(&server->lock); + server->workers[i].active = 0; } VIR_FREE(server->workers); diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -159,13 +159,24 @@ struct qemud_socket { struct qemud_socket *next; }; +struct qemud_worker { + pthread_t thread; + int active :1; + int processing :1; + int quit : 1; + + /* back-pointer to our server */ + struct qemud_server *server; +}; + /* Main server state */ struct qemud_server { virMutex lock; virCond job; int nworkers; - pthread_t *workers; + int nactiveworkers; + struct qemud_worker *workers; int nsockets; struct qemud_socket *sockets; int nclients; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:46:08PM +0000, Daniel P. Berrange wrote:
@@ -1948,6 +2000,26 @@ static int qemudRunLoop(struct qemud_ser } }
+ /* 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].active && + !server->workers[i].processing) { + server->workers[i].quit = 1; + + virCondBroadcast(&server->job); + virMutexUnlock(&server->lock); + pthread_join(server->workers[i].thread, NULL); + virMutexLock(&server->lock); + server->workers[i].active = 0; + server->nactiveworkers--; + } + } +
Doesn't this cause the main loop to hang -- eg. if we happen to try to kill of a worker which is doing some lengthy operation?
+struct qemud_worker { + pthread_t thread; + int active :1; + int processing :1; + int quit : 1;
I guess maybe I'm unclear about the meaning of these flags. What's the difference between active & processing? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora

On Fri, Jan 16, 2009 at 12:10:35PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:08PM +0000, Daniel P. Berrange wrote:
@@ -1948,6 +2000,26 @@ static int qemudRunLoop(struct qemud_ser } }
+ /* 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].active && + !server->workers[i].processing) { + server->workers[i].quit = 1; + + virCondBroadcast(&server->job); + virMutexUnlock(&server->lock); + pthread_join(server->workers[i].thread, NULL); + virMutexLock(&server->lock); + server->workers[i].active = 0; + server->nactiveworkers--; + } + } +
Doesn't this cause the main loop to hang -- eg. if we happen to try to kill of a worker which is doing some lengthy operation?
If the worker is currently processing an RPC call, its 'processing' flag will be set, and thus we won't consider it a candidate for killing off.
+struct qemud_worker { + pthread_t thread; + int active :1; + int processing :1; + int quit : 1;
I guess maybe I'm unclear about the meaning of these flags. What's the difference between active & processing?
I'll add comments to this struct before commiting. Their meanings are: active: the thread actually exists processing: the thread is currently processing an RPC call quit: the thread has been asked to quit At startup we malloc enough 'struct qemud_worker' objects to hold the entire 'max_workers' set, but we only start threads for 'min_workers. This is what the 'active' field tracks. The 'processing' flag is there so that when we want to kill off a thread, we won't block waiting for a thread that's currently busy working. And the 'quit' flag is that condition that a thread checks upon waking up from its condition variable sleep to see if it was asked to quit. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Fri, Jan 16, 2009 at 12:10:35PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:08PM +0000, Daniel P. Berrange wrote:
@@ -1948,6 +2000,26 @@ static int qemudRunLoop(struct qemud_ser } }
+ /* 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].active && + !server->workers[i].processing) { + server->workers[i].quit = 1; + + virCondBroadcast(&server->job); + virMutexUnlock(&server->lock); + pthread_join(server->workers[i].thread, NULL); + virMutexLock(&server->lock); + server->workers[i].active = 0; + server->nactiveworkers--; + } + } +
Doesn't this cause the main loop to hang -- eg. if we happen to try to kill of a worker which is doing some lengthy operation?
If the worker is currently processing an RPC call, its 'processing' flag will be set, and thus we won't consider it a candidate for killing off.
+struct qemud_worker { + pthread_t thread; + int active :1; + int processing :1; + int quit : 1;
I guess maybe I'm unclear about the meaning of these flags. What's the difference between active & processing?
I'll add comments to this struct before commiting. Their meanings are:
active: the thread actually exists processing: the thread is currently processing an RPC call
Please consider using some name other than "active", since it is so close in meaning to "processing". Maybe something like "created", "live" or "alive"?
quit: the thread has been asked to quit
At startup we malloc enough 'struct qemud_worker' objects to hold the entire 'max_workers' set, but we only start threads for 'min_workers. This is what the 'active' field tracks. The 'processing' flag is there so that when we want to kill off a thread, we won't block waiting for a thread that's currently busy working. And the 'quit' flag is that condition that a thread checks upon waking up from its condition variable sleep to see if it was asked to quit.
Regards, Daniel

On Fri, Jan 16, 2009 at 03:07:21PM +0100, Jim Meyering wrote:
Please consider using some name other than "active", since it is so close in meaning to "processing". Maybe something like "created", "live" or "alive"?
Indicates that it has a thread, so how about 'has_thread'? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The libvirtd.conf file has three parameters
max_clients min_workers max_workers
When the daemon starts up it spawns min_workers threads. It accepts connections from upto max_clients. I never implemented the logic to auto-spawn more threads upto max_workers though. ...
Other than the request to rename "active", and the signedness nits below, this looks fine. ACK
diff --git a/qemud/qemud.h b/qemud/qemud.h ... struct qemud_server { virMutex lock; virCond job;
int nworkers; - pthread_t *workers; + int nactiveworkers; + struct qemud_worker *workers;
It'd be nice to make all of these "n*" members unsigned, assuming that they should never go negative.
int nsockets; struct qemud_socket *sockets; int nclients;

On Tue, Jan 20, 2009 at 11:21:03AM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
The libvirtd.conf file has three parameters
max_clients min_workers max_workers
When the daemon starts up it spawns min_workers threads. It accepts connections from upto max_clients. I never implemented the logic to auto-spawn more threads upto max_workers though. ...
Other than the request to rename "active", and the signedness nits below, this looks fine. ACK
diff --git a/qemud/qemud.h b/qemud/qemud.h ... struct qemud_server { virMutex lock; virCond job;
int nworkers; - pthread_t *workers; + int nactiveworkers; + struct qemud_worker *workers;
It'd be nice to make all of these "n*" members unsigned, assuming that they should never go negative.
I've not changed this to unsigned, because again we compare it in various places to the signed in config setting, and I'd prefer we address all this in one go
int nsockets; struct qemud_socket *sockets; int nclients;
Here is an update with the 3 flags renamed to something more clear Daniel diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -167,7 +167,7 @@ static void sig_handler(int sig, siginfo 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, @@ -1249,6 +1249,20 @@ static int qemudDispatchServer(struct qe 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: @@ -1304,19 +1318,28 @@ static struct qemud_client *qemudPending static void *qemudWorker(void *data) { - struct qemud_server *server = data; + struct qemud_worker *worker = data; + struct qemud_server *server = worker->server; while (1) { struct qemud_client *client = NULL; struct qemud_client_message *reply; virMutexLock(&server->lock); - while ((client = qemudPendingJob(server)) == NULL) { + 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... */ @@ -1343,9 +1366,40 @@ static void *qemudWorker(void *data) 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) @@ -1942,21 +1996,19 @@ static int qemudRunLoop(struct qemud_ser virMutexLock(&server->lock); - server->nworkers = min_workers; + 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 < server->nworkers ; i++) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, 1); - - pthread_create(&server->workers[i], - &attr, - qemudWorker, - server); + for (i = 0 ; i < min_workers ; i++) { + if (qemudStartWorker(server, &server->workers[i]) < 0) + goto cleanup; + server->nactiveworkers++; } for (;;) { @@ -2002,6 +2054,26 @@ static int qemudRunLoop(struct qemud_ser } } + /* 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--; + } + } + /* Unregister any timeout that's active, since we * just had an event processed */ @@ -2017,11 +2089,18 @@ static int qemudRunLoop(struct qemud_ser } } +cleanup: for (i = 0 ; i < server->nworkers ; i++) { - pthread_t thread = server->workers[i]; + if (!server->workers[i].hasThread) + continue; + + server->workers[i].quitRequest = 1; + virCondBroadcast(&server->job); + virMutexUnlock(&server->lock); - pthread_join(thread, NULL); + pthread_join(server->workers[i].thread, NULL); virMutexLock(&server->lock); + server->workers[i].hasThread = 0; } VIR_FREE(server->workers); diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -157,13 +157,24 @@ struct qemud_socket { 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 nworkers; - pthread_t *workers; + int nactiveworkers; + struct qemud_worker *workers; int nsockets; struct qemud_socket *sockets; int nclients; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 20, 2009 at 11:21:03AM +0100, Jim Meyering wrote: ...
It'd be nice to make all of these "n*" members unsigned, assuming that they should never go negative.
I've not changed this to unsigned, because again we compare it in various places to the signed in config setting, and I'd prefer we address all this in one go
Makes sense.
int nsockets; struct qemud_socket *sockets; int nclients;
Here is an update with the 3 flags renamed to something more clear
Thanks. Good to go! ...
diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -157,13 +157,24 @@ struct qemud_socket { struct qemud_socket *next; };
+struct qemud_worker { + pthread_t thread; + int hasThread :1; + int processingCall :1; + int quitRequest : 1;

Remove use of various non thread safe functions, specifically strtok getmntent getgrnam getpwuid gethostbyname I thought we also had to remove use of readdir(), but it turns out that *is* threadsafe provided you only use each DIR* object from one thread at a time, which is fine for our needs. The readdir_r() function is absolutely horrific to use safely so its just as well we don't need to. For the gethostbyname removal in Xen, I took the opportunity to switch to getaddrinfo(), since even gethostbyname_r is deprecated these days - not IPv6 aware. configure.in | 2 proxy/libvirt_proxy.c | 3 qemud/qemud.c | 9 +- src/lxc_container.c | 9 +- src/network_driver.c | 7 +- src/openvz_driver.c | 3 src/qemu_driver.c | 7 +- src/remote_internal.c | 4 - src/storage_backend_fs.c | 7 +- src/storage_driver.c | 7 +- src/uml_driver.c | 6 + src/xen_unified.c | 3 src/xen_unified.h | 10 +-- src/xend_internal.c | 147 +++++++++++++++++++++++++---------------------- 14 files changed, 123 insertions(+), 101 deletions(-) Daniel diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -75,7 +75,7 @@ dnl Availability of various common funct AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity getuid getgid]) dnl Availability of various not common threadsafe functions -AC_CHECK_FUNCS([strerror_r]) +AC_CHECK_FUNCS([strerror_r strtok_r getmntent_r getgrnam_r getpwuid_r]) dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h sys/poll.h syslog.h]) diff --git a/proxy/libvirt_proxy.c b/proxy/libvirt_proxy.c --- a/proxy/libvirt_proxy.c +++ b/proxy/libvirt_proxy.c @@ -76,9 +76,6 @@ proxyInitXen(void) { priv->handle = -1; priv->xendConfigVersion = -1; - priv->type = -1; - priv->len = -1; - priv->addr = NULL; priv->xshandle = NULL; priv->proxy = -1; diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -689,9 +689,11 @@ static int qemudInitPaths(struct qemud_s if (snprintf(server->logDir, PATH_MAX, "%s/log/libvirt/", LOCAL_STATE_DIR) >= PATH_MAX) goto snprintf_error; } else { + char buf[1024]; + struct passwd pwbuf; struct passwd *pw; - if (!(pw = getpwuid(uid))) { + if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) { VIR_ERROR(_("Failed to find user record for uid '%d': %s"), uid, strerror(errno)); return -1; @@ -2376,8 +2378,9 @@ remoteReadConfigFile (struct qemud_serve if (getuid() != 0) { VIR_WARN0(_("Cannot set group when not running as root")); } else { - struct group *grp = getgrnam(unix_sock_group); - if (!grp) { + char buf[1024]; + struct group grpdata, *grp; + if (getgrnam_r(unix_sock_group, &grpdata, buf, sizeof(buf), &grp) != 0 || !grp) { VIR_ERROR(_("Failed to lookup group '%s'"), unix_sock_group); goto free_and_fail; } diff --git a/src/lxc_container.c b/src/lxc_container.c --- a/src/lxc_container.c +++ b/src/lxc_container.c @@ -414,19 +414,20 @@ static int lxcContainerMountNewFS(virDom static int lxcContainerUnmountOldFS(void) { - struct mntent *mntent; + struct mntent mntent; char **mounts = NULL; int nmounts = 0; FILE *procmnt; int i; + char mntbuf[1024]; if (!(procmnt = setmntent("/proc/mounts", "r"))) { virReportSystemError(NULL, errno, "%s", _("failed to read /proc/mounts")); return -1; } - while ((mntent = getmntent(procmnt)) != NULL) { - if (!STRPREFIX(mntent->mnt_dir, "/.oldroot")) + while (getmntent_r(procmnt, &mntent, mntbuf, sizeof(mntbuf)) != NULL) { + if (!STRPREFIX(mntent.mnt_dir, "/.oldroot")) continue; if (VIR_REALLOC_N(mounts, nmounts+1) < 0) { @@ -434,7 +435,7 @@ static int lxcContainerUnmountOldFS(void lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } - if (!(mounts[nmounts++] = strdup(mntent->mnt_dir))) { + if (!(mounts[nmounts++] = strdup(mntent.mnt_dir))) { endmntent(procmnt); lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -131,7 +131,6 @@ networkAutostartConfigs(struct network_d static int networkStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; if (VIR_ALLOC(driverState) < 0) @@ -151,7 +150,11 @@ networkStartup(void) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { - if (!(pw = getpwuid(uid))) { + char buf[1024]; + struct passwd pwbuf; + struct passwd *pw; + + if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) { networkLog(NETWORK_ERR, _("Failed to find user record for uid '%d': %s\n"), uid, strerror(errno)); goto out_of_memory; diff --git a/src/openvz_driver.c b/src/openvz_driver.c --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -448,11 +448,12 @@ openvzGenerateContainerVethName(int veid if ( (ret = openvzReadConfigParam(veid, "NETIF", temp, sizeof(temp))) <= 0) { snprintf(temp, sizeof(temp), "eth0"); } else { + char *saveptr; char *s; int max = 0; /* get maximum interface number (actually, it is the last one) */ - for (s=strtok(temp, ";"); s; s=strtok(NULL, ";")) { + for (s=strtok_r(temp, ";", &saveptr); s; s=strtok_r(NULL, ";", &saveptr)) { int x; if (sscanf(s, "ifname=eth%d", &x) != 1) return NULL; diff --git a/src/qemu_driver.c b/src/qemu_driver.c --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -265,7 +265,6 @@ cleanup: static int qemudStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; @@ -304,7 +303,11 @@ qemudStartup(void) { "%s/run/libvirt/qemu/", LOCAL_STATE_DIR) == -1) goto out_of_memory; } else { - if (!(pw = getpwuid(uid))) { + char buf[1024]; + struct passwd pwbuf; + struct passwd *pw; + + if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) { qemudLog(QEMUD_ERR, _("Failed to find user record for uid '%d': %s\n"), uid, strerror(errno)); goto error; diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -603,10 +603,12 @@ doRemoteOpen (virConnectPtr conn, case trans_unix: { if (!sockname) { if (flags & VIR_DRV_OPEN_REMOTE_USER) { + char buf[1024]; + struct passwd pwbuf; struct passwd *pw; uid_t uid = getuid(); - if (!(pw = getpwuid(uid))) { + if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) { virReportSystemError(conn, errno, _("unable to lookup user '%d'"), uid); diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -385,7 +385,8 @@ static int virStorageBackendFileSystemIsMounted(virConnectPtr conn, virStoragePoolObjPtr pool) { FILE *mtab; - struct mntent *ent; + struct mntent ent; + char buf[1024]; if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { virReportSystemError(conn, errno, @@ -394,8 +395,8 @@ virStorageBackendFileSystemIsMounted(vir return -1; } - while ((ent = getmntent(mtab)) != NULL) { - if (STREQ(ent->mnt_dir, pool->def->target.path)) { + while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) { + if (STREQ(ent.mnt_dir, pool->def->target.path)) { fclose(mtab); return 1; } diff --git a/src/storage_driver.c b/src/storage_driver.c --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -108,7 +108,6 @@ storageDriverAutostart(virStorageDriverS static int storageDriverStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; @@ -125,7 +124,11 @@ storageDriverStartup(void) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { - if (!(pw = getpwuid(uid))) { + char buf[1024]; + struct passwd pwbuf; + struct passwd *pw; + + if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) { storageLog("Failed to find user record for uid '%d': %s", uid, strerror(errno)); goto out_of_memory; diff --git a/src/uml_driver.c b/src/uml_driver.c --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -309,9 +309,11 @@ cleanup: static int umlStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; + char buf[1024]; + struct passwd pwbuf; + struct passwd *pw; if (VIR_ALLOC(uml_driver) < 0) return -1; @@ -325,7 +327,7 @@ umlStartup(void) { /* Don't have a dom0 so start from 1 */ uml_driver->nextvmid = 1; - if (!(pw = getpwuid(uid))) { + if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) { umlLog(VIR_LOG_ERROR, _("Failed to find user record for uid '%d': %s\n"), uid, strerror(errno)); goto error; diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -258,9 +258,6 @@ xenUnifiedOpen (virConnectPtr conn, virC priv->handle = -1; priv->xendConfigVersion = -1; - priv->type = -1; - priv->len = -1; - priv->addr = NULL; priv->xshandle = NULL; priv->proxy = -1; diff --git a/src/xen_unified.h b/src/xen_unified.h --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -142,13 +142,11 @@ struct _xenUnifiedPrivate { int xendConfigVersion; /* XenD config version */ - /* XXX This code is not IPv6 aware. */ /* connection to xend */ - int type; /* PF_UNIX or PF_INET */ - int len; /* length of addr */ - struct sockaddr *addr; /* type of address used */ - struct sockaddr_un addr_un; /* the unix address */ - struct sockaddr_in addr_in; /* the inet address */ + struct sockaddr_storage addr; + socklen_t addrlen; + int addrfamily; + int addrprotocol; /* Keep track of the drivers which opened. We keep a yes/no flag * here for each driver, corresponding to the array drivers in diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -61,30 +61,6 @@ #endif /* PROXY */ -/** - * xend_connection_type: - * - * The connection to the Xen Daemon can be done either though a normal TCP - * socket or a local domain direct connection. - */ -enum xend_connection_type { - XEND_DOMAIN, - XEND_TCP, -}; - -/** - * xend: - * - * Structure associated to a connection to a Xen daemon - */ -struct xend { - int len; - int type; - struct sockaddr *addr; - struct sockaddr_un addr_un; - struct sockaddr_in addr_in; -}; - #ifndef PROXY static int @@ -132,7 +108,7 @@ do_connect(virConnectPtr xend) int no_slow_start = 1; xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) xend->privateData; - s = socket(priv->type, SOCK_STREAM, 0); + s = socket(priv->addrfamily, SOCK_STREAM, priv->addrprotocol); if (s == -1) { virXendError(xend, VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create a socket")); @@ -146,7 +122,7 @@ do_connect(virConnectPtr xend) sizeof(no_slow_start)); - if (connect(s, priv->addr, priv->len) == -1) { + if (connect(s, (struct sockaddr *)&priv->addr, priv->addrlen) == -1) { serrno = errno; close(s); errno = serrno; @@ -804,18 +780,16 @@ xenDaemonOpen_unix(virConnectPtr conn, c if ((conn == NULL) || (path == NULL)) return (-1); - addr = &priv->addr_un; + memset(&priv->addr, 0, sizeof(priv->addr)); + priv->addrfamily = AF_UNIX; + priv->addrprotocol = PF_UNIX; + priv->addrlen = sizeof(struct sockaddr_un); + + addr = (struct sockaddr_un *)&priv->addr; addr->sun_family = AF_UNIX; memset(addr->sun_path, 0, sizeof(addr->sun_path)); strncpy(addr->sun_path, path, sizeof(addr->sun_path)); - priv->len = sizeof(addr->sun_family) + strlen(addr->sun_path); - if ((unsigned int) priv->len > sizeof(addr->sun_path)) - priv->len = sizeof(addr->sun_path); - - priv->addr = (struct sockaddr *) addr; - priv->type = PF_UNIX; - return (0); } @@ -832,38 +806,71 @@ xenDaemonOpen_unix(virConnectPtr conn, c * Returns 0 in case of success, -1 in case of error. */ static int -xenDaemonOpen_tcp(virConnectPtr conn, const char *host, int port) -{ - struct in_addr ip; - struct hostent *pent; - xenUnifiedPrivatePtr priv; - - if ((conn == NULL) || (host == NULL) || (port <= 0)) - return (-1); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - pent = gethostbyname(host); - if (pent == NULL) { - if (inet_aton(host, &ip) == 0) { - virXendError(NULL, VIR_ERR_UNKNOWN_HOST, - _("gethostbyname failed: %s"), host); - errno = ESRCH; - return (-1); - } - } else { - memcpy(&ip, pent->h_addr_list[0], sizeof(ip)); - } - - priv->len = sizeof(struct sockaddr_in); - priv->addr = (struct sockaddr *) &priv->addr_in; - priv->type = PF_INET; - - priv->addr_in.sin_family = AF_INET; - priv->addr_in.sin_port = htons(port); - memcpy(&priv->addr_in.sin_addr, &ip, sizeof(ip)); - - return (0); +xenDaemonOpen_tcp(virConnectPtr conn, const char *host, const char *port) +{ + xenUnifiedPrivatePtr priv; + struct addrinfo *res, *r; + struct addrinfo hints; + int saved_errno = EINVAL; + int ret; + + if ((conn == NULL) || (host == NULL) || (port == NULL)) + return (-1); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + priv->addrlen = 0; + memset(&priv->addr, 0, sizeof(priv->addr)); + + // http://people.redhat.com/drepper/userapi-ipv6.html + memset (&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + ret = getaddrinfo (host, port, &hints, &res); + if (ret != 0) { + virXendError(NULL, VIR_ERR_UNKNOWN_HOST, + _("unable to resolve hostname '%s': %s"), + host, gai_strerror (ret)); + return -1; + } + + /* Try to connect to each returned address in turn. */ + for (r = res; r; r = r->ai_next) { + int sock; + + sock = socket (r->ai_family, SOCK_STREAM, r->ai_protocol); + if (sock == -1) { + saved_errno = errno; + continue; + } + + if (connect (sock, r->ai_addr, r->ai_addrlen) == -1) { + saved_errno = errno; + close (sock); + continue; + } + + priv->addrlen = r->ai_addrlen; + priv->addrfamily = r->ai_family; + priv->addrprotocol = r->ai_protocol; + memcpy(&priv->addr, + r->ai_addr, + r->ai_addrlen); + close(sock); + break; + } + + freeaddrinfo (res); + + if (!priv->addrlen) { + virReportSystemError(conn, saved_errno, + _("unable to connect to '%s:%s'"), + host, port); + return -1; + } + + return 0; } @@ -2765,14 +2772,18 @@ xenDaemonOpen(virConnectPtr conn, /* * try though http on port 8000 */ - ret = xenDaemonOpen_tcp(conn, "localhost", 8000); + ret = xenDaemonOpen_tcp(conn, "localhost", "8000"); if (ret < 0) goto failed; ret = xend_detect_config_version(conn); if (ret == -1) goto failed; } else if (STRCASEEQ (conn->uri->scheme, "http")) { - ret = xenDaemonOpen_tcp(conn, conn->uri->server, conn->uri->port); + char *port; + if (virAsprintf(&port, "%d", conn->uri->port) == -1) + goto failed; + ret = xenDaemonOpen_tcp(conn, conn->uri->server, port); + VIR_FREE(port); if (ret < 0) goto failed; ret = xend_detect_config_version(conn); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)? Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/); while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; } Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func: char *virUserHomeDirectory(uid_t uid); and hide all the horrific code in there. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Jan 16, 2009 at 12:25:02PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func:
char *virUserHomeDirectory(uid_t uid);
and hide all the horrific code in there.
yes +1, patch looks sane to me but that would be an improvement ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Jan 16, 2009 at 12:25:02PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func:
char *virUserHomeDirectory(uid_t uid);
and hide all the horrific code in there.
Here's an update with that usage in it diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -75,7 +75,7 @@ dnl Availability of various common funct AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity getuid getgid]) dnl Availability of various not common threadsafe functions -AC_CHECK_FUNCS([strerror_r]) +AC_CHECK_FUNCS([strerror_r strtok_r getmntent_r getgrnam_r getpwuid_r]) dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h sys/poll.h syslog.h]) diff --git a/proxy/libvirt_proxy.c b/proxy/libvirt_proxy.c --- a/proxy/libvirt_proxy.c +++ b/proxy/libvirt_proxy.c @@ -76,9 +76,6 @@ proxyInitXen(void) { priv->handle = -1; priv->xendConfigVersion = -1; - priv->type = -1; - priv->len = -1; - priv->addr = NULL; priv->xshandle = NULL; priv->proxy = -1; diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -688,20 +688,18 @@ static int qemudInitPaths(struct qemud_s if (snprintf(server->logDir, PATH_MAX, "%s/log/libvirt/", LOCAL_STATE_DIR) >= PATH_MAX) goto snprintf_error; } else { - struct passwd *pw; + char *userdir = virGetUserDirectory(NULL, uid); - if (!(pw = getpwuid(uid))) { - VIR_ERROR(_("Failed to find user record for uid '%d': %s"), - uid, strerror(errno)); - return -1; + if (snprintf(sockname, maxlen, "@%s/.libvirt/libvirt-sock", userdir) >= maxlen) { + VIR_FREE(userdir); + goto snprintf_error; } - if (snprintf(sockname, maxlen, "@%s/.libvirt/libvirt-sock", pw->pw_dir) >= maxlen) + if (snprintf(server->logDir, PATH_MAX, "%s/.libvirt/log", userdir) >= PATH_MAX) { + VIR_FREE(userdir); goto snprintf_error; - - if (snprintf(server->logDir, PATH_MAX, "%s/.libvirt/log", pw->pw_dir) >= PATH_MAX) - goto snprintf_error; - + } + VIR_FREE(userdir); } /* !remote */ return 0; @@ -2433,8 +2431,9 @@ remoteReadConfigFile (struct qemud_serve if (getuid() != 0) { VIR_WARN0(_("Cannot set group when not running as root")); } else { - struct group *grp = getgrnam(unix_sock_group); - if (!grp) { + char buf[1024]; + struct group grpdata, *grp; + if (getgrnam_r(unix_sock_group, &grpdata, buf, sizeof(buf), &grp) != 0 || !grp) { VIR_ERROR(_("Failed to lookup group '%s'"), unix_sock_group); goto free_and_fail; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -311,6 +311,7 @@ virAsprintf; virRun; virSkipSpaces; virKillProcess; +virGetUserDirectory; # uuid.h diff --git a/src/lxc_container.c b/src/lxc_container.c --- a/src/lxc_container.c +++ b/src/lxc_container.c @@ -414,19 +414,20 @@ static int lxcContainerMountNewFS(virDom static int lxcContainerUnmountOldFS(void) { - struct mntent *mntent; + struct mntent mntent; char **mounts = NULL; int nmounts = 0; FILE *procmnt; int i; + char mntbuf[1024]; if (!(procmnt = setmntent("/proc/mounts", "r"))) { virReportSystemError(NULL, errno, "%s", _("failed to read /proc/mounts")); return -1; } - while ((mntent = getmntent(procmnt)) != NULL) { - if (!STRPREFIX(mntent->mnt_dir, "/.oldroot")) + while (getmntent_r(procmnt, &mntent, mntbuf, sizeof(mntbuf)) != NULL) { + if (!STRPREFIX(mntent.mnt_dir, "/.oldroot")) continue; if (VIR_REALLOC_N(mounts, nmounts+1) < 0) { @@ -434,7 +435,7 @@ static int lxcContainerUnmountOldFS(void lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } - if (!(mounts[nmounts++] = strdup(mntent->mnt_dir))) { + if (!(mounts[nmounts++] = strdup(mntent.mnt_dir))) { endmntent(procmnt); lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -195,7 +195,6 @@ networkAutostartConfigs(struct network_d static int networkStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; int err; @@ -216,19 +215,22 @@ networkStartup(void) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { - if (!(pw = getpwuid(uid))) { - networkLog(NETWORK_ERR, _("Failed to find user record for uid '%d': %s\n"), - uid, strerror(errno)); + char *userdir = virGetUserDirectory(NULL, uid); + + if (!userdir) + goto error; + + if (virAsprintf(&driverState->logDir, + "%s/.libvirt/qemu/log", userdir) == -1) { + VIR_FREE(userdir); goto out_of_memory; } - if (virAsprintf(&driverState->logDir, - "%s/.libvirt/qemu/log", pw->pw_dir) == -1) - goto out_of_memory; - - if (virAsprintf(&base, "%s/.libvirt", pw->pw_dir) == -1) { + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) { + VIR_FREE(userdir); goto out_of_memory; } + VIR_FREE(userdir); } /* Configuration paths are either ~/.libvirt/qemu/... (session) or diff --git a/src/openvz_driver.c b/src/openvz_driver.c --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -448,11 +448,12 @@ openvzGenerateContainerVethName(int veid if ( (ret = openvzReadConfigParam(veid, "NETIF", temp, sizeof(temp))) <= 0) { snprintf(temp, sizeof(temp), "eth0"); } else { + char *saveptr; char *s; int max = 0; /* get maximum interface number (actually, it is the last one) */ - for (s=strtok(temp, ";"); s; s=strtok(NULL, ";")) { + for (s=strtok_r(temp, ";", &saveptr); s; s=strtok_r(NULL, ";", &saveptr)) { int x; if (sscanf(s, "ifname=eth%d", &x) != 1) return NULL; diff --git a/src/qemu_driver.c b/src/qemu_driver.c --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -382,7 +382,6 @@ next: static int qemudStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; @@ -421,27 +420,30 @@ qemudStartup(void) { "%s/run/libvirt/qemu/", LOCAL_STATE_DIR) == -1) goto out_of_memory; } else { - if (!(pw = getpwuid(uid))) { - qemudLog(QEMUD_ERR, _("Failed to find user record for uid '%d': %s\n"), - uid, strerror(errno)); - goto error; - } + char *userdir = virGetUserDirectory(NULL, uid); + if (!userdir) + goto error; if (virAsprintf(&qemu_driver->logDir, - "%s/.libvirt/qemu/log", pw->pw_dir) == -1) - goto out_of_memory; - - if (virAsprintf(&base, "%s/.libvirt", pw->pw_dir) == -1) - goto out_of_memory; + "%s/.libvirt/qemu/log", userdir) == -1) { + VIR_FREE(userdir); + goto out_of_memory; + } + + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) { + VIR_FREE(userdir); + goto out_of_memory; + } + VIR_FREE(userdir); if (virAsprintf(&qemu_driver->stateDir, "%s/qemu/run", base) == -1) goto out_of_memory; } if (virFileMakePath(qemu_driver->stateDir) < 0) { - qemudLog(QEMUD_ERR, _("Failed to create state dir '%s': %s\n"), - qemu_driver->stateDir, strerror(errno)); - goto error; + qemudLog(QEMUD_ERR, _("Failed to create state dir '%s': %s\n"), + qemu_driver->stateDir, strerror(errno)); + goto error; } /* Configuration paths are either ~/.libvirt/qemu/... (session) or diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -605,19 +605,16 @@ doRemoteOpen (virConnectPtr conn, case trans_unix: { if (!sockname) { if (flags & VIR_DRV_OPEN_REMOTE_USER) { - struct passwd *pw; - uid_t uid = getuid(); - - if (!(pw = getpwuid(uid))) { - virReportSystemError(conn, errno, - _("unable to lookup user '%d'"), - uid); + char *userdir = virGetUserDirectory(conn, getuid()); + + if (!userdir) goto failed; + + if (virAsprintf(&sockname, "@%s" LIBVIRTD_USER_UNIX_SOCKET, userdir) < 0) { + VIR_FREE(userdir); + goto out_of_memory; } - - if (virAsprintf(&sockname, "@%s" LIBVIRTD_USER_UNIX_SOCKET, pw->pw_dir) < 0) - goto out_of_memory; - + VIR_FREE(userdir); } else { if (flags & VIR_DRV_OPEN_REMOTE_RO) sockname = strdup (LIBVIRTD_PRIV_UNIX_SOCKET_RO); diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -385,7 +385,8 @@ static int virStorageBackendFileSystemIsMounted(virConnectPtr conn, virStoragePoolObjPtr pool) { FILE *mtab; - struct mntent *ent; + struct mntent ent; + char buf[1024]; if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { virReportSystemError(conn, errno, @@ -394,8 +395,8 @@ virStorageBackendFileSystemIsMounted(vir return -1; } - while ((ent = getmntent(mtab)) != NULL) { - if (STREQ(ent->mnt_dir, pool->def->target.path)) { + while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) { + if (STREQ(ent.mnt_dir, pool->def->target.path)) { fclose(mtab); return 1; } diff --git a/src/storage_driver.c b/src/storage_driver.c --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -108,7 +108,6 @@ storageDriverAutostart(virStorageDriverS static int storageDriverStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; @@ -125,16 +124,17 @@ storageDriverStartup(void) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { - if (!(pw = getpwuid(uid))) { - storageLog("Failed to find user record for uid '%d': %s", - uid, strerror(errno)); + char *userdir = virGetUserDirectory(NULL, uid); + + if (!userdir) + goto error; + + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) { + storageLog("out of memory in virAsprintf"); + VIR_FREE(userdir); goto out_of_memory; } - - if (virAsprintf(&base, "%s/.libvirt", pw->pw_dir) == -1) { - storageLog("out of memory in virAsprintf"); - goto out_of_memory; - } + VIR_FREE(userdir); } /* Configuration paths are either ~/.libvirt/storage/... (session) or diff --git a/src/uml_driver.c b/src/uml_driver.c --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -309,9 +309,9 @@ cleanup: static int umlStartup(void) { uid_t uid = geteuid(); - struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; + char *userdir = NULL; if (VIR_ALLOC(uml_driver) < 0) return -1; @@ -325,11 +325,9 @@ umlStartup(void) { /* Don't have a dom0 so start from 1 */ uml_driver->nextvmid = 1; - if (!(pw = getpwuid(uid))) { - umlLog(VIR_LOG_ERROR, _("Failed to find user record for uid '%d': %s\n"), - uid, strerror(errno)); + userdir = virGetUserDirectory(NULL, uid); + if (!userdir) goto error; - } if (!uid) { if (virAsprintf(¨_driver->logDir, @@ -339,16 +337,17 @@ umlStartup(void) { if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { + if (virAsprintf(¨_driver->logDir, - "%s/.libvirt/uml/log", pw->pw_dir) == -1) + "%s/.libvirt/uml/log", userdir) == -1) goto out_of_memory; - if (virAsprintf(&base, "%s/.libvirt", pw->pw_dir) == -1) + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) goto out_of_memory; } if (virAsprintf(¨_driver->monitorDir, - "%s/.uml", pw->pw_dir) == -1) + "%s/.uml", userdir) == -1) goto out_of_memory; /* Configuration paths are either ~/.libvirt/uml/... (session) or @@ -403,6 +402,8 @@ umlStartup(void) { umlAutostartConfigs(uml_driver); umlDriverUnlock(uml_driver); + VIR_FREE(userdir); + return 0; out_of_memory: @@ -410,6 +411,7 @@ out_of_memory: "%s", _("umlStartup: out of memory\n")); error: + VIR_FREE(userdir); VIR_FREE(base); umlDriverUnlock(uml_driver); umlShutdown(); diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -49,6 +49,9 @@ #include <paths.h> #endif #include <netdb.h> +#ifdef HAVE_GETPWUID_R +#include <pwd.h> +#endif #include "virterror_internal.h" #include "logging.h" @@ -1431,3 +1434,37 @@ int virKillProcess(pid_t pid, int sig) return kill(pid, sig); #endif } + + +#ifdef HAVE_GETPWUID_R +char *virGetUserDirectory(virConnectPtr conn, + uid_t uid) +{ + char *strbuf; + char *ret; + struct passwd pwbuf; + struct passwd *pw; + size_t strbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (VIR_ALLOC_N(strbuf, strbuflen) < 0) { + virReportOOMError(conn); + return NULL; + } + + if (getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw) != 0) { + virReportSystemError(conn, errno, + _("Failed to find user record for uid '%d'"), + uid); + VIR_FREE(strbuf); + return NULL; + } + + ret = strdup(pw->pw_dir); + + VIR_FREE(strbuf); + if (!ret) + virReportOOMError(conn); + + return NULL; +} +#endif diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -172,4 +172,9 @@ char *virGetHostname(void); int virKillProcess(pid_t pid, int sig); +#ifdef HAVE_GETPWUID_R +char *virGetUserDirectory(virConnectPtr conn, + uid_t uid); +#endif + #endif /* __VIR_UTIL_H__ */ diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -278,9 +278,6 @@ xenUnifiedOpen (virConnectPtr conn, virC priv->handle = -1; priv->xendConfigVersion = -1; - priv->type = -1; - priv->len = -1; - priv->addr = NULL; priv->xshandle = NULL; priv->proxy = -1; diff --git a/src/xen_unified.h b/src/xen_unified.h --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -136,13 +136,11 @@ struct _xenUnifiedPrivate { int xendConfigVersion; /* XenD config version */ - /* XXX This code is not IPv6 aware. */ /* connection to xend */ - int type; /* PF_UNIX or PF_INET */ - int len; /* length of addr */ - struct sockaddr *addr; /* type of address used */ - struct sockaddr_un addr_un; /* the unix address */ - struct sockaddr_in addr_in; /* the inet address */ + struct sockaddr_storage addr; + socklen_t addrlen; + int addrfamily; + int addrprotocol; struct xs_handle *xshandle; /* handle to talk to the xenstore */ diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -69,30 +69,6 @@ #define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 3 #endif -/** - * xend_connection_type: - * - * The connection to the Xen Daemon can be done either though a normal TCP - * socket or a local domain direct connection. - */ -enum xend_connection_type { - XEND_DOMAIN, - XEND_TCP, -}; - -/** - * xend: - * - * Structure associated to a connection to a Xen daemon - */ -struct xend { - int len; - int type; - struct sockaddr *addr; - struct sockaddr_un addr_un; - struct sockaddr_in addr_in; -}; - #ifndef PROXY static int @@ -140,7 +116,7 @@ do_connect(virConnectPtr xend) int no_slow_start = 1; xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) xend->privateData; - s = socket(priv->type, SOCK_STREAM, 0); + s = socket(priv->addrfamily, SOCK_STREAM, priv->addrprotocol); if (s == -1) { virXendError(xend, VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create a socket")); @@ -154,7 +130,7 @@ do_connect(virConnectPtr xend) sizeof(no_slow_start)); - if (connect(s, priv->addr, priv->len) == -1) { + if (connect(s, (struct sockaddr *)&priv->addr, priv->addrlen) == -1) { serrno = errno; close(s); errno = serrno; @@ -767,18 +743,16 @@ xenDaemonOpen_unix(virConnectPtr conn, c if ((conn == NULL) || (path == NULL)) return (-1); - addr = &priv->addr_un; + memset(&priv->addr, 0, sizeof(priv->addr)); + priv->addrfamily = AF_UNIX; + priv->addrprotocol = PF_UNIX; + priv->addrlen = sizeof(struct sockaddr_un); + + addr = (struct sockaddr_un *)&priv->addr; addr->sun_family = AF_UNIX; memset(addr->sun_path, 0, sizeof(addr->sun_path)); strncpy(addr->sun_path, path, sizeof(addr->sun_path)); - priv->len = sizeof(addr->sun_family) + strlen(addr->sun_path); - if ((unsigned int) priv->len > sizeof(addr->sun_path)) - priv->len = sizeof(addr->sun_path); - - priv->addr = (struct sockaddr *) addr; - priv->type = PF_UNIX; - return (0); } @@ -795,38 +769,71 @@ xenDaemonOpen_unix(virConnectPtr conn, c * Returns 0 in case of success, -1 in case of error. */ static int -xenDaemonOpen_tcp(virConnectPtr conn, const char *host, int port) -{ - struct in_addr ip; - struct hostent *pent; - xenUnifiedPrivatePtr priv; - - if ((conn == NULL) || (host == NULL) || (port <= 0)) - return (-1); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - pent = gethostbyname(host); - if (pent == NULL) { - if (inet_aton(host, &ip) == 0) { - virXendError(NULL, VIR_ERR_UNKNOWN_HOST, - _("gethostbyname failed: %s"), host); - errno = ESRCH; - return (-1); - } - } else { - memcpy(&ip, pent->h_addr_list[0], sizeof(ip)); - } - - priv->len = sizeof(struct sockaddr_in); - priv->addr = (struct sockaddr *) &priv->addr_in; - priv->type = PF_INET; - - priv->addr_in.sin_family = AF_INET; - priv->addr_in.sin_port = htons(port); - memcpy(&priv->addr_in.sin_addr, &ip, sizeof(ip)); - - return (0); +xenDaemonOpen_tcp(virConnectPtr conn, const char *host, const char *port) +{ + xenUnifiedPrivatePtr priv; + struct addrinfo *res, *r; + struct addrinfo hints; + int saved_errno = EINVAL; + int ret; + + if ((conn == NULL) || (host == NULL) || (port == NULL)) + return (-1); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + priv->addrlen = 0; + memset(&priv->addr, 0, sizeof(priv->addr)); + + // http://people.redhat.com/drepper/userapi-ipv6.html + memset (&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + ret = getaddrinfo (host, port, &hints, &res); + if (ret != 0) { + virXendError(NULL, VIR_ERR_UNKNOWN_HOST, + _("unable to resolve hostname '%s': %s"), + host, gai_strerror (ret)); + return -1; + } + + /* Try to connect to each returned address in turn. */ + for (r = res; r; r = r->ai_next) { + int sock; + + sock = socket (r->ai_family, SOCK_STREAM, r->ai_protocol); + if (sock == -1) { + saved_errno = errno; + continue; + } + + if (connect (sock, r->ai_addr, r->ai_addrlen) == -1) { + saved_errno = errno; + close (sock); + continue; + } + + priv->addrlen = r->ai_addrlen; + priv->addrfamily = r->ai_family; + priv->addrprotocol = r->ai_protocol; + memcpy(&priv->addr, + r->ai_addr, + r->ai_addrlen); + close(sock); + break; + } + + freeaddrinfo (res); + + if (!priv->addrlen) { + virReportSystemError(conn, saved_errno, + _("unable to connect to '%s:%s'"), + host, port); + return -1; + } + + return 0; } @@ -2721,14 +2728,18 @@ xenDaemonOpen(virConnectPtr conn, /* * try though http on port 8000 */ - ret = xenDaemonOpen_tcp(conn, "localhost", 8000); + ret = xenDaemonOpen_tcp(conn, "localhost", "8000"); if (ret < 0) goto failed; ret = xend_detect_config_version(conn); if (ret == -1) goto failed; } else if (STRCASEEQ (conn->uri->scheme, "http")) { - ret = xenDaemonOpen_tcp(conn, conn->uri->server, conn->uri->port); + char *port; + if (virAsprintf(&port, "%d", conn->uri->port) == -1) + goto failed; + ret = xenDaemonOpen_tcp(conn, conn->uri->server, port); + VIR_FREE(port); if (ret < 0) goto failed; ret = xend_detect_config_version(conn); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 22, 2009 at 10:48:59AM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:25:02PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func:
char *virUserHomeDirectory(uid_t uid);
and hide all the horrific code in there.
Here's an update with that usage in it
That's better. There's still the question about whether the particular sysconf() sub-call we are using exists on all platforms, and we might need to replace it with the glibc-style incremental buffer. I say we should commit this and test it for a while. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

On Thu, Jan 22, 2009 at 10:48:59AM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:25:02PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func:
char *virUserHomeDirectory(uid_t uid);
and hide all the horrific code in there.
Here's an update with that usage in it
diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -49,6 +49,9 @@ #include <paths.h> #endif #include <netdb.h> +#ifdef HAVE_GETPWUID_R +#include <pwd.h> +#endif
#include "virterror_internal.h" #include "logging.h" @@ -1431,3 +1434,37 @@ int virKillProcess(pid_t pid, int sig) return kill(pid, sig); #endif } + + +#ifdef HAVE_GETPWUID_R +char *virGetUserDirectory(virConnectPtr conn, + uid_t uid) +{ + char *strbuf; + char *ret; + struct passwd pwbuf; + struct passwd *pw; + size_t strbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (VIR_ALLOC_N(strbuf, strbuflen) < 0) { + virReportOOMError(conn); + return NULL; + } + + if (getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw) != 0) { + virReportSystemError(conn, errno, + _("Failed to find user record for uid '%d'"), + uid); + VIR_FREE(strbuf); + return NULL; + } + + ret = strdup(pw->pw_dir); + + VIR_FREE(strbuf); + if (!ret) + virReportOOMError(conn); + + return NULL; +} +#endif
Delibrate mistake here :-) s/return NULL/return ret/ Otherwise it always returns NULL :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Fri, Jan 16, 2009 at 12:25:02PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func:
char *virUserHomeDirectory(uid_t uid);
and hide all the horrific code in there.
Here's an update with that usage in it
One problem in virGetUserDirectory. It always returns NULL. And did you mean to include the TCP socket changes in this set?
+char *virGetUserDirectory(virConnectPtr conn, + uid_t uid) +{ + char *strbuf; + char *ret; + struct passwd pwbuf; + struct passwd *pw; + size_t strbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (VIR_ALLOC_N(strbuf, strbuflen) < 0) { + virReportOOMError(conn); + return NULL; + } + + if (getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw) != 0) { + virReportSystemError(conn, errno, + _("Failed to find user record for uid '%d'"), + uid); + VIR_FREE(strbuf); + return NULL; + } + + ret = strdup(pw->pw_dir); + + VIR_FREE(strbuf); + if (!ret) + virReportOOMError(conn); + + return NULL;
This fucntion alwasy returns NULL..

On Thu, Jan 22, 2009 at 04:21:25PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Fri, Jan 16, 2009 at 12:25:02PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 16, 2009 at 12:21:59PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:46:37PM +0000, Daniel P. Berrange wrote:
+ char buf[1024];
sysconf (_SC_GETPW_R_SIZE_MAX)?
Looking at glibc's implementation of getpwuid (which uses getpwuid_r), I see that glibc dynamically reallocates the buffer as necessary to the correct size for the return value. The logic of this is fairly simple so maybe we should do the same?
From glibc:
buffer = malloc (/*some_initial_size*/);
while (buffer != NULL && (getpwuid_r (const char *name, &resbuf, buffer, buffer_size, &result) == ERANGE)) { char *new_buf; buffer_size *= 2; new_buf = (char *) realloc (buffer, buffer_size); if (new_buf == NULL) { free (buffer); errno = ENOMEM; } buffer = new_buf; }
Anyhow, +1 but I'd be happier if these functions were centralized in somewhere like src/utils.c.
That's a good idea - in all the cases where we currently use getpwuid all we actually want is the home directory path. So we could add a simple func:
char *virUserHomeDirectory(uid_t uid);
and hide all the horrific code in there.
Here's an update with that usage in it
One problem in virGetUserDirectory. It always returns NULL.
Yep, already fixed that.
And did you mean to include the TCP socket changes in this set?
Yes, its require to remove the use of non-threadsafe gethostname() call
+char *virGetUserDirectory(virConnectPtr conn, + uid_t uid) +{ + char *strbuf; + char *ret; + struct passwd pwbuf; + struct passwd *pw; + size_t strbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (VIR_ALLOC_N(strbuf, strbuflen) < 0) { + virReportOOMError(conn); + return NULL; + } + + if (getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw) != 0) { + virReportSystemError(conn, errno, + _("Failed to find user record for uid '%d'"), + uid); + VIR_FREE(strbuf); + return NULL; + } + + ret = strdup(pw->pw_dir); + + VIR_FREE(strbuf); + if (!ret) + virReportOOMError(conn); + + return NULL;
This fucntion alwasy returns NULL..
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
One problem in virGetUserDirectory. It always returns NULL.
Yep, already fixed that.
And did you mean to include the TCP socket changes in this set?
Yes, its require to remove the use of non-threadsafe gethostname() call
In that case, ACK.

Import the gnulib 'random_r' module, which provides a nice strong random number generator that is portable across OS. bootstrap | 1 gnulib/lib/.cvsignore | 12 - gnulib/lib/Makefile.am | 13 + gnulib/lib/gettimeofday.c | 5 gnulib/lib/ioctl.c | 1 gnulib/lib/poll.c | 4 gnulib/lib/random_r.c | 420 +++++++++++++++++++++++++++++++++++++++++++ gnulib/lib/strerror.c | 128 ++++++------- gnulib/m4/gnulib-cache.m4 | 3 gnulib/m4/gnulib-comp.m4 | 5 gnulib/m4/random_r.m4 | 21 ++ gnulib/tests/Makefile.am | 9 gnulib/tests/test-random_r.c | 56 +++++ 13 files changed, 600 insertions(+), 78 deletions(-) Daniel diff --git a/bootstrap b/bootstrap --- a/bootstrap +++ b/bootstrap @@ -80,6 +80,7 @@ physmem physmem poll posix-shell +random_r recv send setsockopt diff --git a/gnulib/lib/.cvsignore b/gnulib/lib/.cvsignore --- a/gnulib/lib/.cvsignore +++ b/gnulib/lib/.cvsignore @@ -1,13 +1,13 @@ +alloca.h +arpa_inet.h +.deps +errno.h +float.h *.la +.libs *.lo -.deps -.libs Makefile Makefile.in -alloca.h -arpa_inet.h -errno.h -float.h netdb.h netinet_in.h poll.h diff --git a/gnulib/lib/Makefile.am b/gnulib/lib/Makefile.am --- a/gnulib/lib/Makefile.am +++ b/gnulib/lib/Makefile.am @@ -9,7 +9,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=gnulib/lib --m4-base=gnulib/m4 --doc-base=doc --tests-base=gnulib/tests --aux-dir=build-aux --with-tests --lgpl=2 --libtool --macro-prefix=gl --no-vc-files c-ctype close connect getaddrinfo gethostname getpass gettext inet_pton ioctl mkstemp mktempd perror physmem poll posix-shell recv send setsockopt socket strerror strndup strsep sys_stat time_r useless-if-before-free vasprintf vc-list-files verify +# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=gnulib/lib --m4-base=gnulib/m4 --doc-base=doc --tests-base=gnulib/tests --aux-dir=build-aux --with-tests --lgpl=2 --libtool --macro-prefix=gl --no-vc-files c-ctype close connect getaddrinfo gethostname getpass gettext inet_pton ioctl mkstemp mktempd perror physmem poll posix-shell random_r recv send setsockopt socket strerror strndup strsep sys_stat time_r useless-if-before-free vasprintf vc-list-files verify AUTOMAKE_OPTIONS = 1.5 gnits @@ -26,7 +26,7 @@ DISTCLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = -AM_CPPFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS = noinst_LTLIBRARIES += libgnu.la @@ -454,6 +454,15 @@ EXTRA_libgnu_la_SOURCES += poll.c #MOSTLYCLEANFILES += script script-t ## end gnulib module posix-shell + +## begin gnulib module random_r + + +EXTRA_DIST += random_r.c + +EXTRA_libgnu_la_SOURCES += random_r.c + +## end gnulib module random_r ## begin gnulib module realloc-posix diff --git a/gnulib/lib/gettimeofday.c b/gnulib/lib/gettimeofday.c --- a/gnulib/lib/gettimeofday.c +++ b/gnulib/lib/gettimeofday.c @@ -47,12 +47,11 @@ static struct tm *localtime_buffer_addr On the first call, record the address of the static buffer that localtime uses for its result. */ -#undef localtime -extern struct tm *localtime (time_t const *); - struct tm * rpl_localtime (time_t const *timep) { +#undef localtime + extern struct tm *localtime (time_t const *); struct tm *tm = localtime (timep); if (localtime_buffer_addr == &tm_zero_buffer) diff --git a/gnulib/lib/ioctl.c b/gnulib/lib/ioctl.c --- a/gnulib/lib/ioctl.c +++ b/gnulib/lib/ioctl.c @@ -24,7 +24,6 @@ #define WIN32_LEAN_AND_MEAN /* Get winsock2.h. */ #include <sys/socket.h> -#include <sys/ioctl.h> /* Get set_winsock_errno, FD_TO_SOCKET etc. */ #include "w32sock.h" diff --git a/gnulib/lib/poll.c b/gnulib/lib/poll.c --- a/gnulib/lib/poll.c +++ b/gnulib/lib/poll.c @@ -404,10 +404,11 @@ poll (pfd, nfd, timeout) fd_set rfds, wfds, xfds; BOOL poll_again; MSG msg; + char sockbuf[256]; int rc = 0; nfds_t i; - if (timeout < -1) + if (nfd < 0 || timeout < -1) { errno = EINVAL; return -1; @@ -425,6 +426,7 @@ poll (pfd, nfd, timeout) /* Classify socket handles and create fd sets. */ for (i = 0; i < nfd; i++) { + size_t optlen = sizeof(sockbuf); pfd[i].revents = 0; if (pfd[i].fd < 0) continue; diff --git a/gnulib/lib/random_r.c b/gnulib/lib/random_r.c new file mode 100644 --- /dev/null +++ b/gnulib/lib/random_r.c @@ -0,0 +1,420 @@ +/* + Copyright (C) 1995, 2005, 2008 Free Software Foundation + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + Copyright (C) 1983 Regents of the University of California. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE.*/ + +/* + * This is derived from the Berkeley source: + * @(#)random.c 5.5 (Berkeley) 7/6/88 + * It was reworked for the GNU C Library by Roland McGrath. + * Rewritten to be reentrant by Ulrich Drepper, 1995 + */ + +#include <config.h> + +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> +#include <inttypes.h> + + +/* An improved random number generation package. In addition to the standard + rand()/srand() like interface, this package also has a special state info + interface. The initstate() routine is called with a seed, an array of + bytes, and a count of how many bytes are being passed in; this array is + then initialized to contain information for random number generation with + that much state information. Good sizes for the amount of state + information are 32, 64, 128, and 256 bytes. The state can be switched by + calling the setstate() function with the same array as was initialized + with initstate(). By default, the package runs with 128 bytes of state + information and generates far better random numbers than a linear + congruential generator. If the amount of state information is less than + 32 bytes, a simple linear congruential R.N.G. is used. Internally, the + state information is treated as an array of longs; the zeroth element of + the array is the type of R.N.G. being used (small integer); the remainder + of the array is the state information for the R.N.G. Thus, 32 bytes of + state information will give 7 longs worth of state information, which will + allow a degree seven polynomial. (Note: The zeroth word of state + information also has some other information stored in it; see setstate + for details). The random number generation technique is a linear feedback + shift register approach, employing trinomials (since there are fewer terms + to sum up that way). In this approach, the least significant bit of all + the numbers in the state table will act as a linear feedback shift register, + and will have period 2^deg - 1 (where deg is the degree of the polynomial + being used, assuming that the polynomial is irreducible and primitive). + The higher order bits will have longer periods, since their values are + also influenced by pseudo-random carries out of the lower bits. The + total period of the generator is approximately deg*(2**deg - 1); thus + doubling the amount of state information has a vast influence on the + period of the generator. Note: The deg*(2**deg - 1) is an approximation + only good for large deg, when the period of the shift register is the + dominant factor. With deg equal to seven, the period is actually much + longer than the 7*(2**7 - 1) predicted by this formula. */ + + + +/* For each of the currently supported random number generators, we have a + break value on the amount of state information (you need at least this many + bytes of state info to support this random number generator), a degree for + the polynomial (actually a trinomial) that the R.N.G. is based on, and + separation between the two lower order coefficients of the trinomial. */ + +/* Linear congruential. */ +#define TYPE_0 0 +#define BREAK_0 8 +#define DEG_0 0 +#define SEP_0 0 + +/* x**7 + x**3 + 1. */ +#define TYPE_1 1 +#define BREAK_1 32 +#define DEG_1 7 +#define SEP_1 3 + +/* x**15 + x + 1. */ +#define TYPE_2 2 +#define BREAK_2 64 +#define DEG_2 15 +#define SEP_2 1 + +/* x**31 + x**3 + 1. */ +#define TYPE_3 3 +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +/* x**63 + x + 1. */ +#define TYPE_4 4 +#define BREAK_4 256 +#define DEG_4 63 +#define SEP_4 1 + + +/* Array versions of the above information to make code run faster. + Relies on fact that TYPE_i == i. */ + +#define MAX_TYPES 5 /* Max number of types above. */ + +struct random_poly_info +{ + int seps[MAX_TYPES]; + int degrees[MAX_TYPES]; +}; + +static const struct random_poly_info random_poly_info = +{ + { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 }, + { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 } +}; + +#ifndef _LIBC +# define weak_alias(local, symbol) +# define __set_errno(e) errno = (e) +# define __srandom_r srandom_r +# define __initstate_r initstate_r +# define __setstate_r setstate_r +# define __random_r random_r +#endif + + + +/* Initialize the random number generator based on the given seed. If the + type is the trivial no-state-information type, just remember the seed. + Otherwise, initializes state[] based on the given "seed" via a linear + congruential generator. Then, the pointers are set to known locations + that are exactly rand_sep places apart. Lastly, it cycles the state + information a given number of times to get rid of any initial dependencies + introduced by the L.C.R.N.G. Note that the initialization of randtbl[] + for default usage relies on values produced by this routine. */ +int +__srandom_r (unsigned int seed, struct random_data *buf) +{ + int type; + int32_t *state; + long int i; + long int word; + int32_t *dst; + int kc; + + if (buf == NULL) + goto fail; + type = buf->rand_type; + if ((unsigned int) type >= MAX_TYPES) + goto fail; + + state = buf->state; + /* We must make sure the seed is not 0. Take arbitrarily 1 in this case. */ + if (seed == 0) + seed = 1; + state[0] = seed; + if (type == TYPE_0) + goto done; + + dst = state; + word = seed; + kc = buf->rand_deg; + for (i = 1; i < kc; ++i) + { + /* This does: + state[i] = (16807 * state[i - 1]) % 2147483647; + but avoids overflowing 31 bits. */ + long int hi = word / 127773; + long int lo = word % 127773; + word = 16807 * lo - 2836 * hi; + if (word < 0) + word += 2147483647; + *++dst = word; + } + + buf->fptr = &state[buf->rand_sep]; + buf->rptr = &state[0]; + kc *= 10; + while (--kc >= 0) + { + int32_t discard; + (void) __random_r (buf, &discard); + } + + done: + return 0; + + fail: + return -1; +} + +weak_alias (__srandom_r, srandom_r) + +/* Initialize the state information in the given array of N bytes for + future random number generation. Based on the number of bytes we + are given, and the break values for the different R.N.G.'s, we choose + the best (largest) one we can and set things up for it. srandom is + then called to initialize the state information. Note that on return + from srandom, we set state[-1] to be the type multiplexed with the current + value of the rear pointer; this is so successive calls to initstate won't + lose this information and will be able to restart with setstate. + Note: The first thing we do is save the current state, if any, just like + setstate so that it doesn't matter when initstate is called. + Returns a pointer to the old state. */ +int +__initstate_r (unsigned int seed, char *arg_state, size_t n, + struct random_data *buf) +{ + int32_t *old_state; + int32_t *state; + int type; + int degree; + int separation; + + if (buf == NULL) + goto fail; + + old_state = buf->state; + if (old_state != NULL) + { + int old_type = buf->rand_type; + if (old_type == TYPE_0) + old_state[-1] = TYPE_0; + else + old_state[-1] = (MAX_TYPES * (buf->rptr - old_state)) + old_type; + } + + if (n >= BREAK_3) + type = n < BREAK_4 ? TYPE_3 : TYPE_4; + else if (n < BREAK_1) + { + if (n < BREAK_0) + { + __set_errno (EINVAL); + goto fail; + } + type = TYPE_0; + } + else + type = n < BREAK_2 ? TYPE_1 : TYPE_2; + + degree = random_poly_info.degrees[type]; + separation = random_poly_info.seps[type]; + + buf->rand_type = type; + buf->rand_sep = separation; + buf->rand_deg = degree; + state = &((int32_t *) arg_state)[1]; /* First location. */ + /* Must set END_PTR before srandom. */ + buf->end_ptr = &state[degree]; + + buf->state = state; + + __srandom_r (seed, buf); + + state[-1] = TYPE_0; + if (type != TYPE_0) + state[-1] = (buf->rptr - state) * MAX_TYPES + type; + + return 0; + + fail: + __set_errno (EINVAL); + return -1; +} + +weak_alias (__initstate_r, initstate_r) + +/* Restore the state from the given state array. + Note: It is important that we also remember the locations of the pointers + in the current state information, and restore the locations of the pointers + from the old state information. This is done by multiplexing the pointer + location into the zeroth word of the state information. Note that due + to the order in which things are done, it is OK to call setstate with the + same state as the current state + Returns a pointer to the old state information. */ +int +__setstate_r (char *arg_state, struct random_data *buf) +{ + int32_t *new_state = 1 + (int32_t *) arg_state; + int type; + int old_type; + int32_t *old_state; + int degree; + int separation; + + if (arg_state == NULL || buf == NULL) + goto fail; + + old_type = buf->rand_type; + old_state = buf->state; + if (old_type == TYPE_0) + old_state[-1] = TYPE_0; + else + old_state[-1] = (MAX_TYPES * (buf->rptr - old_state)) + old_type; + + type = new_state[-1] % MAX_TYPES; + if (type < TYPE_0 || type > TYPE_4) + goto fail; + + buf->rand_deg = degree = random_poly_info.degrees[type]; + buf->rand_sep = separation = random_poly_info.seps[type]; + buf->rand_type = type; + + if (type != TYPE_0) + { + int rear = new_state[-1] / MAX_TYPES; + buf->rptr = &new_state[rear]; + buf->fptr = &new_state[(rear + separation) % degree]; + } + buf->state = new_state; + /* Set end_ptr too. */ + buf->end_ptr = &new_state[degree]; + + return 0; + + fail: + __set_errno (EINVAL); + return -1; +} + +weak_alias (__setstate_r, setstate_r) + +/* If we are using the trivial TYPE_0 R.N.G., just do the old linear + congruential bit. Otherwise, we do our fancy trinomial stuff, which is the + same in all the other cases due to all the global variables that have been + set up. The basic operation is to add the number at the rear pointer into + the one at the front pointer. Then both pointers are advanced to the next + location cyclically in the table. The value returned is the sum generated, + reduced to 31 bits by throwing away the "least random" low bit. + Note: The code takes advantage of the fact that both the front and + rear pointers can't wrap on the same call by not testing the rear + pointer if the front one has wrapped. Returns a 31-bit random number. */ + +int +__random_r (struct random_data *buf, int32_t *result) +{ + int32_t *state; + + if (buf == NULL || result == NULL) + goto fail; + + state = buf->state; + + if (buf->rand_type == TYPE_0) + { + int32_t val = state[0]; + val = ((state[0] * 1103515245) + 12345) & 0x7fffffff; + state[0] = val; + *result = val; + } + else + { + int32_t *fptr = buf->fptr; + int32_t *rptr = buf->rptr; + int32_t *end_ptr = buf->end_ptr; + int32_t val; + + val = *fptr += *rptr; + /* Chucking least random bit. */ + *result = (val >> 1) & 0x7fffffff; + ++fptr; + if (fptr >= end_ptr) + { + fptr = state; + ++rptr; + } + else + { + ++rptr; + if (rptr >= end_ptr) + rptr = state; + } + buf->fptr = fptr; + buf->rptr = rptr; + } + return 0; + + fail: + __set_errno (EINVAL); + return -1; +} + +weak_alias (__random_r, random_r) diff --git a/gnulib/lib/strerror.c b/gnulib/lib/strerror.c --- a/gnulib/lib/strerror.c +++ b/gnulib/lib/strerror.c @@ -45,89 +45,89 @@ rpl_strerror (int n) { # if GNULIB_defined_ETXTBSY case ETXTBSY: - return (char*)"Text file busy"; + return "Text file busy"; # endif # if GNULIB_defined_ESOCK /* native Windows platforms */ /* EWOULDBLOCK is the same as EAGAIN. */ case EINPROGRESS: - return (char*)"Operation now in progress"; + return "Operation now in progress"; case EALREADY: - return (char*)"Operation already in progress"; + return "Operation already in progress"; case ENOTSOCK: - return (char*)"Socket operation on non-socket"; + return "Socket operation on non-socket"; case EDESTADDRREQ: - return (char*)"Destination address required"; + return "Destination address required"; case EMSGSIZE: - return (char*)"Message too long"; + return "Message too long"; case EPROTOTYPE: - return (char*)"Protocol wrong type for socket"; + return "Protocol wrong type for socket"; case ENOPROTOOPT: - return (char*)"Protocol not available"; + return "Protocol not available"; case EPROTONOSUPPORT: - return (char*)"Protocol not supported"; + return "Protocol not supported"; case ESOCKTNOSUPPORT: - return (char*)"Socket type not supported"; + return "Socket type not supported"; case EOPNOTSUPP: - return (char*)"Operation not supported"; + return "Operation not supported"; case EPFNOSUPPORT: - return (char*)"Protocol family not supported"; + return "Protocol family not supported"; case EAFNOSUPPORT: - return (char*)"Address family not supported by protocol"; + return "Address family not supported by protocol"; case EADDRINUSE: - return (char*)"Address already in use"; + return "Address already in use"; case EADDRNOTAVAIL: - return (char*)"Cannot assign requested address"; + return "Cannot assign requested address"; case ENETDOWN: - return (char*)"Network is down"; + return "Network is down"; case ENETUNREACH: - return (char*)"Network is unreachable"; + return "Network is unreachable"; case ENETRESET: - return (char*)"Network dropped connection on reset"; + return "Network dropped connection on reset"; case ECONNABORTED: - return (char*)"Software caused connection abort"; + return "Software caused connection abort"; case ECONNRESET: - return (char*)"Connection reset by peer"; + return "Connection reset by peer"; case ENOBUFS: - return (char*)"No buffer space available"; + return "No buffer space available"; case EISCONN: - return (char*)"Transport endpoint is already connected"; + return "Transport endpoint is already connected"; case ENOTCONN: - return (char*)"Transport endpoint is not connected"; + return "Transport endpoint is not connected"; case ESHUTDOWN: - return (char*)"Cannot send after transport endpoint shutdown"; + return "Cannot send after transport endpoint shutdown"; case ETOOMANYREFS: - return (char*)"Too many references: cannot splice"; + return "Too many references: cannot splice"; case ETIMEDOUT: - return (char*)"Connection timed out"; + return "Connection timed out"; case ECONNREFUSED: - return (char*)"Connection refused"; + return "Connection refused"; case ELOOP: - return (char*)"Too many levels of symbolic links"; + return "Too many levels of symbolic links"; case EHOSTDOWN: - return (char*)"Host is down"; + return "Host is down"; case EHOSTUNREACH: - return (char*)"No route to host"; + return "No route to host"; case EPROCLIM: - return (char*)"Too many processes"; + return "Too many processes"; case EUSERS: - return (char*)"Too many users"; + return "Too many users"; case EDQUOT: - return (char*)"Disk quota exceeded"; + return "Disk quota exceeded"; case ESTALE: - return (char*)"Stale NFS file handle"; + return "Stale NFS file handle"; case EREMOTE: - return (char*)"Object is remote"; + return "Object is remote"; # if HAVE_WINSOCK2_H /* WSA_INVALID_HANDLE maps to EBADF */ /* WSA_NOT_ENOUGH_MEMORY maps to ENOMEM */ /* WSA_INVALID_PARAMETER maps to EINVAL */ case WSA_OPERATION_ABORTED: - return (char*)"Overlapped operation aborted"; + return "Overlapped operation aborted"; case WSA_IO_INCOMPLETE: - return (char*)"Overlapped I/O event object not in signaled state"; + return "Overlapped I/O event object not in signaled state"; case WSA_IO_PENDING: - return (char*)"Overlapped operations will complete later"; + return "Overlapped operations will complete later"; /* WSAEINTR maps to EINTR */ /* WSAEBADF maps to EBADF */ /* WSAEACCES maps to EACCES */ @@ -172,86 +172,86 @@ rpl_strerror (int n) /* WSAESTALE is ESTALE */ /* WSAEREMOTE is EREMOTE */ case WSASYSNOTREADY: - return (char*)"Network subsystem is unavailable"; + return "Network subsystem is unavailable"; case WSAVERNOTSUPPORTED: - return (char*)"Winsock.dll version out of range"; + return "Winsock.dll version out of range"; case WSANOTINITIALISED: - return (char*)"Successful WSAStartup not yet performed"; + return "Successful WSAStartup not yet performed"; case WSAEDISCON: - return (char*)"Graceful shutdown in progress"; + return "Graceful shutdown in progress"; case WSAENOMORE: case WSA_E_NO_MORE: - return (char*)"No more results"; + return "No more results"; case WSAECANCELLED: case WSA_E_CANCELLED: - return (char*)"Call was canceled"; + return "Call was canceled"; case WSAEINVALIDPROCTABLE: - return (char*)"Procedure call table is invalid"; + return "Procedure call table is invalid"; case WSAEINVALIDPROVIDER: - return (char*)"Service provider is invalid"; + return "Service provider is invalid"; case WSAEPROVIDERFAILEDINIT: - return (char*)"Service provider failed to initialize"; + return "Service provider failed to initialize"; case WSASYSCALLFAILURE: - return (char*)"System call failure"; + return "System call failure"; case WSASERVICE_NOT_FOUND: - return (char*)"Service not found"; + return "Service not found"; case WSATYPE_NOT_FOUND: - return (char*)"Class type not found"; + return "Class type not found"; case WSAEREFUSED: - return (char*)"Database query was refused"; + return "Database query was refused"; case WSAHOST_NOT_FOUND: - return (char*)"Host not found"; + return "Host not found"; case WSATRY_AGAIN: - return (char*)"Nonauthoritative host not found"; + return "Nonauthoritative host not found"; case WSANO_RECOVERY: - return (char*)"Nonrecoverable error"; + return "Nonrecoverable error"; case WSANO_DATA: - return (char*)"Valid name, no data record of requested type"; + return "Valid name, no data record of requested type"; /* WSA_QOS_* omitted */ # endif # endif # if GNULIB_defined_ENOMSG case ENOMSG: - return (char*)"No message of desired type"; + return "No message of desired type"; # endif # if GNULIB_defined_EIDRM case EIDRM: - return (char*)"Identifier removed"; + return "Identifier removed"; # endif # if GNULIB_defined_ENOLINK case ENOLINK: - return (char*)"Link has been severed"; + return "Link has been severed"; # endif # if GNULIB_defined_EPROTO case EPROTO: - return (char*)"Protocol error"; + return "Protocol error"; # endif # if GNULIB_defined_EMULTIHOP case EMULTIHOP: - return (char*)"Multihop attempted"; + return "Multihop attempted"; # endif # if GNULIB_defined_EBADMSG case EBADMSG: - return (char*)"Bad message"; + return "Bad message"; # endif # if GNULIB_defined_EOVERFLOW case EOVERFLOW: - return (char*)"Value too large for defined data type"; + return "Value too large for defined data type"; # endif # if GNULIB_defined_ENOTSUP case ENOTSUP: - return (char*)"Not supported"; + return "Not supported"; # endif # if GNULIB_defined_ case ECANCELED: - return (char*)"Operation canceled"; + return "Operation canceled"; # endif } diff --git a/gnulib/m4/gnulib-cache.m4 b/gnulib/m4/gnulib-cache.m4 --- a/gnulib/m4/gnulib-cache.m4 +++ b/gnulib/m4/gnulib-cache.m4 @@ -15,7 +15,7 @@ # Specification in the form of a command-line invocation: -# gnulib-tool --import --dir=. --lib=libgnu --source-base=gnulib/lib --m4-base=gnulib/m4 --doc-base=doc --tests-base=gnulib/tests --aux-dir=build-aux --with-tests --lgpl=2 --libtool --macro-prefix=gl --no-vc-files c-ctype close connect getaddrinfo gethostname getpass gettext inet_pton ioctl mkstemp mktempd perror physmem poll posix-shell recv send setsockopt socket strerror strndup strsep sys_stat time_r useless-if-before-free vasprintf vc-list-files verify +# gnulib-tool --import --dir=. --lib=libgnu --source-base=gnulib/lib --m4-base=gnulib/m4 --doc-base=doc --tests-base=gnulib/tests --aux-dir=build-aux --with-tests --lgpl=2 --libtool --macro-prefix=gl --no-vc-files c-ctype close connect getaddrinfo gethostname getpass gettext inet_pton ioctl mkstemp mktempd perror physmem poll posix-shell random_r recv send setsockopt socket strerror strndup strsep sys_stat time_r useless-if-before-free vasprintf vc-list-files verify # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([]) @@ -35,6 +35,7 @@ gl_MODULES([ physmem poll posix-shell + random_r recv send setsockopt diff --git a/gnulib/m4/gnulib-comp.m4 b/gnulib/m4/gnulib-comp.m4 --- a/gnulib/m4/gnulib-comp.m4 +++ b/gnulib/m4/gnulib-comp.m4 @@ -106,6 +106,8 @@ AC_SUBST([LTALLOCA]) gl_PHYSMEM gl_FUNC_POLL gl_POSIX_SHELL + gl_FUNC_RANDOM_R + gl_STDLIB_MODULE_INDICATOR([random_r]) gl_FUNC_REALLOC_POSIX gl_STDLIB_MODULE_INDICATOR([realloc-posix]) AC_REQUIRE([gl_HEADER_SYS_SOCKET]) @@ -370,6 +372,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/printf-args.h lib/printf-parse.c lib/printf-parse.h + lib/random_r.c lib/realloc.c lib/recv.c lib/send.c @@ -461,6 +464,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/printf-posix.m4 m4/printf.m4 m4/progtest.m4 + m4/random_r.m4 m4/realloc.m4 m4/servent.m4 m4/size_max.m4 @@ -516,6 +520,7 @@ AC_DEFUN([gl_FILE_LIST], [ tests/test-perror.c tests/test-perror.sh tests/test-poll.c + tests/test-random_r.c tests/test-snprintf.c tests/test-sockets.c tests/test-stdbool.c diff --git a/gnulib/m4/random_r.m4 b/gnulib/m4/random_r.m4 new file mode 100644 --- /dev/null +++ b/gnulib/m4/random_r.m4 @@ -0,0 +1,21 @@ +# serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_RANDOM_R], +[ + AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) + AC_CHECK_FUNCS([random_r]) + if test $ac_cv_func_random_r = no; then + HAVE_RANDOM_R=0 + AC_LIBOBJ([random_r]) + gl_PREREQ_RANDOM_R + fi +]) + +# Prerequisites of lib/random_r.c. +AC_DEFUN([gl_PREREQ_RANDOM_R], [ + : +]) diff --git a/gnulib/tests/Makefile.am b/gnulib/tests/Makefile.am --- a/gnulib/tests/Makefile.am +++ b/gnulib/tests/Makefile.am @@ -211,6 +211,15 @@ EXTRA_DIST += test-poll.c EXTRA_DIST += test-poll.c ## end gnulib module poll-tests + +## begin gnulib module random_r-tests + +TESTS += test-random_r +check_PROGRAMS += test-random_r + +EXTRA_DIST += test-random_r.c + +## end gnulib module random_r-tests ## begin gnulib module snprintf-tests diff --git a/gnulib/tests/test-random_r.c b/gnulib/tests/test-random_r.c new file mode 100644 --- /dev/null +++ b/gnulib/tests/test-random_r.c @@ -0,0 +1,56 @@ +/* Test random_r. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +int +main () +{ + struct random_data rand_state; + char buf[128]; + unsigned int i; + unsigned int n_big = 0; + + rand_state.state = NULL; + if (initstate_r (time (NULL), buf, sizeof buf, &rand_state)) + return 1; + for (i = 0; i < 1000; i++) + { + int32_t r; + ASSERT (random_r (&rand_state, &r) == 0); + ASSERT (0 <= r); + if (RAND_MAX / 2 < r) + ++n_big; + } + + /* Fail if none of the numbers were larger than RAND_MAX / 2. */ + return !n_big; +} -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:47:03PM +0000, Daniel P. Berrange wrote:
Import the gnulib 'random_r' module, which provides a nice strong random number generator that is portable across OS.
bootstrap | 1 gnulib/lib/.cvsignore | 12 - gnulib/lib/Makefile.am | 13 + gnulib/lib/gettimeofday.c | 5 gnulib/lib/ioctl.c | 1 gnulib/lib/poll.c | 4 gnulib/lib/random_r.c | 420 +++++++++++++++++++++++++++++++++++++++++++ gnulib/lib/strerror.c | 128 ++++++-------
diff --git a/gnulib/lib/strerror.c b/gnulib/lib/strerror.c --- a/gnulib/lib/strerror.c +++ b/gnulib/lib/strerror.c @@ -45,89 +45,89 @@ rpl_strerror (int n) { # if GNULIB_defined_ETXTBSY case ETXTBSY: - return (char*)"Text file busy"; + return "Text file busy"; # endif
# if GNULIB_defined_ESOCK /* native Windows platforms */ /* EWOULDBLOCK is the same as EAGAIN. */ case EINPROGRESS: - return (char*)"Operation now in progress"; + return "Operation now in progress";
Reverts an earlier change? Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

"Daniel P. Berrange" <berrange@redhat.com> wrote:
Import the gnulib 'random_r' module, which provides a nice strong random number generator that is portable across OS.
bootstrap | 1 gnulib/lib/.cvsignore | 12 - gnulib/lib/Makefile.am | 13 + gnulib/lib/gettimeofday.c | 5 gnulib/lib/ioctl.c | 1 gnulib/lib/poll.c | 4 gnulib/lib/random_r.c | 420 +++++++++++++++++++++++++++++++++++++++++++ gnulib/lib/strerror.c | 128 ++++++------- gnulib/m4/gnulib-cache.m4 | 3 gnulib/m4/gnulib-comp.m4 | 5 gnulib/m4/random_r.m4 | 21 ++ gnulib/tests/Makefile.am | 9 gnulib/tests/test-random_r.c | 56 +++++ 13 files changed, 600 insertions(+), 78 deletions(-)
Daniel
diff --git a/bootstrap b/bootstrap --- a/bootstrap +++ b/bootstrap @@ -80,6 +80,7 @@ physmem physmem poll posix-shell +random_r recv send setsockopt
ACK. The above should be the only manual change. The rest is the result of running ./bootstrap. Actually, there's one more semi-manual step I usually perform when .cvsignore files are modified: Run "make sync-vcs-ignore-files" to sync the .gitignore files from the .cvsignore ones. The rule is at the bottom of Makefile.maint.

Now that gnulib's rand module is imported, we have a decent quality random number generator that's portable. We don't want to mess with the apps state, so in virInitialize() we explicitly initialize our own private random nubmer generator state with virRandomInitialize(). The util.h file gains a convenience macro, since random_r() is horrible to call and we need to protect our global state int virRandom(int max) Makefile.maint | 2 +- src/libvirt.c | 3 ++- src/util.c | 38 +++++++++++++++++++++++++++++++++++--- src/util.h | 3 +++ src/uuid.c | 4 ++-- 5 files changed, 43 insertions(+), 7 deletions(-) Daniel diff --git a/Makefile.maint b/Makefile.maint --- a/Makefile.maint +++ b/Makefile.maint @@ -117,7 +117,7 @@ sc_prohibit_nonreentrant: @fail=0 ; \ for i in $(NON_REENTRANT) ; \ do \ - grep -nE "\<$$i\>[:space:]*\(" $$($(VC_LIST_EXCEPT)) && \ + grep --before 2 --after 1 -nE "\<$$i\>[:space:]*\(" $$($(VC_LIST_EXCEPT)) && \ fail=1 && echo "$(ME): use $${i}_r, not $${i}" || : ; \ done ; \ exit $$fail diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -257,7 +257,8 @@ virInitialize(void) initialized = 1; if (virThreadInitialize() < 0 || - virErrorInitialize() < 0) + virErrorInitialize() < 0 || + virRandomInitialize()) return -1; #ifdef ENABLE_DEBUG diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -32,6 +32,7 @@ #include <fcntl.h> #include <errno.h> #include <poll.h> +#include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -56,6 +57,7 @@ #include "buf.h" #include "util.h" #include "memory.h" +#include "threads.h" #ifndef NSIG # define NSIG 32 @@ -1282,9 +1284,9 @@ void virGenerateMacAddr(const unsigned c addr[0] = prefix[0]; addr[1] = prefix[1]; addr[2] = prefix[2]; - addr[3] = (int)(256*(rand()/(RAND_MAX+1.0))); - addr[4] = (int)(256*(rand()/(RAND_MAX+1.0))); - addr[5] = (int)(256*(rand()/(RAND_MAX+1.0))); + addr[3] = virRandom(256); + addr[4] = virRandom(256); + addr[5] = virRandom(256); } @@ -1431,3 +1433,33 @@ int virKillProcess(pid_t pid, int sig) return kill(pid, sig); #endif } + + +static char randomState[128]; +static struct random_data randomData; +static virMutex randomLock; + +int virRandomInitialize(void) +{ + if (virMutexInit(&randomLock) < 0) + return -1; + + if (initstate_r(time(NULL), + randomState, + sizeof(randomState), + &randomData) < 0) + return -1; + + return 0; +} + +int virRandom(int max) +{ + int32_t ret; + + virMutexLock(&randomLock); + random_r(&randomData, &ret); + virMutexUnlock(&randomLock); + + return (int) ((double)max * ((double)ret / (double)RAND_MAX)); +} diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -172,4 +172,7 @@ char *virGetHostname(void); int virKillProcess(pid_t pid, int sig); +int virRandomInitialize(void); +int virRandom(int max); + #endif /* __VIR_UTIL_H__ */ diff --git a/src/uuid.c b/src/uuid.c --- a/src/uuid.c +++ b/src/uuid.c @@ -35,6 +35,7 @@ #include "c-ctype.h" #include "internal.h" +#include "util.h" #define qemudLog(level, msg...) fprintf(stderr, msg) @@ -74,9 +75,8 @@ virUUIDGeneratePseudoRandomBytes(unsigne virUUIDGeneratePseudoRandomBytes(unsigned char *buf, int buflen) { - srand(time(NULL)); while (buflen > 0) { - *buf = (int) (255.0 * (rand() / (double) RAND_MAX)); + *buf = virRandom(256); buflen--; } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:47:27PM +0000, Daniel P. Berrange wrote:
Now that gnulib's rand module is imported, we have a decent quality random number generator that's portable. We don't want to mess with the apps state, so in virInitialize() we explicitly initialize our own private random nubmer generator state with virRandomInitialize().
The util.h file gains a convenience macro, since random_r() is horrible to call and we need to protect our global state
int virRandom(int max)
+1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

On Tue, Jan 13, 2009 at 05:47:27PM +0000, Daniel P. Berrange wrote:
Now that gnulib's rand module is imported, we have a decent quality random number generator that's portable. We don't want to mess with the apps state, so in virInitialize() we explicitly initialize our own private random nubmer generator state with virRandomInitialize().
The util.h file gains a convenience macro, since random_r() is horrible to call and we need to protect our global state
int virRandom(int max)
+1 looks fine ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

"Daniel P. Berrange" <berrange@redhat.com> wrote:
Now that gnulib's rand module is imported, we have a decent quality random number generator that's portable. We don't want to mess with the apps state, so in virInitialize() we explicitly initialize our own private random nubmer generator state with virRandomInitialize().
The util.h file gains a convenience macro, since random_r() is horrible to call and we need to protect our global state
int virRandom(int max) ... +int virRandomInitialize(void)
This all looks good and correct. Only one suggestion: consider exposing the seed-setting functionality by moving it up a level: int virRandomInitialize(int seed) in case we ever want reproducibly random MAC addresses and UUIDs. This might be useful for testing.

On Tue, Jan 20, 2009 at 12:22:46PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
Now that gnulib's rand module is imported, we have a decent quality random number generator that's portable. We don't want to mess with the apps state, so in virInitialize() we explicitly initialize our own private random nubmer generator state with virRandomInitialize().
The util.h file gains a convenience macro, since random_r() is horrible to call and we need to protect our global state
int virRandom(int max) ... +int virRandomInitialize(void)
This all looks good and correct. Only one suggestion: consider exposing the seed-setting functionality by moving it up a level:
int virRandomInitialize(int seed)
in case we ever want reproducibly random MAC addresses and UUIDs.
How about this then.... BTW, what's good practice for actually initializing a seed ? time() ? time() ^ getpid() ? anything better ? Daniel diff --git a/Makefile.maint b/Makefile.maint --- a/Makefile.maint +++ b/Makefile.maint @@ -117,7 +117,7 @@ sc_prohibit_nonreentrant: @fail=0 ; \ for i in $(NON_REENTRANT) ; \ do \ - grep -nE "\<$$i\>[:space:]*\(" $$($(VC_LIST_EXCEPT)) && \ + grep --before 2 --after 1 -nE "\<$$i\>[:space:]*\(" $$($(VC_LIST_EXCEPT)) && \ fail=1 && echo "$(ME): use $${i}_r, not $${i}" || : ; \ done ; \ exit $$fail diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -257,7 +257,8 @@ virInitialize(void) initialized = 1; if (virThreadInitialize() < 0 || - virErrorInitialize() < 0) + virErrorInitialize() < 0 || + virRandomInitialize(time(NULL) ^ getpid())) return -1; #ifdef ENABLE_DEBUG @@ -332,23 +333,19 @@ DllMain (HINSTANCE instance ATTRIBUTE_UN { switch (reason) { case DLL_PROCESS_ATTACH: - fprintf(stderr, "Initializing DLL\n"); virInitialize(); break; case DLL_THREAD_ATTACH: - fprintf(stderr, "Thread start\n"); /* Nothing todo in libvirt yet */ break; case DLL_THREAD_DETACH: - fprintf(stderr, "Thread exit\n"); /* Release per-thread local data */ virThreadOnExit(); break; case DLL_PROCESS_DETACH: - fprintf(stderr, "Process exit\n"); /* Don't bother releasing per-thread data since (hopefully) windows cleans up everything on process exit */ diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -32,6 +32,7 @@ #include <fcntl.h> #include <errno.h> #include <poll.h> +#include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -59,6 +60,7 @@ #include "buf.h" #include "util.h" #include "memory.h" +#include "threads.h" #ifndef NSIG # define NSIG 32 @@ -1285,9 +1287,9 @@ void virGenerateMacAddr(const unsigned c addr[0] = prefix[0]; addr[1] = prefix[1]; addr[2] = prefix[2]; - addr[3] = (int)(256*(rand()/(RAND_MAX+1.0))); - addr[4] = (int)(256*(rand()/(RAND_MAX+1.0))); - addr[5] = (int)(256*(rand()/(RAND_MAX+1.0))); + addr[3] = virRandom(256); + addr[4] = virRandom(256); + addr[5] = virRandom(256); } @@ -1436,6 +1438,36 @@ int virKillProcess(pid_t pid, int sig) } +static char randomState[128]; +static struct random_data randomData; +static virMutex randomLock; + +int virRandomInitialize(unsigned int seed) +{ + if (virMutexInit(&randomLock) < 0) + return -1; + + if (initstate_r(seed, + randomState, + sizeof(randomState), + &randomData) < 0) + return -1; + + return 0; +} + +int virRandom(int max) +{ + int32_t ret; + + virMutexLock(&randomLock); + random_r(&randomData, &ret); + virMutexUnlock(&randomLock); + + return (int) ((double)max * ((double)ret / (double)RAND_MAX)); +} + + #ifdef HAVE_GETPWUID_R char *virGetUserDirectory(virConnectPtr conn, uid_t uid) diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -177,4 +177,7 @@ char *virGetUserDirectory(virConnectPtr uid_t uid); #endif +int virRandomInitialize(unsigned int seed); +int virRandom(int max); + #endif /* __VIR_UTIL_H__ */ diff --git a/src/uuid.c b/src/uuid.c --- a/src/uuid.c +++ b/src/uuid.c @@ -35,6 +35,7 @@ #include "c-ctype.h" #include "internal.h" +#include "util.h" #define qemudLog(level, msg...) fprintf(stderr, msg) @@ -74,9 +75,8 @@ static int virUUIDGeneratePseudoRandomBytes(unsigned char *buf, int buflen) { - srand(time(NULL)); while (buflen > 0) { - *buf = (int) (255.0 * (rand() / (double) RAND_MAX)); + *buf = virRandom(256); buflen--; } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
How about this then....
Looks fine. Thanks!
BTW, what's good practice for actually initializing a seed ?
time() ? time() ^ getpid() ?
anything better ?
You could mix in some more get*id numbers. If we really care, using gnulib's gethrxtime module would be nice, but parts of it are GPL'd. Here's part of coreutils' gl/lib/rand-isaac.c: void isaac_seed (struct isaac_state *s) { isaac_seed_start (s); { pid_t t = getpid (); ISAAC_SEED (s, t); } { pid_t t = getppid (); ISAAC_SEED (s, t); } { uid_t t = getuid (); ISAAC_SEED (s, t); } { gid_t t = getgid (); ISAAC_SEED (s, t); } { xtime_t t = gethrxtime (); ISAAC_SEED (s, t); } isaac_seed_finish (s); }
diff --git a/Makefile.maint b/Makefile.maint
ACK

The configure output looked untidy, so this aligns things correctly. configure.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) Daniel diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -1333,24 +1333,24 @@ else AC_MSG_NOTICE([ polkit: no]) fi if test "$with_selinux" = "yes" ; then -AC_MSG_NOTICE([ selinux: $SELINUX_CFLAGS $SELINUX_LIBS]) +AC_MSG_NOTICE([ selinux: $SELINUX_CFLAGS $SELINUX_LIBS]) else -AC_MSG_NOTICE([ selinux: no]) +AC_MSG_NOTICE([ selinux: no]) fi if test "$with_numactl" = "yes" ; then -AC_MSG_NOTICE([ numactl: $NUMACTL_CFLAGS $NUMACTL_LIBS]) +AC_MSG_NOTICE([ numactl: $NUMACTL_CFLAGS $NUMACTL_LIBS]) else -AC_MSG_NOTICE([ numactl: no]) +AC_MSG_NOTICE([ numactl: no]) fi if test "$with_xen" = "yes" ; then -AC_MSG_NOTICE([ xen: $XEN_CFLAGS $XEN_LIBS]) +AC_MSG_NOTICE([ xen: $XEN_CFLAGS $XEN_LIBS]) else -AC_MSG_NOTICE([ xen: no]) +AC_MSG_NOTICE([ xen: no]) fi if test "$with_hal" = "yes" ; then -AC_MSG_NOTICE([ hal: $HAL_CFLAGS $HAL_LIBS]) +AC_MSG_NOTICE([ hal: $HAL_CFLAGS $HAL_LIBS]) else -AC_MSG_NOTICE([ hal: no]) +AC_MSG_NOTICE([ hal: no]) fi if test "$with_devkit" = "yes" ; then AC_MSG_NOTICE([ devkit: $DEVKIT_CFLAGS $DEVKIT_LIBS]) @@ -1360,12 +1360,12 @@ fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Test suite]) AC_MSG_NOTICE([]) -AC_MSG_NOTICE([ Coverage: $enable_coverage]) +AC_MSG_NOTICE([ Coverage: $enable_coverage]) AC_MSG_NOTICE([ Alloc OOM: $enable_oom]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Miscellaneous]) AC_MSG_NOTICE([]) -AC_MSG_NOTICE([ Debug: $enable_debug]) +AC_MSG_NOTICE([ Debug: $enable_debug]) AC_MSG_NOTICE([ Warnings: $enable_compile_warnings]) AC_MSG_NOTICE([ Readline: $lv_use_readline]) AC_MSG_NOTICE([]) -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:47:49PM +0000, Daniel P. Berrange wrote:
The configure output looked untidy, so this aligns things correctly.
configure.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
Cleanup always good. +1 Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

On Fri, Jan 16, 2009 at 12:24:36PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:47:49PM +0000, Daniel P. Berrange wrote:
The configure output looked untidy, so this aligns things correctly.
configure.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
Cleanup always good. +1
trivial, +1 Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Jan 16, 2009 at 04:43:52PM +0100, Daniel Veillard wrote:
On Fri, Jan 16, 2009 at 12:24:36PM +0000, Richard W.M. Jones wrote:
On Tue, Jan 13, 2009 at 05:47:49PM +0000, Daniel P. Berrange wrote:
The configure output looked untidy, so this aligns things correctly.
configure.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
Cleanup always good. +1
trivial, +1
Comitted this patch Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
The configure output looked untidy, so this aligns things correctly.
configure.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
+1, of course

This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver. test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-) Daniel diff --git a/src/test.c b/src/test.c --- a/src/test.c +++ b/src/test.c @@ -42,6 +42,8 @@ #include "memory.h" #include "network_conf.h" #include "domain_conf.h" +#include "domain_event.h" +#include "event.h" #include "storage_conf.h" #include "xml.h" #include "threads.h" @@ -72,6 +74,13 @@ struct _testConn { virStoragePoolObjList pools; int numCells; testCell cells[MAX_CELLS]; + + + /* An array of callbacks */ + virDomainEventCallbackListPtr domainEventCallbacks; + virDomainEventQueuePtr domainEventQueue; + int domainEventTimer; + int domainEventDispatching; }; typedef struct _testConn testConn; typedef struct _testConn *testConnPtr; @@ -96,6 +105,12 @@ static const virNodeInfo defaultNodeInfo virReportErrorHelper(conn, VIR_FROM_TEST, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) +static int testClose(virConnectPtr conn); +static void testDomainEventFlush(int timer, void *opaque); +static void testDomainEventQueue(testConnPtr driver, + virDomainEventPtr event); + + static void testDriverLock(testConnPtr driver) { virMutexLock(&driver->lock); @@ -644,6 +659,23 @@ static virDrvOpenStatus testOpen(virConn ret = testOpenFromFile(conn, conn->uri->path); + if (ret == VIR_DRV_OPEN_SUCCESS) { + testConnPtr privconn = conn->privateData; + /* Init callback list */ + if (VIR_ALLOC(privconn->domainEventCallbacks) < 0 || + !(privconn->domainEventQueue = virDomainEventQueueNew())) { + virReportOOMError(NULL); + testClose(conn); + return VIR_DRV_OPEN_ERROR; + } + + if ((privconn->domainEventTimer = + virEventAddTimeout(-1, testDomainEventFlush, privconn, NULL)) < 0) { + testClose(conn); + return VIR_DRV_OPEN_ERROR; + } + } + return (ret); } @@ -655,6 +687,13 @@ static int testClose(virConnectPtr conn) virDomainObjListFree(&privconn->domains); virNetworkObjListFree(&privconn->networks); virStoragePoolObjListFree(&privconn->pools); + + virDomainEventCallbackListFree(privconn->domainEventCallbacks); + virDomainEventQueueFree(privconn->domainEventQueue); + + if (privconn->domainEventTimer != -1) + virEventRemoveTimeout(privconn->domainEventTimer); + testDriverUnlock(privconn); virMutexDestroy(&privconn->lock); @@ -733,6 +772,7 @@ testDomainCreateXML(virConnectPtr conn, virDomainPtr ret = NULL; virDomainDefPtr def; virDomainObjPtr dom = NULL; + virDomainEventPtr event = NULL; testDriverLock(privconn); if ((def = virDomainDefParseString(conn, privconn->caps, xml, @@ -747,6 +787,10 @@ testDomainCreateXML(virConnectPtr conn, dom->state = VIR_DOMAIN_RUNNING; dom->def->id = privconn->nextDomID++; + event = virDomainEventNewFromObj(dom, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); + ret = virGetDomain(conn, def->name, def->uuid); if (ret) ret->id = def->id; @@ -754,6 +798,8 @@ testDomainCreateXML(virConnectPtr conn, cleanup: if (dom) virDomainObjUnlock(dom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -860,6 +906,7 @@ static int testDestroyDomain (virDomainP { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -874,16 +921,22 @@ static int testDestroyDomain (virDomainP privdom->state = VIR_DOMAIN_SHUTOFF; privdom->def->id = -1; domain->id = -1; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_DESTROYED); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); privdom = NULL; } + ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -892,6 +945,7 @@ static int testResumeDomain (virDomainPt { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -912,11 +966,18 @@ static int testResumeDomain (virDomainPt } privdom->state = VIR_DOMAIN_RUNNING; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_RESUMED_UNPAUSED); ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + testDriverLock(privconn); + if (event) + testDomainEventQueue(privconn, event); + testDriverUnlock(privconn); return ret; } @@ -924,6 +985,7 @@ static int testPauseDomain (virDomainPtr { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -945,11 +1007,18 @@ static int testPauseDomain (virDomainPtr } privdom->state = VIR_DOMAIN_PAUSED; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + testDriverLock(privconn); + if (event) + testDomainEventQueue(privconn, event); + testDriverUnlock(privconn); return ret; } @@ -957,6 +1026,7 @@ static int testShutdownDomain (virDomain { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -977,6 +1047,9 @@ static int testShutdownDomain (virDomain privdom->state = VIR_DOMAIN_SHUTOFF; domain->id = -1; privdom->def->id = -1; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); @@ -987,6 +1060,8 @@ static int testShutdownDomain (virDomain cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -997,6 +1072,7 @@ static int testRebootDomain (virDomainPt { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1037,10 +1113,15 @@ static int testRebootDomain (virDomainPt break; } - if (privdom->state == VIR_DOMAIN_SHUTOFF && !privdom->persistent) { - virDomainRemoveInactive(&privconn->domains, - privdom); - privdom = NULL; + if (privdom->state == VIR_DOMAIN_SHUTOFF) { + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); + if (!privdom->persistent) { + virDomainRemoveInactive(&privconn->domains, + privdom); + privdom = NULL; + } } ret = 0; @@ -1048,6 +1129,8 @@ static int testRebootDomain (virDomainPt cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1101,6 +1184,7 @@ static int testDomainSave(virDomainPtr d int fd = -1; int len; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1155,6 +1239,9 @@ static int testDomainSave(virDomainPtr d fd = -1; privdom->state = VIR_DOMAIN_SHUTOFF; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SAVED); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); @@ -1175,6 +1262,8 @@ cleanup: } if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1189,6 +1278,7 @@ static int testDomainRestore(virConnectP int len; virDomainDefPtr def = NULL; virDomainObjPtr dom = NULL; + virDomainEventPtr event = NULL; int ret = -1; if ((fd = open(path, O_RDONLY)) < 0) { @@ -1243,6 +1333,9 @@ static int testDomainRestore(virConnectP dom->state = VIR_DOMAIN_RUNNING; dom->def->id = privconn->nextDomID++; def = NULL; + event = virDomainEventNewFromObj(dom, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_RESTORED); ret = dom->def->id; cleanup: @@ -1252,6 +1345,8 @@ cleanup: close(fd); if (dom) virDomainObjUnlock(dom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1263,6 +1358,7 @@ static int testDomainCoreDump(virDomainP testConnPtr privconn = domain->conn->privateData; int fd = -1; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1293,6 +1389,9 @@ static int testDomainCoreDump(virDomainP goto cleanup; } privdom->state = VIR_DOMAIN_SHUTOFF; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_CRASHED); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); @@ -1305,6 +1404,8 @@ cleanup: close(fd); if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1510,6 +1611,7 @@ static virDomainPtr testDomainDefineXML( virDomainPtr ret = NULL; virDomainDefPtr def; virDomainObjPtr dom = NULL; + virDomainEventPtr event = NULL; testDriverLock(privconn); if ((def = virDomainDefParseString(conn, privconn->caps, xml, @@ -1522,6 +1624,9 @@ static virDomainPtr testDomainDefineXML( } dom->persistent = 1; dom->def->id = -1; + event = virDomainEventNewFromObj(dom, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_ADDED); ret = virGetDomain(conn, def->name, def->uuid); def = NULL; @@ -1532,6 +1637,8 @@ cleanup: virDomainDefFree(def); if (dom) virDomainObjUnlock(dom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1566,6 +1673,7 @@ cleanup: static int testDomainCreate(virDomainPtr domain) { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1585,11 +1693,16 @@ static int testDomainCreate(virDomainPtr domain->id = privdom->def->id = privconn->nextDomID++; privdom->state = VIR_DOMAIN_RUNNING; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1597,6 +1710,7 @@ cleanup: static int testDomainUndefine(virDomainPtr domain) { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1615,6 +1729,9 @@ static int testDomainUndefine(virDomainP } privdom->state = VIR_DOMAIN_SHUTOFF; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_UNDEFINED, + VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); virDomainRemoveInactive(&privconn->domains, privdom); privdom = NULL; @@ -1623,6 +1740,8 @@ static int testDomainUndefine(virDomainP cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -3255,6 +3374,98 @@ static int testDevMonClose(virConnectPtr } +static int +testDomainEventRegister (virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freecb) +{ + testConnPtr driver = conn->privateData; + int ret; + + testDriverLock(driver); + ret = virDomainEventCallbackListAdd(conn, driver->domainEventCallbacks, + callback, opaque, freecb); + testDriverUnlock(driver); + + return ret; +} + +static int +testDomainEventDeregister (virConnectPtr conn, + virConnectDomainEventCallback callback) +{ + testConnPtr driver = conn->privateData; + int ret; + + testDriverLock(driver); + if (driver->domainEventDispatching) + ret = virDomainEventCallbackListMarkDelete(conn, driver->domainEventCallbacks, + callback); + else + ret = virDomainEventCallbackListRemove(conn, driver->domainEventCallbacks, + callback); + testDriverUnlock(driver); + + return ret; +} + +static void testDomainEventDispatchFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventCallback cb, + void *cbopaque, + void *opaque) +{ + testConnPtr driver = opaque; + + /* Drop the lock whle dispatching, for sake of re-entrancy */ + testDriverUnlock(driver); + virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL); + testDriverLock(driver); +} + +static void testDomainEventFlush(int timer ATTRIBUTE_UNUSED, void *opaque) +{ + testConnPtr driver = opaque; + virDomainEventQueue tempQueue; + + testDriverLock(driver); + + driver->domainEventDispatching = 1; + + /* Copy the queue, so we're reentrant safe */ + tempQueue.count = driver->domainEventQueue->count; + tempQueue.events = driver->domainEventQueue->events; + driver->domainEventQueue->count = 0; + driver->domainEventQueue->events = NULL; + + virEventUpdateTimeout(driver->domainEventTimer, -1); + virDomainEventQueueDispatch(&tempQueue, + driver->domainEventCallbacks, + testDomainEventDispatchFunc, + driver); + + /* Purge any deleted callbacks */ + virDomainEventCallbackListPurgeMarked(driver->domainEventCallbacks); + + driver->domainEventDispatching = 0; + testDriverUnlock(driver); +} + + +/* driver must be locked before calling */ +static void testDomainEventQueue(testConnPtr driver, + virDomainEventPtr event) +{ + if (virDomainEventQueuePush(driver->domainEventQueue, + event) < 0) + virDomainEventFree(event); + + if (driver->domainEventQueue->count == 1) + virEventUpdateTimeout(driver->domainEventTimer, 0); +} + + static virDriver testDriver = { VIR_DRV_TEST, "Test", @@ -3313,8 +3524,8 @@ static virDriver testDriver = { NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ - NULL, /* domainEventRegister */ - NULL, /* domainEventDeregister */ + testDomainEventRegister, /* domainEventRegister */ + testDomainEventDeregister, /* domainEventDeregister */ NULL, /* domainMigratePrepare2 */ NULL, /* domainMigrateFinish2 */ }; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:48:12PM +0000, Daniel P. Berrange wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
Updated patch to not queue events if no event loop impl is defined (as is common from virsh). This is what was breaking the tests cases Daniel diff --git a/src/test.c b/src/test.c --- a/src/test.c +++ b/src/test.c @@ -42,9 +42,12 @@ #include "memory.h" #include "network_conf.h" #include "domain_conf.h" +#include "domain_event.h" +#include "event.h" #include "storage_conf.h" #include "xml.h" #include "threads.h" +#include "logging.h" #define VIR_FROM_THIS VIR_FROM_TEST @@ -72,6 +75,13 @@ struct _testConn { virStoragePoolObjList pools; int numCells; testCell cells[MAX_CELLS]; + + + /* An array of callbacks */ + virDomainEventCallbackListPtr domainEventCallbacks; + virDomainEventQueuePtr domainEventQueue; + int domainEventTimer; + int domainEventDispatching; }; typedef struct _testConn testConn; typedef struct _testConn *testConnPtr; @@ -96,6 +106,12 @@ static const virNodeInfo defaultNodeInfo virReportErrorHelper(conn, VIR_FROM_TEST, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) +static int testClose(virConnectPtr conn); +static void testDomainEventFlush(int timer, void *opaque); +static void testDomainEventQueue(testConnPtr driver, + virDomainEventPtr event); + + static void testDriverLock(testConnPtr driver) { virMutexLock(&driver->lock); @@ -644,6 +660,22 @@ static virDrvOpenStatus testOpen(virConn ret = testOpenFromFile(conn, conn->uri->path); + if (ret == VIR_DRV_OPEN_SUCCESS) { + testConnPtr privconn = conn->privateData; + /* Init callback list */ + if (VIR_ALLOC(privconn->domainEventCallbacks) < 0 || + !(privconn->domainEventQueue = virDomainEventQueueNew())) { + virReportOOMError(NULL); + testClose(conn); + return VIR_DRV_OPEN_ERROR; + } + + if ((privconn->domainEventTimer = + virEventAddTimeout(-1, testDomainEventFlush, privconn, NULL)) < 0) + DEBUG0("virEventAddTimeout failed: No addTimeoutImpl defined. " + "continuing without events."); + } + return (ret); } @@ -655,6 +687,13 @@ static int testClose(virConnectPtr conn) virDomainObjListFree(&privconn->domains); virNetworkObjListFree(&privconn->networks); virStoragePoolObjListFree(&privconn->pools); + + virDomainEventCallbackListFree(privconn->domainEventCallbacks); + virDomainEventQueueFree(privconn->domainEventQueue); + + if (privconn->domainEventTimer != -1) + virEventRemoveTimeout(privconn->domainEventTimer); + testDriverUnlock(privconn); virMutexDestroy(&privconn->lock); @@ -733,6 +772,7 @@ testDomainCreateXML(virConnectPtr conn, virDomainPtr ret = NULL; virDomainDefPtr def; virDomainObjPtr dom = NULL; + virDomainEventPtr event = NULL; testDriverLock(privconn); if ((def = virDomainDefParseString(conn, privconn->caps, xml, @@ -747,6 +787,10 @@ testDomainCreateXML(virConnectPtr conn, dom->state = VIR_DOMAIN_RUNNING; dom->def->id = privconn->nextDomID++; + event = virDomainEventNewFromObj(dom, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); + ret = virGetDomain(conn, def->name, def->uuid); if (ret) ret->id = def->id; @@ -754,6 +798,8 @@ testDomainCreateXML(virConnectPtr conn, cleanup: if (dom) virDomainObjUnlock(dom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -860,6 +906,7 @@ static int testDestroyDomain (virDomainP { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -874,16 +921,22 @@ static int testDestroyDomain (virDomainP privdom->state = VIR_DOMAIN_SHUTOFF; privdom->def->id = -1; domain->id = -1; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_DESTROYED); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); privdom = NULL; } + ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -892,6 +945,7 @@ static int testResumeDomain (virDomainPt { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -912,11 +966,18 @@ static int testResumeDomain (virDomainPt } privdom->state = VIR_DOMAIN_RUNNING; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_RESUMED_UNPAUSED); ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + testDriverLock(privconn); + if (event) + testDomainEventQueue(privconn, event); + testDriverUnlock(privconn); return ret; } @@ -924,6 +985,7 @@ static int testPauseDomain (virDomainPtr { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -945,11 +1007,18 @@ static int testPauseDomain (virDomainPtr } privdom->state = VIR_DOMAIN_PAUSED; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + testDriverLock(privconn); + if (event) + testDomainEventQueue(privconn, event); + testDriverUnlock(privconn); return ret; } @@ -957,6 +1026,7 @@ static int testShutdownDomain (virDomain { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -977,6 +1047,9 @@ static int testShutdownDomain (virDomain privdom->state = VIR_DOMAIN_SHUTOFF; domain->id = -1; privdom->def->id = -1; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); @@ -987,6 +1060,8 @@ static int testShutdownDomain (virDomain cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -997,6 +1072,7 @@ static int testRebootDomain (virDomainPt { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1037,10 +1113,15 @@ static int testRebootDomain (virDomainPt break; } - if (privdom->state == VIR_DOMAIN_SHUTOFF && !privdom->persistent) { - virDomainRemoveInactive(&privconn->domains, - privdom); - privdom = NULL; + if (privdom->state == VIR_DOMAIN_SHUTOFF) { + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); + if (!privdom->persistent) { + virDomainRemoveInactive(&privconn->domains, + privdom); + privdom = NULL; + } } ret = 0; @@ -1048,6 +1129,8 @@ static int testRebootDomain (virDomainPt cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1101,6 +1184,7 @@ static int testDomainSave(virDomainPtr d int fd = -1; int len; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1155,6 +1239,9 @@ static int testDomainSave(virDomainPtr d fd = -1; privdom->state = VIR_DOMAIN_SHUTOFF; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SAVED); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); @@ -1175,6 +1262,8 @@ cleanup: } if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1189,6 +1278,7 @@ static int testDomainRestore(virConnectP int len; virDomainDefPtr def = NULL; virDomainObjPtr dom = NULL; + virDomainEventPtr event = NULL; int ret = -1; if ((fd = open(path, O_RDONLY)) < 0) { @@ -1243,6 +1333,9 @@ static int testDomainRestore(virConnectP dom->state = VIR_DOMAIN_RUNNING; dom->def->id = privconn->nextDomID++; def = NULL; + event = virDomainEventNewFromObj(dom, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_RESTORED); ret = dom->def->id; cleanup: @@ -1252,6 +1345,8 @@ cleanup: close(fd); if (dom) virDomainObjUnlock(dom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1263,6 +1358,7 @@ static int testDomainCoreDump(virDomainP testConnPtr privconn = domain->conn->privateData; int fd = -1; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1293,6 +1389,9 @@ static int testDomainCoreDump(virDomainP goto cleanup; } privdom->state = VIR_DOMAIN_SHUTOFF; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_CRASHED); if (!privdom->persistent) { virDomainRemoveInactive(&privconn->domains, privdom); @@ -1305,6 +1404,8 @@ cleanup: close(fd); if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1510,6 +1611,7 @@ static virDomainPtr testDomainDefineXML( virDomainPtr ret = NULL; virDomainDefPtr def; virDomainObjPtr dom = NULL; + virDomainEventPtr event = NULL; testDriverLock(privconn); if ((def = virDomainDefParseString(conn, privconn->caps, xml, @@ -1522,6 +1624,9 @@ static virDomainPtr testDomainDefineXML( } dom->persistent = 1; dom->def->id = -1; + event = virDomainEventNewFromObj(dom, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_ADDED); ret = virGetDomain(conn, def->name, def->uuid); def = NULL; @@ -1532,6 +1637,8 @@ cleanup: virDomainDefFree(def); if (dom) virDomainObjUnlock(dom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1566,6 +1673,7 @@ cleanup: static int testDomainCreate(virDomainPtr domain) { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1585,11 +1693,16 @@ static int testDomainCreate(virDomainPtr domain->id = privdom->def->id = privconn->nextDomID++; privdom->state = VIR_DOMAIN_RUNNING; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); ret = 0; cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -1597,6 +1710,7 @@ cleanup: static int testDomainUndefine(virDomainPtr domain) { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1; testDriverLock(privconn); @@ -1615,6 +1729,9 @@ static int testDomainUndefine(virDomainP } privdom->state = VIR_DOMAIN_SHUTOFF; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_UNDEFINED, + VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); virDomainRemoveInactive(&privconn->domains, privdom); privdom = NULL; @@ -1623,6 +1740,8 @@ static int testDomainUndefine(virDomainP cleanup: if (privdom) virDomainObjUnlock(privdom); + if (event) + testDomainEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -3255,6 +3374,103 @@ static int testDevMonClose(virConnectPtr } +static int +testDomainEventRegister (virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freecb) +{ + testConnPtr driver = conn->privateData; + int ret; + + testDriverLock(driver); + ret = virDomainEventCallbackListAdd(conn, driver->domainEventCallbacks, + callback, opaque, freecb); + testDriverUnlock(driver); + + return ret; +} + +static int +testDomainEventDeregister (virConnectPtr conn, + virConnectDomainEventCallback callback) +{ + testConnPtr driver = conn->privateData; + int ret; + + testDriverLock(driver); + if (driver->domainEventDispatching) + ret = virDomainEventCallbackListMarkDelete(conn, driver->domainEventCallbacks, + callback); + else + ret = virDomainEventCallbackListRemove(conn, driver->domainEventCallbacks, + callback); + testDriverUnlock(driver); + + return ret; +} + +static void testDomainEventDispatchFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventCallback cb, + void *cbopaque, + void *opaque) +{ + testConnPtr driver = opaque; + + /* Drop the lock whle dispatching, for sake of re-entrancy */ + testDriverUnlock(driver); + virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL); + testDriverLock(driver); +} + +static void testDomainEventFlush(int timer ATTRIBUTE_UNUSED, void *opaque) +{ + testConnPtr driver = opaque; + virDomainEventQueue tempQueue; + + testDriverLock(driver); + + driver->domainEventDispatching = 1; + + /* Copy the queue, so we're reentrant safe */ + tempQueue.count = driver->domainEventQueue->count; + tempQueue.events = driver->domainEventQueue->events; + driver->domainEventQueue->count = 0; + driver->domainEventQueue->events = NULL; + + virEventUpdateTimeout(driver->domainEventTimer, -1); + virDomainEventQueueDispatch(&tempQueue, + driver->domainEventCallbacks, + testDomainEventDispatchFunc, + driver); + + /* Purge any deleted callbacks */ + virDomainEventCallbackListPurgeMarked(driver->domainEventCallbacks); + + driver->domainEventDispatching = 0; + testDriverUnlock(driver); +} + + +/* driver must be locked before calling */ +static void testDomainEventQueue(testConnPtr driver, + virDomainEventPtr event) +{ + if (driver->domainEventTimer < 0) { + virDomainEventFree(event); + return; + } + + if (virDomainEventQueuePush(driver->domainEventQueue, + event) < 0) + virDomainEventFree(event); + + if (driver->domainEventQueue->count == 1) + virEventUpdateTimeout(driver->domainEventTimer, 0); +} + + static virDriver testDriver = { VIR_DRV_TEST, "Test", @@ -3313,8 +3529,8 @@ static virDriver testDriver = { NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ - NULL, /* domainEventRegister */ - NULL, /* domainEventDeregister */ + testDomainEventRegister, /* domainEventRegister */ + testDomainEventDeregister, /* domainEventDeregister */ NULL, /* domainMigratePrepare2 */ NULL, /* domainMigrateFinish2 */ }; diff --git a/tests/read-bufsiz b/tests/read-bufsiz --- a/tests/read-bufsiz +++ b/tests/read-bufsiz @@ -21,6 +21,9 @@ if test "$VERBOSE" = yes; then virsh --version fi +test -z "$srcdir" && srcdir=`pwd` +test -z "$abs_top_srcdir" && abs_top_srcdir=`pwd`/.. + . $srcdir/test-lib.sh fail=0 -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:48:12PM +0000, Daniel P. Berrange wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
Updated patch to not queue events if no event loop impl is defined (as is common from virsh). This is what was breaking the tests cases
Thanks! With that, there's only one failure remaining: 35) Xen SEXPR-2-XML fv-sound-all -> fv-sound-all ... FAILED FAIL: sexpr2xmltest

Jim Meyering <jim@meyering.net> wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:48:12PM +0000, Daniel P. Berrange wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
Updated patch to not queue events if no event loop impl is defined (as is common from virsh). This is what was breaking the tests cases
Thanks! With that, there's only one failure remaining:
35) Xen SEXPR-2-XML fv-sound-all -> fv-sound-all ... FAILED FAIL: sexpr2xmltest
In your 25-patch series, the new syntax check rule, sc_prohibit_nonreentrant (nice!) fails because it finds lots of uses of strerror. Did I miss a patch? This looks like the change I would have expected to remove those: Remove use of strerror() http://git.et.redhat.com/?p=libvirt.git;a=commitdiff;h=9d17447c64a54ab620f6b Here's a sample: src/uml_driver.c:796: errno, strerror(errno)); src/uml_driver.c:806: errno, strerror(errno)); src/qemu_driver.c:1029: errno, strerror(errno)); src/storage_conf.c:1351: configDir, strerror(errno)); src/console.c:131: strerror(errno)); src/iptables.c:250: rules->path, strerror(err)); qemud/qemud.c:207: type, file, strerror(errno), errno); Also, I needed this patch to get past a po-check failure: diff --git a/po/POTFILES.in b/po/POTFILES.in index 3d5e715..49b72e7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,6 +15,7 @@ src/network_conf.c src/network_driver.c src/node_device.c src/node_device_conf.c +src/nodeinfo.c src/openvz_conf.c src/openvz_driver.c src/proxy_internal.c

On Wed, Jan 14, 2009 at 09:57:26AM +0100, Jim Meyering wrote:
Jim Meyering <jim@meyering.net> wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:48:12PM +0000, Daniel P. Berrange wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
Updated patch to not queue events if no event loop impl is defined (as is common from virsh). This is what was breaking the tests cases
Thanks! With that, there's only one failure remaining:
35) Xen SEXPR-2-XML fv-sound-all -> fv-sound-all ... FAILED FAIL: sexpr2xmltest
In your 25-patch series, the new syntax check rule, sc_prohibit_nonreentrant (nice!) fails because it finds lots of uses of strerror. Did I miss a patch? This looks like the change I would have expected to remove those:
No, I forgot to mention there were still many more to remove. The ones I have not dealt with are less critical because they're only used in the logging calls like DEBUG(), rather than app facing virRaiseError calls. A number of these logging calls should be removed (if the errno is already propagated to caller where its reported), or converted into error calls. There'd still be a number of calls to strerror() left though. I didn't do this yet because it'll cause even more conflicts with other peoples work than my patchset already has. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Wed, Jan 14, 2009 at 09:57:26AM +0100, Jim Meyering wrote:
Jim Meyering <jim@meyering.net> wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:48:12PM +0000, Daniel P. Berrange wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
Updated patch to not queue events if no event loop impl is defined (as is common from virsh). This is what was breaking the tests cases
Thanks! With that, there's only one failure remaining:
35) Xen SEXPR-2-XML fv-sound-all -> fv-sound-all ... FAILED FAIL: sexpr2xmltest
In your 25-patch series, the new syntax check rule, sc_prohibit_nonreentrant (nice!) fails because it finds lots of uses of strerror. Did I miss a patch? This looks like the change I would have expected to remove those:
No, I forgot to mention there were still many more to remove. The ones I have not dealt with are less critical because they're only used in the logging calls like DEBUG(), rather than app facing virRaiseError calls.
A number of these logging calls should be removed (if the errno is already propagated to caller where its reported), or converted into error calls. There'd still be a number of calls to strerror() left though.
I didn't do this yet because it'll cause even more conflicts with other peoples work than my patchset already has.
Sounds prudent. In that case, you might want to add sc_prohibit_nonreentrant to the "local-checks-to-skip" list in Makefile.cfg. That will make it so "make syntax-check" skips that rule.

On Tue, Jan 13, 2009 at 09:51:52PM +0000, Daniel P. Berrange wrote:
On Tue, Jan 13, 2009 at 05:48:12PM +0000, Daniel P. Berrange wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
Updated patch to not queue events if no event loop impl is defined (as is common from virsh). This is what was breaking the tests cases
Jim seems to have covered this ... Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This adds support for the domain events in the test driver. Code is following the same pattern as the impl in the QEMU driver.
test.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-)
ACK. Looks fine. One minor optimization below:
diff --git a/src/test.c b/src/test.c ... @@ -924,6 +985,7 @@ static int testPauseDomain (virDomainPtr { testConnPtr privconn = domain->conn->privateData; virDomainObjPtr privdom; + virDomainEventPtr event = NULL; int ret = -1;
testDriverLock(privconn); @@ -945,11 +1007,18 @@ static int testPauseDomain (virDomainPtr }
privdom->state = VIR_DOMAIN_PAUSED; + event = virDomainEventNewFromObj(privdom, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); ret = 0;
cleanup: if (privdom) virDomainObjUnlock(privdom); + testDriverLock(privconn); + if (event) + testDomainEventQueue(privconn, event); + testDriverUnlock(privconn);
If event is NULL, don't fiddle with the lock at all: if (event) { testDriverLock(privconn); testDomainEventQueue(privconn, event); testDriverUnlock(privconn); } There is at least one other just like this.

The virGetLastError() method is now stored in a thread local and virConnGetLastError() is deprecated, so switch to only use the former. Also, no need to copy the error object since it is thread local already thus guarenteed not to change behind our backs libvir.c | 44 ++++++++++++++++++++++---------------------- libvir.py | 7 +++---- 2 files changed, 25 insertions(+), 26 deletions(-) Daniel diff --git a/python/libvir.c b/python/libvir.c --- a/python/libvir.c +++ b/python/libvir.c @@ -438,23 +438,23 @@ static PyObject *libvirt_virPythonErrorF static PyObject * libvirt_virGetLastError(PyObject *self ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) { - virError err; + virError *err; PyObject *info; - if (virCopyLastError(&err) <= 0) + if ((err = virGetLastError()) == NULL) return VIR_PY_NONE; if ((info = PyTuple_New(9)) == NULL) return VIR_PY_NONE; - PyTuple_SetItem(info, 0, PyInt_FromLong((long) err.code)); - PyTuple_SetItem(info, 1, PyInt_FromLong((long) err.domain)); - PyTuple_SetItem(info, 2, libvirt_constcharPtrWrap(err.message)); - PyTuple_SetItem(info, 3, PyInt_FromLong((long) err.level)); - PyTuple_SetItem(info, 4, libvirt_constcharPtrWrap(err.str1)); - PyTuple_SetItem(info, 5, libvirt_constcharPtrWrap(err.str2)); - PyTuple_SetItem(info, 6, libvirt_constcharPtrWrap(err.str3)); - PyTuple_SetItem(info, 7, PyInt_FromLong((long) err.int1)); - PyTuple_SetItem(info, 8, PyInt_FromLong((long) err.int2)); + PyTuple_SetItem(info, 0, PyInt_FromLong((long) err->code)); + PyTuple_SetItem(info, 1, PyInt_FromLong((long) err->domain)); + PyTuple_SetItem(info, 2, libvirt_constcharPtrWrap(err->message)); + PyTuple_SetItem(info, 3, PyInt_FromLong((long) err->level)); + PyTuple_SetItem(info, 4, libvirt_constcharPtrWrap(err->str1)); + PyTuple_SetItem(info, 5, libvirt_constcharPtrWrap(err->str2)); + PyTuple_SetItem(info, 6, libvirt_constcharPtrWrap(err->str3)); + PyTuple_SetItem(info, 7, PyInt_FromLong((long) err->int1)); + PyTuple_SetItem(info, 8, PyInt_FromLong((long) err->int2)); return info; } @@ -462,7 +462,7 @@ libvirt_virGetLastError(PyObject *self A static PyObject * libvirt_virConnGetLastError(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { - virError err; + virError *err; PyObject *info; virConnectPtr conn; PyObject *pyobj_conn; @@ -471,20 +471,20 @@ libvirt_virConnGetLastError(PyObject *se return(NULL); conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); - if (virConnCopyLastError(conn, &err) <= 0) + if ((err = virConnGetLastError(conn)) == NULL) return VIR_PY_NONE; if ((info = PyTuple_New(9)) == NULL) return VIR_PY_NONE; - PyTuple_SetItem(info, 0, PyInt_FromLong((long) err.code)); - PyTuple_SetItem(info, 1, PyInt_FromLong((long) err.domain)); - PyTuple_SetItem(info, 2, libvirt_constcharPtrWrap(err.message)); - PyTuple_SetItem(info, 3, PyInt_FromLong((long) err.level)); - PyTuple_SetItem(info, 4, libvirt_constcharPtrWrap(err.str1)); - PyTuple_SetItem(info, 5, libvirt_constcharPtrWrap(err.str2)); - PyTuple_SetItem(info, 6, libvirt_constcharPtrWrap(err.str3)); - PyTuple_SetItem(info, 7, PyInt_FromLong((long) err.int1)); - PyTuple_SetItem(info, 8, PyInt_FromLong((long) err.int2)); + PyTuple_SetItem(info, 0, PyInt_FromLong((long) err->code)); + PyTuple_SetItem(info, 1, PyInt_FromLong((long) err->domain)); + PyTuple_SetItem(info, 2, libvirt_constcharPtrWrap(err->message)); + PyTuple_SetItem(info, 3, PyInt_FromLong((long) err->level)); + PyTuple_SetItem(info, 4, libvirt_constcharPtrWrap(err->str1)); + PyTuple_SetItem(info, 5, libvirt_constcharPtrWrap(err->str2)); + PyTuple_SetItem(info, 6, libvirt_constcharPtrWrap(err->str3)); + PyTuple_SetItem(info, 7, PyInt_FromLong((long) err->int1)); + PyTuple_SetItem(info, 8, PyInt_FromLong((long) err->int2)); return info; } diff --git a/python/libvir.py b/python/libvir.py --- a/python/libvir.py +++ b/python/libvir.py @@ -26,10 +26,9 @@ class libvirtError(Exception): elif vol is not None: conn = vol._conn - if conn is None: - err = virGetLastError() - else: - err = conn.virConnGetLastError() + # Never call virConnGetLastError(). + # virGetLastError() is now thread local + err = virGetLastError() if err is None: msg = defmsg else: -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:48:40PM +0000, Daniel P. Berrange wrote:
The virGetLastError() method is now stored in a thread local and virConnGetLastError() is deprecated, so switch to only use the former. Also, no need to copy the error object since it is thread local already thus guarenteed not to change behind our backs
libvir.c | 44 ++++++++++++++++++++++---------------------- libvir.py | 7 +++---- 2 files changed, 25 insertions(+), 26 deletions(-)
Simple, +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

On Tue, Jan 13, 2009 at 05:48:40PM +0000, Daniel P. Berrange wrote:
The virGetLastError() method is now stored in a thread local and virConnGetLastError() is deprecated, so switch to only use the former. Also, no need to copy the error object since it is thread local already thus guarenteed not to change behind our backs
okay, fine, +1, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver. Also remove some unused cruft relating to sound in Xen. domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-) Daniel diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -121,7 +121,8 @@ VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_C VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", - "pcspk") + "pcspk", + "ac97") VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", diff --git a/src/domain_conf.h b/src/domain_conf.h --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -236,6 +236,7 @@ enum virDomainSoundModel { VIR_DOMAIN_SOUND_MODEL_SB16, VIR_DOMAIN_SOUND_MODEL_ES1370, VIR_DOMAIN_SOUND_MODEL_PCSPK, + VIR_DOMAIN_SOUND_MODEL_ES97, VIR_DOMAIN_SOUND_MODEL_LAST }; diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -713,52 +713,6 @@ urlencode(const char *string) } #endif /* ! PROXY */ -/* Applicable sound models */ -static const char *const sound_models[] = { "sb16", "es1370" }; - -/** - * is_sound_model_valid: - * @model : model string to check against whitelist - * - * checks passed model string against whitelist of acceptable models - * - * Returns 0 if invalid, 1 otherwise - */ -int is_sound_model_valid(const char *model) { - int i; - - for (i = 0; i < sizeof(sound_models)/sizeof(*sound_models); ++i) { - if (STREQ(model, sound_models[i])) { - return 1; - } - } - return 0; -} - -/** - * is_sound_model_conflict: - * @model : model string to look for duplicates of - * @soundstr : soundhw string for the form m1,m2,m3 ... - * - * Returns 0 if no conflict, 1 otherwise - */ -int is_sound_model_conflict(const char *model, const char *soundstr) { - - char *dupe; - char *cur = (char *) soundstr; - while ((dupe = strstr(cur, model))) { - if (( (dupe == cur) || // (Start of line | - (*(dupe - 1) == ',') ) && // Preceded by comma) & - ( (dupe[strlen(model)] == ',') || // (Ends with comma | - (dupe[strlen(model)] == '\0') )) // Ends whole string) - return 1; - else - cur = dupe + strlen(model); - } - return 0; -} - - /* PUBLIC FUNCTIONS */ /** -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Also remove some unused cruft relating to sound in Xen.
domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-)
Now we added 'ac97', we can no longer blindly convert Xen's 'all' string into all possible sound card types - must restrict it to just the 3 that were around historically. This fixes the Xen test case Daniel diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -121,7 +121,8 @@ VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_C VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", - "pcspk") + "pcspk", + "ac97") VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", diff --git a/src/domain_conf.h b/src/domain_conf.h --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -236,6 +236,7 @@ enum virDomainSoundModel { VIR_DOMAIN_SOUND_MODEL_SB16, VIR_DOMAIN_SOUND_MODEL_ES1370, VIR_DOMAIN_SOUND_MODEL_PCSPK, + VIR_DOMAIN_SOUND_MODEL_ES97, VIR_DOMAIN_SOUND_MODEL_LAST }; diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -713,52 +713,6 @@ urlencode(const char *string) } #endif /* ! PROXY */ -/* Applicable sound models */ -static const char *const sound_models[] = { "sb16", "es1370" }; - -/** - * is_sound_model_valid: - * @model : model string to check against whitelist - * - * checks passed model string against whitelist of acceptable models - * - * Returns 0 if invalid, 1 otherwise - */ -int is_sound_model_valid(const char *model) { - int i; - - for (i = 0; i < sizeof(sound_models)/sizeof(*sound_models); ++i) { - if (STREQ(model, sound_models[i])) { - return 1; - } - } - return 0; -} - -/** - * is_sound_model_conflict: - * @model : model string to look for duplicates of - * @soundstr : soundhw string for the form m1,m2,m3 ... - * - * Returns 0 if no conflict, 1 otherwise - */ -int is_sound_model_conflict(const char *model, const char *soundstr) { - - char *dupe; - char *cur = (char *) soundstr; - while ((dupe = strstr(cur, model))) { - if (( (dupe == cur) || // (Start of line | - (*(dupe - 1) == ',') ) && // Preceded by comma) & - ( (dupe[strlen(model)] == ',') || // (Ends with comma | - (dupe[strlen(model)] == '\0') )) // Ends whole string) - return 1; - else - cur = dupe + strlen(model); - } - return 0; -} - - /* PUBLIC FUNCTIONS */ /** @@ -1864,11 +1818,25 @@ xenDaemonParseSxprSound(virConnectPtr co if (STREQ(str, "all")) { int i; + /* + * Special compatability code for Xen with a bogus + * sound=all in config. + * + * NB delibrately, don't include all possible + * sound models anymore, just the 3 that were + * historically present in QEMU. + * + * ie no ac97. + * + * Hence use of MODEL_PCSPK + 1, instead of MODEL_LAST + */ + if (VIR_ALLOC_N(def->sounds, - VIR_DOMAIN_SOUND_MODEL_LAST) < 0) - goto no_memory; - - for (i = 0 ; i < VIR_DOMAIN_SOUND_MODEL_LAST ; i++) { + VIR_DOMAIN_SOUND_MODEL_PCSPK + 1) < 0) + goto no_memory; + + + for (i = 0 ; i < (VIR_DOMAIN_SOUND_MODEL_PCSPK + 1) ; i++) { virDomainSoundDefPtr sound; if (VIR_ALLOC(sound) < 0) goto no_memory; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Also remove some unused cruft relating to sound in Xen.
domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-)
Now we added 'ac97', we can no longer blindly convert Xen's 'all' string into all possible sound card types - must restrict it to just the 3 that were around historically. This fixes the Xen test case
Daniel
diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -121,7 +121,8 @@ VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_C VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", - "pcspk") + "pcspk", + "ac97")
VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", diff --git a/src/domain_conf.h b/src/domain_conf.h --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -236,6 +236,7 @@ enum virDomainSoundModel { VIR_DOMAIN_SOUND_MODEL_SB16, VIR_DOMAIN_SOUND_MODEL_ES1370, VIR_DOMAIN_SOUND_MODEL_PCSPK, + VIR_DOMAIN_SOUND_MODEL_ES97,
VIR_DOMAIN_SOUND_MODEL_LAST }; diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -713,52 +713,6 @@ urlencode(const char *string) } #endif /* ! PROXY */
-/* Applicable sound models */ -static const char *const sound_models[] = { "sb16", "es1370" }; - -/** - * is_sound_model_valid: - * @model : model string to check against whitelist - * - * checks passed model string against whitelist of acceptable models - * - * Returns 0 if invalid, 1 otherwise - */ -int is_sound_model_valid(const char *model) { - int i; - - for (i = 0; i < sizeof(sound_models)/sizeof(*sound_models); ++i) { - if (STREQ(model, sound_models[i])) { - return 1; - } - } - return 0; -} - -/** - * is_sound_model_conflict: - * @model : model string to look for duplicates of - * @soundstr : soundhw string for the form m1,m2,m3 ... - * - * Returns 0 if no conflict, 1 otherwise - */ -int is_sound_model_conflict(const char *model, const char *soundstr) { - - char *dupe; - char *cur = (char *) soundstr; - while ((dupe = strstr(cur, model))) { - if (( (dupe == cur) || // (Start of line | - (*(dupe - 1) == ',') ) && // Preceded by comma) & - ( (dupe[strlen(model)] == ',') || // (Ends with comma | - (dupe[strlen(model)] == '\0') )) // Ends whole string) - return 1; - else - cur = dupe + strlen(model); - } - return 0; -} - - /* PUBLIC FUNCTIONS */
/** @@ -1864,11 +1818,25 @@ xenDaemonParseSxprSound(virConnectPtr co if (STREQ(str, "all")) { int i;
+ /* + * Special compatability code for Xen with a bogus + * sound=all in config. + * + * NB delibrately, don't include all possible + * sound models anymore, just the 3 that were + * historically present in QEMU. + * + * ie no ac97. + * + * Hence use of MODEL_PCSPK + 1, instead of MODEL_LAST + */ +
Prior to the xml parsing unification, 'pcspk' was not allowed as a valid option for xen. At least the xen version on f8 didn't offer it, so this may be incorrect. Not sure if current upstream xen supports 'pcspk' though. Besides that issue, the patch looks good. Thanks, Cole

On Wed, Jan 14, 2009 at 09:34:51AM -0500, Cole Robinson wrote:
Daniel P. Berrange wrote:
On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Also remove some unused cruft relating to sound in Xen.
domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-)
Now we added 'ac97', we can no longer blindly convert Xen's 'all' string into all possible sound card types - must restrict it to just the 3 that were around historically. This fixes the Xen test case
Prior to the xml parsing unification, 'pcspk' was not allowed as a valid option for xen. At least the xen version on f8 didn't offer it, so this may be incorrect. Not sure if current upstream xen supports 'pcspk' though.
Yes, logs show I mistakenly added pcspk. I'll remove that from the back-compat support too. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Also remove some unused cruft relating to sound in Xen.
domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-)
Now we added 'ac97', we can no longer blindly convert Xen's 'all' string into all possible sound card types - must restrict it to just the 3 that were around historically. This fixes the Xen test case
Thanks. That looks fine and fixed it for me. I've pushed that incremental to the git branch, along with the two suggested syntax-check fixes.

On Wed, Jan 14, 2009 at 11:41:32AM +0000, Daniel P. Berrange wrote:
On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Also remove some unused cruft relating to sound in Xen.
domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-)
Now we added 'ac97', we can no longer blindly convert Xen's 'all' string into all possible sound card types - must restrict it to just the 3 that were around historically. This fixes the Xen test case
Cole & Jim seem to have covered this one. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora

On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Not related to the threading really, looks fine. Any automatic way to detect unused functions ? I guess we can't expect the compiler or linker to help in this case since we make them public for internal consumption. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Jan 16, 2009 at 04:59:13PM +0100, Daniel Veillard wrote:
On Tue, Jan 13, 2009 at 05:49:02PM +0000, Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Not related to the threading really, looks fine.
Any automatic way to detect unused functions ? I guess we can't expect the compiler or linker to help in this case since we make them public for internal consumption.
CIL can do this easily. However, over the weekend I was looking at the new -fwhole-program and -combine options to gcc. They are intended to do whole-program optimization, but because of the way they work they can also remove unused functions from the output: $ cat test1.c #include <stdio.h> void unused (void) { printf ("I'm not used.\n"); } $ cat test2.c #include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { printf ("hello, world\n"); exit (0); } $ gcc -Wall -c test1.c -o test1.o $ gcc -Wall -c test2.c -o test2.o $ gcc -Wall test1.o test2.o -o test $ nm test | grep unused 000000000040050c T unused $ gcc -Wall -combine -fwhole-program test1.c test2.c -o test $ nm test | grep unused One problem is that the rest of the toolchain (ie. libtool) doesn't understand them yet :-( Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/

Hi All, Small patch for updating the schema file and testcase for the same: diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 2e2e408..107215c 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -874,6 +874,7 @@ <value>sb16</value> <value>es1370</value> <value>pcspk</value> + <value>ac97</value> </choice> </attribute> </element> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-sound.xml b/tests/qemuxml2argvdata/qemuxml2argv-sound.xml index cc85c53..8c33e6c 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-sound.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-sound.xml @@ -21,5 +21,6 @@ <sound model='pcspk'/> <sound model='es1370'/> <sound model='sb16'/> + <sound model='ac97'/> </devices> </domain> Regards, -pritesh On Tuesday 13 January 2009 18:49:02 Daniel P. Berrange wrote:
QEMU now has support for a sound card of type "ac97", so enable that in the XML parser / qemu driver.
Also remove some unused cruft relating to sound in Xen.
domain_conf.c | 3 ++- domain_conf.h | 1 + xend_internal.c | 46 ---------------------------------------------- 3 files changed, 3 insertions(+), 47 deletions(-)
Daniel
diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -121,7 +121,8 @@ VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_C VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", - "pcspk") + "pcspk", + "ac97")
VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", diff --git a/src/domain_conf.h b/src/domain_conf.h --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -236,6 +236,7 @@ enum virDomainSoundModel { VIR_DOMAIN_SOUND_MODEL_SB16, VIR_DOMAIN_SOUND_MODEL_ES1370, VIR_DOMAIN_SOUND_MODEL_PCSPK, + VIR_DOMAIN_SOUND_MODEL_ES97,
VIR_DOMAIN_SOUND_MODEL_LAST }; diff --git a/src/xend_internal.c b/src/xend_internal.c --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -713,52 +713,6 @@ urlencode(const char *string) } #endif /* ! PROXY */
-/* Applicable sound models */ -static const char *const sound_models[] = { "sb16", "es1370" }; - -/** - * is_sound_model_valid: - * @model : model string to check against whitelist - * - * checks passed model string against whitelist of acceptable models - * - * Returns 0 if invalid, 1 otherwise - */ -int is_sound_model_valid(const char *model) { - int i; - - for (i = 0; i < sizeof(sound_models)/sizeof(*sound_models); ++i) { - if (STREQ(model, sound_models[i])) { - return 1; - } - } - return 0; -} - -/** - * is_sound_model_conflict: - * @model : model string to look for duplicates of - * @soundstr : soundhw string for the form m1,m2,m3 ... - * - * Returns 0 if no conflict, 1 otherwise - */ -int is_sound_model_conflict(const char *model, const char *soundstr) { - - char *dupe; - char *cur = (char *) soundstr; - while ((dupe = strstr(cur, model))) { - if (( (dupe == cur) || // (Start of line | - (*(dupe - 1) == ',') ) && // Preceded by comma) & - ( (dupe[strlen(model)] == ',') || // (Ends with comma | - (dupe[strlen(model)] == '\0') )) // Ends whole string) - return 1; - else - cur = dupe + strlen(model); - } - return 0; -} - - /* PUBLIC FUNCTIONS */
/**

On Tue, Mar 10, 2009 at 11:46:13AM +0100, Pritesh Kothari wrote:
Hi All,
Small patch for updating the schema file and testcase for the same:
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 2e2e408..107215c 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -874,6 +874,7 @@ <value>sb16</value> <value>es1370</value> <value>pcspk</value> + <value>ac97</value> </choice> </attribute> </element>
ACK,
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-sound.xml b/tests/qemuxml2argvdata/qemuxml2argv-sound.xml index cc85c53..8c33e6c 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-sound.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-sound.xml @@ -21,5 +21,6 @@ <sound model='pcspk'/> <sound model='es1370'/> <sound model='sb16'/> + <sound model='ac97'/> </devices> </domain>
If you change this file, you'll also need to change the corresponding file tests/qemuxml2argvdata/qemuxml2argv-sound.args, which is the command line ARGV associated with that XML. Should merely need to add ',ac97' into the appropriate place. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Hi All,
If you change this file, you'll also need to change the corresponding file tests/qemuxml2argvdata/qemuxml2argv-sound.args, which is the command line ARGV associated with that XML. Should merely need to add ',ac97' into the appropriate place.
Resubmitting the patch with above change in it. (Including it as a attachment because the mail client clips the line at column 80 :( ) Regards, -pritesh
Regards, Daniel

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch series is (mostly) aimed at providing full support for threads in the libvirt API. Specifically we remove the restriction that a single virConnectPtr is tied to a thread. Multiple threads can safely use the same object (provided one doesn't free it behind another's back :-P )
This is tested to compile on Linux + Win32 (MinGW), hopefully doesn't break Solaris, but I'll be testing that soon too.
I've tortured the patches in various nasty ways are they're now stable enough to let loose and probably merge for wider testing. It'll probably be easier to just fix future problems in tree, rather than maintain this patchset longer.
The full patchset is also available here
http://berrange.fedorapeople.org/libvirt-threads-2009-01-13/
Thanks! In order to make it easier for others to evaluate, I've applied all of that, massaging headers so that "git am --whitespace=fix" would do the right thing. The sole non-whitespace change was to remove a single conflicting hunk in #17, which was due to my s/X_OK/R_OK/ fix. The result is now in the mirrored git tree: http://git.et.redhat.com/?p=libvirt.git;a=shortlog;h=refs/heads/danpb-thread... Once you have a clone of the tree (or if you have an existing one), you can check out that branch like this: git checkout -b danpb-threads origin/danpb-threads I expect to rebase it as things evolve, and eventually to discard it once everything makes it to "master".
participants (7)
-
Cole Robinson
-
Daniel P. Berrange
-
Daniel Veillard
-
Dave Allan
-
Jim Meyering
-
Pritesh Kothari
-
Richard W.M. Jones