[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen staging] tools: split libxenstore into new tools/libs/store directory
commit dd33fd2e81a0e83707cc203b974e501fa336590f Author: Juergen Gross <jgross@xxxxxxxx> AuthorDate: Fri Aug 28 17:07:36 2020 +0200 Commit: Wei Liu <wl@xxxxxxx> CommitDate: Wed Sep 9 10:57:25 2020 +0000 tools: split libxenstore into new tools/libs/store directory There is no reason why libxenstore is not placed in the tools/libs directory. The common files between libxenstore and xenstored are kept in the tools/xenstore directory to be easily accessible by xenstore-stubdom which needs the xenstored files to be built. Signed-off-by: Juergen Gross <jgross@xxxxxxxx> Acked-by: Wei Liu <wl@xxxxxxx> --- .gitignore | 7 +- stubdom/mini-os.mk | 2 +- tools/Makefile | 2 +- tools/Rules.mk | 5 - tools/libs/Makefile | 1 + tools/libs/store/Makefile | 64 ++ tools/libs/store/include/compat/xs.h | 2 + tools/libs/store/include/compat/xs_lib.h | 2 + tools/libs/store/include/xenstore.h | 290 ++++++ tools/libs/store/libxenstore.map | 49 + tools/libs/store/xs.c | 1473 ++++++++++++++++++++++++++++++ tools/libs/uselibs.mk | 2 + tools/python/setup.py | 2 +- tools/xenstore/Makefile | 86 +- tools/xenstore/include/compat/xs.h | 2 - tools/xenstore/include/compat/xs_lib.h | 2 - tools/xenstore/include/xenstore.h | 290 ------ tools/xenstore/include/xenstore_lib.h | 94 -- tools/xenstore/xenstore_lib.h | 94 ++ tools/xenstore/xs.c | 1473 ------------------------------ 20 files changed, 1990 insertions(+), 1952 deletions(-) diff --git a/.gitignore b/.gitignore index eb637a98e9..1335034fd3 100644 --- a/.gitignore +++ b/.gitignore @@ -128,6 +128,12 @@ tools/libs/guest/xc_core.h tools/libs/guest/xc_core_arm.h tools/libs/guest/xc_core_x86.h tools/libs/guest/xc_private.h +tools/libs/store/headers.chk +tools/libs/store/list.h +tools/libs/store/utils.h +tools/libs/store/xenstore.pc +tools/libs/store/xs_lib.c +tools/libs/store/include/xenstore_lib.h tools/console/xenconsole tools/console/xenconsoled tools/console/client/_paths.h @@ -282,7 +288,6 @@ tools/xenstore/xenstore-control tools/xenstore/xenstore-ls tools/xenstore/xenstored tools/xenstore/xenstored_test -tools/xenstore/xenstore.pc tools/xenstore/xs_tdb_dump tools/xentrace/xentrace_setsize tools/xentrace/tbctl diff --git a/stubdom/mini-os.mk b/stubdom/mini-os.mk index e1640a7cbc..420e9a8771 100644 --- a/stubdom/mini-os.mk +++ b/stubdom/mini-os.mk @@ -5,7 +5,7 @@ # XEN_ROOT # MINIOS_TARGET_ARCH -XENSTORE_CPPFLAGS = -isystem $(XEN_ROOT)/tools/xenstore/include +XENSTORE_CPPFLAGS = -isystem $(XEN_ROOT)/tools/libs/store/include TOOLCORE_PATH = $(XEN_ROOT)/stubdom/libs-$(MINIOS_TARGET_ARCH)/toolcore TOOLLOG_PATH = $(XEN_ROOT)/stubdom/libs-$(MINIOS_TARGET_ARCH)/toollog EVTCHN_PATH = $(XEN_ROOT)/stubdom/libs-$(MINIOS_TARGET_ARCH)/evtchn diff --git a/tools/Makefile b/tools/Makefile index f9b4012290..4a3646871c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -43,7 +43,7 @@ SUBDIRS-y += pygrub SUBDIRS-$(OCAML_TOOLS) += ocaml ifeq ($(CONFIG_RUMP),y) -SUBDIRS-y := libs xenstore +SUBDIRS-y := libs endif # For the sake of linking, set the sys-root diff --git a/tools/Rules.mk b/tools/Rules.mk index e17ac3ecc6..21aa8ea844 100644 --- a/tools/Rules.mk +++ b/tools/Rules.mk @@ -18,7 +18,6 @@ include $(XEN_ROOT)/tools/libs/uselibs.mk XEN_libxenlight = $(XEN_ROOT)/tools/libxl # Currently libxlutil lives in the same directory as libxenlight XEN_libxlutil = $(XEN_libxenlight) -XEN_libxenstore = $(XEN_ROOT)/tools/xenstore XEN_libxenstat = $(XEN_ROOT)/tools/xenstat/libxenstat/src XEN_libxenvchan = $(XEN_ROOT)/tools/libvchan @@ -106,10 +105,6 @@ $(foreach lib,$(LIBS_LIBS),$(eval $(call LIB_defs,$(lib)))) CFLAGS_libxenctrl += $(CFLAGS_libxentoollog) $(CFLAGS_libxenforeignmemory) $(CFLAGS_libxendevicemodel) -D__XEN_TOOLS__ CFLAGS_libxenguest += $(CFLAGS_libxenevtchn) $(CFLAGS_libxenforeignmemory) -CFLAGS_libxenstore = -I$(XEN_libxenstore)/include $(CFLAGS_xeninclude) -SHDEPS_libxenstore = $(SHLIB_libxentoolcore) $(SHLIB_libxenctrl) -LDLIBS_libxenstore = $(SHDEPS_libxenstore) $(XEN_libxenstore)/libxenstore$(libextension) -SHLIB_libxenstore = $(SHDEPS_libxenstore) -Wl,-rpath-link=$(XEN_libxenstore) ifeq ($(CONFIG_Linux),y) LDLIBS_libxenstore += -ldl endif diff --git a/tools/libs/Makefile b/tools/libs/Makefile index f15c1688f7..62bd8f5292 100644 --- a/tools/libs/Makefile +++ b/tools/libs/Makefile @@ -12,6 +12,7 @@ SUBDIRS-y += devicemodel SUBDIRS-y += ctrl SUBDIRS-y += guest SUBDIRS-y += hypfs +SUBDIRS-y += store ifeq ($(CONFIG_RUMP),y) SUBDIRS-y := toolcore diff --git a/tools/libs/store/Makefile b/tools/libs/store/Makefile new file mode 100644 index 0000000000..4da502646e --- /dev/null +++ b/tools/libs/store/Makefile @@ -0,0 +1,64 @@ +XEN_ROOT=$(CURDIR)/../../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 3.0 +MINOR = 3 + +ifeq ($(CONFIG_Linux),y) +APPEND_LDFLAGS += -ldl +endif + +SRCS-y += xs_lib.c +SRCS-y += xs.c + +LIBHEADER = xenstore.h xenstore_lib.h + +include ../libs.mk + +# Include configure output (config.h) +CFLAGS += -include $(XEN_ROOT)/tools/config.h +CFLAGS += $(CFLAGS_libxentoolcore) +CFLAGS += -DXEN_LIB_STORED="\"$(XEN_LIB_STORED)\"" +CFLAGS += -DXEN_RUN_STORED="\"$(XEN_RUN_STORED)\"" + +LINK_FILES = xs_lib.c include/xenstore_lib.h list.h utils.h + +$(LIB_OBJS): $(LINK_FILES) + +$(LINK_FILES): + ln -sf $(XEN_ROOT)/tools/xenstore/$(notdir $@) $@ + +xs.opic: CFLAGS += -DUSE_PTHREAD +ifeq ($(CONFIG_Linux),y) +xs.opic: CFLAGS += -DUSE_DLSYM +else +PKG_CONFIG_REMOVE += -ldl +endif + +$(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_libxenstore)/include +$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude) + +.PHONY: install +install: install-headers + +.PHONY: install-headers +install-headers: + $(INSTALL_DIR) $(DESTDIR)$(includedir) + $(INSTALL_DIR) $(DESTDIR)$(includedir)/xenstore-compat + $(INSTALL_DATA) include/compat/xs.h $(DESTDIR)$(includedir)/xenstore-compat/xs.h + $(INSTALL_DATA) include/compat/xs_lib.h $(DESTDIR)$(includedir)/xenstore-compat/xs_lib.h + ln -sf xenstore-compat/xs.h $(DESTDIR)$(includedir)/xs.h + ln -sf xenstore-compat/xs_lib.h $(DESTDIR)$(includedir)/xs_lib.h + +.PHONY: uninstall +uninstall: uninstall-headers + +.PHONY: uninstall-headers +uninstall-headers: + rm -f $(DESTDIR)$(includedir)/xs_lib.h + rm -f $(DESTDIR)$(includedir)/xs.h + rm -f $(DESTDIR)$(includedir)/xenstore-compat/xs_lib.h + rm -f $(DESTDIR)$(includedir)/xenstore-compat/xs.h + if [ -d $(DESTDIR)$(includedir)/xenstore-compat ]; then \ + rmdir --ignore-fail-on-non-empty $(DESTDIR)$(includedir)/xenstore-compat; \ + fi diff --git a/tools/libs/store/include/compat/xs.h b/tools/libs/store/include/compat/xs.h new file mode 100644 index 0000000000..99cf39bb1a --- /dev/null +++ b/tools/libs/store/include/compat/xs.h @@ -0,0 +1,2 @@ +#warning xs.h is deprecated use xenstore.h instead +#include <xenstore.h> diff --git a/tools/libs/store/include/compat/xs_lib.h b/tools/libs/store/include/compat/xs_lib.h new file mode 100644 index 0000000000..ad81b54c0e --- /dev/null +++ b/tools/libs/store/include/compat/xs_lib.h @@ -0,0 +1,2 @@ +#warning xs_lib.h is deprecated use xenstore_lib.h instead +#include <xenstore_lib.h> diff --git a/tools/libs/store/include/xenstore.h b/tools/libs/store/include/xenstore.h new file mode 100644 index 0000000000..25b31881c8 --- /dev/null +++ b/tools/libs/store/include/xenstore.h @@ -0,0 +1,290 @@ +/* + Xen Store Daemon providing simple tree-like database. + Copyright (C) 2005 Rusty Russell IBM Corporation + + 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef XENSTORE_H +#define XENSTORE_H + +#include <xenstore_lib.h> + +#define XBT_NULL 0 + +#define XS_OPEN_READONLY (1UL<<0) +#define XS_OPEN_SOCKETONLY (1UL<<1) + +/* + * Setting XS_UNWATCH_FILTER arranges that after xs_unwatch, no + * related watch events will be delivered via xs_read_watch. But + * this relies on the couple token, subpath is unique. + * + * XS_UNWATCH_FILTER clear XS_UNWATCH_FILTER set + * + * Even after xs_unwatch, "stale" After xs_unwatch returns, no + * instances of the watch event watch events with the same + * may be delivered. token and with the same subpath + * will be delivered. + * + * A path and a subpath can be The application must avoid + * register with the same token. registering a path (/foo/) and + * a subpath (/foo/bar) with the + * same path until a successful + * xs_unwatch for the first watch + * has returned. + */ +#define XS_UNWATCH_FILTER (1UL<<2) + +struct xs_handle; +typedef uint32_t xs_transaction_t; + +/* IMPORTANT: For details on xenstore protocol limits, see + * docs/misc/xenstore.txt in the Xen public source repository, and use the + * XENSTORE_*_MAX limit macros defined in xen/io/xs_wire.h. + */ + +/* On failure, these routines set errno. */ + +/* Open a connection to the xs daemon. + * Attempts to make a connection over the socket interface, + * and if it fails, then over the xenbus interface. + * Mode 0 specifies read-write access, XS_OPEN_READONLY for + * read-only access. + * + * * Connections made with xs_open(0) (which might be shared page or + * socket based) are only guaranteed to work in the parent after + * fork. + * * Connections made with xs_open(XS_OPEN_SOCKETONLY) will be usable + * in either the parent or the child after fork, but not both. + * * xs_daemon_open*() and xs_domain_open() are deprecated synonyms + * for xs_open(0). + * * XS_OPEN_READONLY has no bearing on any of this. + * + * Returns a handle or NULL. + */ +struct xs_handle *xs_open(unsigned long flags); + +/* Close the connection to the xs daemon. */ +void xs_close(struct xs_handle *xsh /* NULL ok */); + +/* Connect to the xs daemon. + * Returns a handle or NULL. + * Deprecated, please use xs_open(0) instead + */ +struct xs_handle *xs_daemon_open(void); +struct xs_handle *xs_domain_open(void); + +/* Connect to the xs daemon (readonly for non-root clients). + * Returns a handle or NULL. + * Deprecated, please use xs_open(XS_OPEN_READONLY) instead + */ +struct xs_handle *xs_daemon_open_readonly(void); + +/* Close the connection to the xs daemon. + * Deprecated, please use xs_close() instead + */ +void xs_daemon_close(struct xs_handle *); + +/* Throw away the connection to the xs daemon, for use after fork(). */ +void xs_daemon_destroy_postfork(struct xs_handle *); + +/* Get contents of a directory. + * Returns a malloced array: call free() on it after use. + * Num indicates size. + * Returns NULL on failure. + */ +char **xs_directory(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *num); + +/* Get the value of a single file, nul terminated. + * Returns a malloced value: call free() on it after use. + * len indicates length in bytes, not including terminator. + * Returns NULL on failure. + */ +void *xs_read(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *len); + +/* Write the value of a single file. + * Returns false on failure. + */ +bool xs_write(struct xs_handle *h, xs_transaction_t t, + const char *path, const void *data, unsigned int len); + +/* Create a new directory. + * Returns false on failure, or success if it already exists. + */ +bool xs_mkdir(struct xs_handle *h, xs_transaction_t t, + const char *path); + +/* Destroy a file or directory (and children). + * Returns false on failure, or if it doesn't exist. + */ +bool xs_rm(struct xs_handle *h, xs_transaction_t t, + const char *path); + +/* Fake function which will always return false (required to let + * libxenstore remain at 3.0 version. + */ +bool xs_restrict(struct xs_handle *h, unsigned domid); + +/* Get permissions of node (first element is owner, first perms is "other"). + * Returns malloced array, or NULL: call free() after use. + */ +struct xs_permissions *xs_get_permissions(struct xs_handle *h, + xs_transaction_t t, + const char *path, unsigned int *num); + +/* Set permissions of node (must be owner). Returns false on failure. + * + * Domain 0 may read / write anywhere in the store, regardless of + * permission settings. + * + * Note: + * The perms array is a list of (domid, permissions) pairs. The first + * element in the list specifies the owner of the list, plus the flags + * for every domain not explicitly specified subsequently. The + * subsequent entries are normal capabilities. + * + * Example C code: + * + * struct xs_permissions perms[2]; + * + * perms[0].id = dm_domid; + * perms[0].perms = XS_PERM_NONE; + * perms[1].id = guest_domid; + * perms[1].perms = XS_PERM_READ; + * + * It means the owner of the path is domain $dm_domid (hence it always + * has read and write permission), all other domains (unless specified + * in subsequent pair) can neither read from nor write to that + * path. It then specifies domain $guest_domid can read from that + * path. + */ +bool xs_set_permissions(struct xs_handle *h, xs_transaction_t t, + const char *path, struct xs_permissions *perms, + unsigned int num_perms); + +/* Watch a node for changes (poll on fd to detect, or call read_watch()). + * When the node (or any child) changes, fd will become readable. + * Token is returned when watch is read, to allow matching. + * Returns false on failure. + */ +bool xs_watch(struct xs_handle *h, const char *path, const char *token); + +/* Return the FD to poll on to see if a watch has fired. */ +int xs_fileno(struct xs_handle *h); + +/* Check for node changes. On success, returns a non-NULL pointer ret + * such that ret[0] and ret[1] are valid C strings, namely the + * triggering path (see docs/misc/xenstore.txt) and the token (from + * xs_watch). On error return value is NULL setting errno. + * + * Callers should, after xs_fileno has become readable, repeatedly + * call xs_check_watch until it returns NULL and sets errno to EAGAIN. + * (If the fd became readable, xs_check_watch is allowed to make it no + * longer show up as readable even if future calls to xs_check_watch + * will return more watch events.) + * + * After the caller is finished with the returned information it + * should be freed all in one go with free(ret). + */ +char **xs_check_watch(struct xs_handle *h); + +/* Find out what node change was on (will block if nothing pending). + * Returns array containing the path and token, or NULL. + * Use XS_WATCH_* to access these elements. + * Call free() after use. + */ +char **xs_read_watch(struct xs_handle *h, unsigned int *num); + +/* Remove a watch on a node: implicitly acks any outstanding watch. + * Returns false on failure (no watch on that node). + */ +bool xs_unwatch(struct xs_handle *h, const char *path, const char *token); + +/* Start a transaction: changes by others will not be seen during this + * transaction, and changes will not be visible to others until end. + * Returns NULL on failure. + */ +xs_transaction_t xs_transaction_start(struct xs_handle *h); + +/* End a transaction. + * If abandon is true, transaction is discarded instead of committed. + * Returns false on failure: if errno == EAGAIN, you have to restart + * transaction. + */ +bool xs_transaction_end(struct xs_handle *h, xs_transaction_t t, + bool abort); + +/* Introduce a new domain. + * This tells the store daemon about a shared memory page, event channel and + * store path associated with a domain: the domain uses these to communicate. + */ +bool xs_introduce_domain(struct xs_handle *h, + unsigned int domid, + unsigned long mfn, + unsigned int eventchn); + +/* Set the target of a domain + * This tells the store daemon that a domain is targetting another one, so + * it should let it tinker with it. + */ +bool xs_set_target(struct xs_handle *h, + unsigned int domid, + unsigned int target); + +/* Resume a domain. + * Clear the shutdown flag for this domain in the store. + */ +bool xs_resume_domain(struct xs_handle *h, unsigned int domid); + +/* Release a domain. + * Tells the store domain to release the memory page to the domain. + */ +bool xs_release_domain(struct xs_handle *h, unsigned int domid); + +/* Query the home path of a domain. Call free() after use. + */ +char *xs_get_domain_path(struct xs_handle *h, unsigned int domid); + +/* Returns true if child is either equal to parent, or a node underneath + * parent; or false otherwise. Done by string comparison, so relative and + * absolute pathnames never in a parent/child relationship by this + * definition. Cannot fail. + */ +bool xs_path_is_subpath(const char *parent, const char *child); + +/* Return whether the domain specified has been introduced to xenstored. + */ +bool xs_is_domain_introduced(struct xs_handle *h, unsigned int domid); + +char *xs_control_command(struct xs_handle *h, const char *cmd, + void *data, unsigned int len); +/* Deprecated: use xs_control_command() instead. */ +char *xs_debug_command(struct xs_handle *h, const char *cmd, + void *data, unsigned int len); + +int xs_suspend_evtchn_port(int domid); +#endif /* XENSTORE_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "linux" + * indent-tabs-mode: t + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/tools/libs/store/libxenstore.map b/tools/libs/store/libxenstore.map new file mode 100644 index 0000000000..9854305a2c --- /dev/null +++ b/tools/libs/store/libxenstore.map @@ -0,0 +1,49 @@ +VERS_3.0.3 { + global: + xs_open; + xs_close; + xs_daemon_open; + xs_domain_open; + xs_daemon_open_readonly; + xs_daemon_close; + xs_daemon_destroy_postfork; + xs_directory; + xs_read; + xs_write; + xs_mkdir; + xs_rm; + xs_restrict; + xs_get_permissions; + xs_set_permissions; + xs_watch; + xs_fileno; + xs_check_watch; + xs_read_watch; + xs_unwatch; + xs_transaction_start; + xs_transaction_end; + xs_introduce_domain; + xs_set_target; + xs_resume_domain; + xs_release_domain; + xs_get_domain_path; + xs_path_is_subpath; + xs_is_domain_introduced; + xs_control_command; + xs_debug_command; + xs_suspend_evtchn_port; + xs_daemon_rootdir; + xs_daemon_rundir; + xs_daemon_socket; + xs_daemon_socket_ro; + xs_domain_dev; + xs_daemon_tdb; + xs_write_all; + xs_strings_to_perms; + xs_perm_to_string; + xs_count_strings; + expanding_buffer_ensure; + sanitise_value; + unsanitise_value; + local: *; /* Do not expose anything by default */ +}; diff --git a/tools/libs/store/xs.c b/tools/libs/store/xs.c new file mode 100644 index 0000000000..aa1d24b8b9 --- /dev/null +++ b/tools/libs/store/xs.c @@ -0,0 +1,1473 @@ +/* + Xen Store Daemon interface providing simple tree-like database. + Copyright (C) 2005 Rusty Russell IBM Corporation + + 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, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <string.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <signal.h> +#include <stdint.h> +#include <errno.h> +#include "xenstore.h" +#include "list.h" +#include "utils.h" + +#include <xentoolcore_internal.h> + +struct xs_stored_msg { + struct list_head list; + struct xsd_sockmsg hdr; + char *body; +}; + +#ifdef USE_PTHREAD + +#include <pthread.h> + +#ifdef USE_DLSYM +#include <dlfcn.h> +#endif + +struct xs_handle { + /* Communications channel to xenstore daemon. */ + int fd; + Xentoolcore__Active_Handle tc_ah; /* for restrict */ + + /* + * A read thread which pulls messages off the comms channel and + * signals waiters. + */ + pthread_t read_thr; + int read_thr_exists; + + /* + * A list of fired watch messages, protected by a mutex. Users can + * wait on the conditional variable until a watch is pending. + */ + struct list_head watch_list; + pthread_mutex_t watch_mutex; + pthread_cond_t watch_condvar; + + /* Clients can select() on this pipe to wait for a watch to fire. */ + int watch_pipe[2]; + /* Filtering watch event in unwatch function? */ + bool unwatch_filter; + + /* + * A list of replies. Currently only one will ever be outstanding + * because we serialise requests. The requester can wait on the + * conditional variable for its response. + */ + struct list_head reply_list; + pthread_mutex_t reply_mutex; + pthread_cond_t reply_condvar; + + /* One request at a time. */ + pthread_mutex_t request_mutex; + + /* Lock discipline: + * Only holder of the request lock may write to h->fd. + * Only holder of the request lock may access read_thr_exists. + * If read_thr_exists==0, only holder of request lock may read h->fd; + * If read_thr_exists==1, only the read thread may read h->fd. + * Only holder of the reply lock may access reply_list. + * Only holder of the watch lock may access watch_list. + * Lock hierarchy: + * The order in which to acquire locks is + * request_mutex + * reply_mutex + * watch_mutex + */ +}; + +#define mutex_lock(m) pthread_mutex_lock(m) +#define mutex_unlock(m) pthread_mutex_unlock(m) +#define condvar_signal(c) pthread_cond_signal(c) +#define condvar_wait(c,m) pthread_cond_wait(c,m) +#define cleanup_push(f, a) \ + pthread_cleanup_push((void (*)(void *))(f), (void *)(a)) +/* + * Some definitions of pthread_cleanup_pop() are a macro starting with an + * end-brace. GCC then complains if we immediately precede that with a label. + * Hence we insert a dummy statement to appease the compiler in this situation. + */ +#define cleanup_pop(run) ((void)0); pthread_cleanup_pop(run) + +#define read_thread_exists(h) (h->read_thr_exists) + +/* Because pthread_cleanup_p* are not available when USE_PTHREAD is + * disabled, use these macros which convert appropriately. */ +#define cleanup_push_heap(p) cleanup_push(free, p) +#define cleanup_pop_heap(run, p) cleanup_pop((run)) + +static void *read_thread(void *arg); + +#else /* !defined(USE_PTHREAD) */ + +struct xs_handle { + int fd; + Xentoolcore__Active_Handle tc_ah; /* for restrict */ + struct list_head reply_list; + struct list_head watch_list; + /* Clients can select() on this pipe to wait for a watch to fire. */ + int watch_pipe[2]; + /* Filtering watch event in unwatch function? */ + bool unwatch_filter; +}; + +#define mutex_lock(m) ((void)0) +#define mutex_unlock(m) ((void)0) +#define condvar_signal(c) ((void)0) +#define condvar_wait(c,m) ((void)0) +#define cleanup_push(f, a) ((void)0) +#define cleanup_pop(run) ((void)0) +#define read_thread_exists(h) (0) + +#define cleanup_push_heap(p) ((void)0) +#define cleanup_pop_heap(run, p) do { if ((run)) free(p); } while(0) + +#endif + +static int read_message(struct xs_handle *h, int nonblocking); + +static bool setnonblock(int fd, int nonblock) { + int flags = fcntl(fd, F_GETFL); + if (flags == -1) + return false; + + if (nonblock) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) == -1) + return false; + + return true; +} + +int xs_fileno(struct xs_handle *h) +{ + char c = 0; + + mutex_lock(&h->watch_mutex); + + if ((h->watch_pipe[0] == -1) && (pipe(h->watch_pipe) != -1)) { + /* Kick things off if the watch list is already non-empty. */ + if (!list_empty(&h->watch_list)) + while (write(h->watch_pipe[1], &c, 1) != 1) + continue; + } + + mutex_unlock(&h->watch_mutex); + + return h->watch_pipe[0]; +} + +static int get_socket(const char *connect_to) +{ + struct sockaddr_un addr; + int sock, saved_errno, flags; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + if ((flags = fcntl(sock, F_GETFD)) < 0) + goto error; + flags |= FD_CLOEXEC; + if (fcntl(sock, F_SETFD, flags) < 0) + goto error; + + addr.sun_family = AF_UNIX; + if(strlen(connect_to) >= sizeof(addr.sun_path)) { + errno = EINVAL; + goto error; + } + strcpy(addr.sun_path, connect_to); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) + goto error; + + return sock; + +error: + saved_errno = errno; + close(sock); + errno = saved_errno; + return -1; +} + +static int get_dev(const char *connect_to) +{ + /* We cannot open read-only because requests are writes */ + return open(connect_to, O_RDWR); +} + +static int all_restrict_cb(Xentoolcore__Active_Handle *ah, domid_t domid) { + struct xs_handle *h = CONTAINER_OF(ah, *h, tc_ah); + return xentoolcore__restrict_by_dup2_null(h->fd); +} + +static struct xs_handle *get_handle(const char *connect_to) +{ + struct stat buf; + struct xs_handle *h = NULL; + int saved_errno; + + h = malloc(sizeof(*h)); + if (h == NULL) + goto err; + + memset(h, 0, sizeof(*h)); + h->fd = -1; + + h->tc_ah.restrict_callback = all_restrict_cb; + xentoolcore__register_active_handle(&h->tc_ah); + + if (stat(connect_to, &buf) != 0) + goto err; + + if (S_ISSOCK(buf.st_mode)) + h->fd = get_socket(connect_to); + else + h->fd = get_dev(connect_to); + + if (h->fd == -1) + goto err; + + INIT_LIST_HEAD(&h->reply_list); + INIT_LIST_HEAD(&h->watch_list); + + /* Watch pipe is allocated on demand in xs_fileno(). */ + h->watch_pipe[0] = h->watch_pipe[1] = -1; + + h->unwatch_filter = false; + +#ifdef USE_PTHREAD + pthread_mutex_init(&h->watch_mutex, NULL); + pthread_cond_init(&h->watch_condvar, NULL); + + pthread_mutex_init(&h->reply_mutex, NULL); + pthread_cond_init(&h->reply_condvar, NULL); + + pthread_mutex_init(&h->request_mutex, NULL); +#endif + + return h; + +err: + saved_errno = errno; + + if (h) { + xentoolcore__deregister_active_handle(&h->tc_ah); + if (h->fd >= 0) + close(h->fd); + } + free(h); + + errno = saved_errno; + return NULL; +} + +struct xs_handle *xs_daemon_open(void) +{ + return xs_open(0); +} + +struct xs_handle *xs_daemon_open_readonly(void) +{ + return xs_open(XS_OPEN_READONLY); +} + +struct xs_handle *xs_domain_open(void) +{ + return xs_open(0); +} + +struct xs_handle *xs_open(unsigned long flags) +{ + struct xs_handle *xsh = NULL; + + if (flags & XS_OPEN_READONLY) + xsh = get_handle(xs_daemon_socket_ro()); + else + xsh = get_handle(xs_daemon_socket()); + + if (!xsh && !(flags & XS_OPEN_SOCKETONLY)) + xsh = get_handle(xs_domain_dev()); + + if (xsh && (flags & XS_UNWATCH_FILTER)) + xsh->unwatch_filter = true; + + return xsh; +} + +static void close_free_msgs(struct xs_handle *h) { + struct xs_stored_msg *msg, *tmsg; + + list_for_each_entry_safe(msg, tmsg, &h->reply_list, list) { + free(msg->body); + free(msg); + } + + list_for_each_entry_safe(msg, tmsg, &h->watch_list, list) { + free(msg->body); + free(msg); + } +} + +static void close_fds_free(struct xs_handle *h) { + if (h->watch_pipe[0] != -1) { + close(h->watch_pipe[0]); + close(h->watch_pipe[1]); + } + + xentoolcore__deregister_active_handle(&h->tc_ah); + close(h->fd); + + free(h); +} + +void xs_daemon_destroy_postfork(struct xs_handle *h) +{ + close_free_msgs(h); + close_fds_free(h); +} + +void xs_daemon_close(struct xs_handle *h) +{ +#ifdef USE_PTHREAD + if (h->read_thr_exists) { + pthread_cancel(h->read_thr); + pthread_join(h->read_thr, NULL); + } +#endif + + mutex_lock(&h->request_mutex); + mutex_lock(&h->reply_mutex); + mutex_lock(&h->watch_mutex); + + close_free_msgs(h); + + mutex_unlock(&h->request_mutex); + mutex_unlock(&h->reply_mutex); + mutex_unlock(&h->watch_mutex); + + close_fds_free(h); +} + +void xs_close(struct xs_handle* xsh) +{ + if (xsh) + xs_daemon_close(xsh); +} + +static bool read_all(int fd, void *data, unsigned int len, int nonblocking) + /* With nonblocking, either reads either everything requested, + * or nothing. */ +{ + if (!len) + return true; + + if (nonblocking && !setnonblock(fd, 1)) + return false; + + while (len) { + int done; + + done = read(fd, data, len); + if (done < 0) { + if (errno == EINTR) + continue; + goto out_false; + } + if (done == 0) { + /* It closed fd on us? EBADF is appropriate. */ + errno = EBADF; + goto out_false; + } + data += done; + len -= done; + + if (nonblocking) { + nonblocking = 0; + if (!setnonblock(fd, 0)) + goto out_false; + } + } + + return true; + +out_false: + if (nonblocking) + setnonblock(fd, 0); + return false; +} + +#ifdef XSTEST +#define read_all read_all_choice +#define xs_write_all write_all_choice +#endif + +static int get_error(const char *errorstring) +{ + unsigned int i; + + for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) + if (i == ARRAY_SIZE(xsd_errors) - 1) + return EINVAL; + return xsd_errors[i].errnum; +} + +/* Adds extra nul terminator, because we generally (always?) hold strings. */ +static void *read_reply( + struct xs_handle *h, enum xsd_sockmsg_type *type, unsigned int *len) +{ + struct xs_stored_msg *msg; + char *body; + int read_from_thread; + + read_from_thread = read_thread_exists(h); + + /* Read from comms channel ourselves if there is no reader thread. */ + if (!read_from_thread && (read_message(h, 0) == -1)) + return NULL; + + mutex_lock(&h->reply_mutex); +#ifdef USE_PTHREAD + while (list_empty(&h->reply_list) && read_from_thread && h->fd != -1) + condvar_wait(&h->reply_condvar, &h->reply_mutex); +#endif + if (list_empty(&h->reply_list)) { + mutex_unlock(&h->reply_mutex); + errno = EINVAL; + return NULL; + } + msg = list_top(&h->reply_list, struct xs_stored_msg, list); + list_del(&msg->list); + assert(list_empty(&h->reply_list)); + mutex_unlock(&h->reply_mutex); + + *type = msg->hdr.type; + if (len) + *len = msg->hdr.len; + body = msg->body; + + free(msg); + + return body; +} + +/* Send message to xs, get malloc'ed reply. NULL and set errno on error. */ +static void *xs_talkv(struct xs_handle *h, xs_transaction_t t, + enum xsd_sockmsg_type type, + const struct iovec *iovec, + unsigned int num_vecs, + unsigned int *len) +{ + struct xsd_sockmsg msg; + void *ret = NULL; + int saved_errno; + unsigned int i; + struct sigaction ignorepipe, oldact; + + msg.tx_id = t; + msg.req_id = 0; + msg.type = type; + msg.len = 0; + for (i = 0; i < num_vecs; i++) + msg.len += iovec[i].iov_len; + + if (msg.len > XENSTORE_PAYLOAD_MAX) { + errno = E2BIG; + return 0; + } + + ignorepipe.sa_handler = SIG_IGN; + sigemptyset(&ignorepipe.sa_mask); + ignorepipe.sa_flags = 0; + sigaction(SIGPIPE, &ignorepipe, &oldact); + + mutex_lock(&h->request_mutex); + + if (!xs_write_all(h->fd, &msg, sizeof(msg))) + goto fail; + + for (i = 0; i < num_vecs; i++) + if (!xs_write_all(h->fd, iovec[i].iov_base, iovec[i].iov_len)) + goto fail; + + ret = read_reply(h, &msg.type, len); + if (!ret) + goto fail; + + mutex_unlock(&h->request_mutex); + + sigaction(SIGPIPE, &oldact, NULL); + if (msg.type == XS_ERROR) { + saved_errno = get_error(ret); + free(ret); + errno = saved_errno; + return NULL; + } + + if (msg.type != type) { + free(ret); + saved_errno = EBADF; + goto close_fd; + } + return ret; + +fail: + /* We're in a bad state, so close fd. */ + saved_errno = errno; + mutex_unlock(&h->request_mutex); + sigaction(SIGPIPE, &oldact, NULL); +close_fd: + close(h->fd); + h->fd = -1; + errno = saved_errno; + return NULL; +} + +/* free(), but don't change errno. */ +static void free_no_errno(void *p) +{ + int saved_errno = errno; + free(p); + errno = saved_errno; +} + +/* Simplified version of xs_talkv: single message. */ +static void *xs_single(struct xs_handle *h, xs_transaction_t t, + enum xsd_sockmsg_type type, + const char *string, + unsigned int *len) +{ + struct iovec iovec; + + iovec.iov_base = (void *)string; + iovec.iov_len = strlen(string) + 1; + return xs_talkv(h, t, type, &iovec, 1, len); +} + +static bool xs_bool(char *reply) +{ + if (!reply) + return false; + free(reply); + return true; +} + +static char **xs_directory_common(char *strings, unsigned int len, + unsigned int *num) +{ + char *p, **ret; + + /* Count the strings. */ + *num = xs_count_strings(strings, len); + + /* Transfer to one big alloc for easy freeing. */ + ret = malloc(*num * sizeof(char *) + len); + if (!ret) { + free_no_errno(strings); + return NULL; + } + memcpy(&ret[*num], strings, len); + free_no_errno(strings); + + strings = (char *)&ret[*num]; + for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1) + ret[(*num)++] = p; + return ret; +} + +static char **xs_directory_part(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *num) +{ + unsigned int off, result_len; + char gen[24], offstr[8]; + struct iovec iovec[2]; + char *result = NULL, *strings = NULL; + + memset(gen, 0, sizeof(gen)); + iovec[0].iov_base = (void *)path; + iovec[0].iov_len = strlen(path) + 1; + + for (off = 0;;) { + snprintf(offstr, sizeof(offstr), "%u", off); + iovec[1].iov_base = (void *)offstr; + iovec[1].iov_len = strlen(offstr) + 1; + result = xs_talkv(h, t, XS_DIRECTORY_PART, iovec, 2, + &result_len); + + /* If XS_DIRECTORY_PART isn't supported return E2BIG. */ + if (!result) { + if (errno == ENOSYS) + errno = E2BIG; + return NULL; + } + + if (off) { + if (strcmp(gen, result)) { + free(result); + free(strings); + strings = NULL; + off = 0; + continue; + } + } else + strncpy(gen, result, sizeof(gen) - 1); + + result_len -= strlen(result) + 1; + strings = realloc(strings, off + result_len); + memcpy(strings + off, result + strlen(result) + 1, result_len); + free(result); + off += result_len; + + if (off <= 1 || strings[off - 2] == 0) + break; + } + + if (off > 1) + off--; + + return xs_directory_common(strings, off, num); +} + +char **xs_directory(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *num) +{ + char *strings; + unsigned int len; + + strings = xs_single(h, t, XS_DIRECTORY, path, &len); + if (!strings) { + if (errno != E2BIG) + return NULL; + return xs_directory_part(h, t, path, num); + } + + return xs_directory_common(strings, len, num); +} + +/* Get the value of a single file, nul terminated. + * Returns a malloced value: call free() on it after use. + * len indicates length in bytes, not including the nul. + * Returns NULL on failure. + */ +void *xs_read(struct xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *len) +{ + return xs_single(h, t, XS_READ, path, len); +} + +/* Write the value of a single file. + * Returns false on failure. + */ +bool xs_write(struct xs_handle *h, xs_transaction_t t, + const char *path, const void *data, unsigned int len) +{ + struct iovec iovec[2]; + + iovec[0].iov_base = (void *)path; + iovec[0].iov_len = strlen(path) + 1; + iovec[1].iov_base = (void *)data; + iovec[1].iov_len = len; + + return xs_bool(xs_talkv(h, t, XS_WRITE, iovec, + ARRAY_SIZE(iovec), NULL)); +} + +/* Create a new directory. + * Returns false on failure, or success if it already exists. + */ +bool xs_mkdir(struct xs_handle *h, xs_transaction_t t, + const char *path) +{ + return xs_bool(xs_single(h, t, XS_MKDIR, path, NULL)); +} + +/* Destroy a file or directory (directories must be empty). + * Returns false on failure, or success if it doesn't exist. + */ +bool xs_rm(struct xs_handle *h, xs_transaction_t t, + const char *path) +{ + return xs_bool(xs_single(h, t, XS_RM, path, NULL)); +} + +/* Get permissions of node (first element is owner). + * Returns malloced array, or NULL: call free() after use. + */ +struct xs_permissions *xs_get_permissions(struct xs_handle *h, + xs_transaction_t t, + const char *path, unsigned int *num) +{ + char *strings; + unsigned int len; + struct xs_permissions *ret; + + strings = xs_single(h, t, XS_GET_PERMS, path, &len); + if (!strings) + return NULL; + + /* Count the strings: each one perms then domid. */ + *num = xs_count_strings(strings, len); + + /* Transfer to one big alloc for easy freeing. */ + ret = malloc(*num * sizeof(struct xs_permissions)); + if (!ret) { + free_no_errno(strings); + return NULL; + } + + if (!xs_strings_to_perms(ret, *num, strings)) { + free_no_errno(ret); + ret = NULL; + } + + free(strings); + return ret; +} + +/* Set permissions of node (must be owner). + * Returns false on failure. + */ +bool xs_set_permissions(struct xs_handle *h, + xs_transaction_t t, + const char *path, + struct xs_permissions *perms, + unsigned int num_perms) +{ + unsigned int i; + struct iovec iov[1+num_perms]; + + iov[0].iov_base = (void *)path; + iov[0].iov_len = strlen(path) + 1; + + for (i = 0; i < num_perms; i++) { + char buffer[MAX_STRLEN(unsigned int)+1]; + + if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) + goto unwind; + + iov[i+1].iov_base = strdup(buffer); + iov[i+1].iov_len = strlen(buffer) + 1; + if (!iov[i+1].iov_base) + goto unwind; + } + + if (!xs_bool(xs_talkv(h, t, XS_SET_PERMS, iov, 1+num_perms, NULL))) + goto unwind; + for (i = 0; i < num_perms; i++) + free(iov[i+1].iov_base); + return true; + +unwind: + num_perms = i; + for (i = 0; i < num_perms; i++) + free_no_errno(iov[i+1].iov_base); + return false; +} + +/* Always return false a functionality has been removed in Xen 4.9 */ +bool xs_restrict(struct xs_handle *h, unsigned domid) +{ + return false; +} + +/* Watch a node for changes (poll on fd to detect, or call read_watch()). + * When the node (or any child) changes, fd will become readable. + * Token is returned when watch is read, to allow matching. + * Returns false on failure. + */ +bool xs_watch(struct xs_handle *h, const char *path, const char *token) +{ + struct iovec iov[2]; + +#ifdef USE_PTHREAD +#define DEFAULT_THREAD_STACKSIZE (16 * 1024) +#define READ_THREAD_STACKSIZE \ + ((DEFAULT_THREAD_STACKSIZE < PTHREAD_STACK_MIN) ? \ + PTHREAD_STACK_MIN : DEFAULT_THREAD_STACKSIZE) + + /* We dynamically create a reader thread on demand. */ + mutex_lock(&h->request_mutex); + if (!h->read_thr_exists) { + sigset_t set, old_set; + pthread_attr_t attr; + static size_t stack_size; +#ifdef USE_DLSYM + size_t (*getsz)(pthread_attr_t *attr); +#endif + + if (pthread_attr_init(&attr) != 0) { + mutex_unlock(&h->request_mutex); + return false; + } + if (!stack_size) { +#ifdef USE_DLSYM + getsz = dlsym(RTLD_DEFAULT, "__pthread_get_minstack"); + if (getsz) + stack_size = getsz(&attr); +#endif + if (stack_size < READ_THREAD_STACKSIZE) + stack_size = READ_THREAD_STACKSIZE; + } + if (pthread_attr_setstacksize(&attr, stack_size) != 0) { + pthread_attr_destroy(&attr); + mutex_unlock(&h->request_mutex); + return false; + } + + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, &old_set); + + if (pthread_create(&h->read_thr, &attr, read_thread, h) != 0) { + pthread_sigmask(SIG_SETMASK, &old_set, NULL); + pthread_attr_destroy(&attr); + mutex_unlock(&h->request_mutex); + return false; + } + h->read_thr_exists = 1; + pthread_sigmask(SIG_SETMASK, &old_set, NULL); + pthread_attr_destroy(&attr); + } + mutex_unlock(&h->request_mutex); +#endif + + iov[0].iov_base = (void *)path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = (void *)token; + iov[1].iov_len = strlen(token) + 1; + + return xs_bool(xs_talkv(h, XBT_NULL, XS_WATCH, iov, + ARRAY_SIZE(iov), NULL)); +} + + +/* Clear the pipe token if there are no more pending watchs. + * We suppose the watch_mutex is already taken. + */ +static void xs_maybe_clear_watch_pipe(struct xs_handle *h) +{ + char c; + + if (list_empty(&h->watch_list) && (h->watch_pipe[0] != -1)) + while (read(h->watch_pipe[0], &c, 1) != 1) + continue; +} + +/* Find out what node change was on (will block if nothing pending). + * Returns array of two pointers: path and token, or NULL. + * Call free() after use. + */ +static char **read_watch_internal(struct xs_handle *h, unsigned int *num, + int nonblocking) +{ + struct xs_stored_msg *msg; + char **ret, *strings; + unsigned int num_strings, i; + + mutex_lock(&h->watch_mutex); + +#ifdef USE_PTHREAD + /* Wait on the condition variable for a watch to fire. + * If the reader thread doesn't exist yet, then that's because + * we haven't called xs_watch. Presumably the application + * will do so later; in the meantime we just block. + */ + while (list_empty(&h->watch_list) && h->fd != -1) { + if (nonblocking) { + mutex_unlock(&h->watch_mutex); + errno = EAGAIN; + return 0; + } + condvar_wait(&h->watch_condvar, &h->watch_mutex); + } +#else /* !defined(USE_PTHREAD) */ + /* Read from comms channel ourselves if there are no threads + * and therefore no reader thread. */ + + assert(!read_thread_exists(h)); /* not threadsafe but worth a check */ + if ((read_message(h, nonblocking) == -1)) + return NULL; + +#endif /* !defined(USE_PTHREAD) */ + + if (list_empty(&h->watch_list)) { + mutex_unlock(&h->watch_mutex); + errno = EINVAL; + return NULL; + } + msg = list_top(&h->watch_list, struct xs_stored_msg, list); + list_del(&msg->list); + + xs_maybe_clear_watch_pipe(h); + mutex_unlock(&h->watch_mutex); + + assert(msg->hdr.type == XS_WATCH_EVENT); + + strings = msg->body; + num_strings = xs_count_strings(strings, msg->hdr.len); + + ret = malloc(sizeof(char*) * num_strings + msg->hdr.len); + if (!ret) { + free_no_errno(strings); + free_no_errno(msg); + return NULL; + } + + ret[0] = (char *)(ret + num_strings); + memcpy(ret[0], strings, msg->hdr.len); + + free(strings); + free(msg); + + for (i = 1; i < num_strings; i++) + ret[i] = ret[i - 1] + strlen(ret[i - 1]) + 1; + + *num = num_strings; + + return ret; +} + +char **xs_check_watch(struct xs_handle *h) +{ + unsigned int num; + char **ret; + ret = read_watch_internal(h, &num, 1); + if (ret) assert(num >= 2); + return ret; +} + +/* Find out what node change was on (will block if nothing pending). + * Returns array of two pointers: path and token, or NULL. + * Call free() after use. + */ +char **xs_read_watch(struct xs_handle *h, unsigned int *num) +{ + return read_watch_internal(h, num, 0); +} + +/* Remove a watch on a node. + * Returns false on failure (no watch on that node). + */ +bool xs_unwatch(struct xs_handle *h, const char *path, const char *token) +{ + struct iovec iov[2]; + struct xs_stored_msg *msg, *tmsg; + bool res; + char *s, *p; + unsigned int i; + char *l_token, *l_path; + + iov[0].iov_base = (char *)path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = (char *)token; + iov[1].iov_len = strlen(token) + 1; + + res = xs_bool(xs_talkv(h, XBT_NULL, XS_UNWATCH, iov, + ARRAY_SIZE(iov), NULL)); + + if (!h->unwatch_filter) /* Don't filter the watch list */ + return res; + + + /* Filter the watch list to remove potential message */ + mutex_lock(&h->watch_mutex); + + if (list_empty(&h->watch_list)) { + mutex_unlock(&h->watch_mutex); + return res; + } + + list_for_each_entry_safe(msg, tmsg, &h->watch_list, list) { + assert(msg->hdr.type == XS_WATCH_EVENT); + + s = msg->body; + + l_token = NULL; + l_path = NULL; + + for (p = s, i = 0; p < msg->body + msg->hdr.len; p++) { + if (*p == '\0') + { + if (i == XS_WATCH_TOKEN) + l_token = s; + else if (i == XS_WATCH_PATH) + l_path = s; + i++; + s = p + 1; + } + } + + if (l_token && !strcmp(token, l_token) && + l_path && xs_path_is_subpath(path, l_path)) { + list_del(&msg->list); + free(msg); + } + } + + xs_maybe_clear_watch_pipe(h); + + mutex_unlock(&h->watch_mutex); + + return res; +} + +/* Start a transaction: changes by others will not be seen during this + * transaction, and changes will not be visible to others until end. + * Returns XBT_NULL on failure. + */ +xs_transaction_t xs_transaction_start(struct xs_handle *h) +{ + char *id_str; + xs_transaction_t id; + + id_str = xs_single(h, XBT_NULL, XS_TRANSACTION_START, "", NULL); + if (id_str == NULL) + return XBT_NULL; + + id = strtoul(id_str, NULL, 0); + free(id_str); + + return id; +} + +/* End a transaction. + * If abandon is true, transaction is discarded instead of committed. + * Returns false on failure, which indicates an error: transactions will + * not fail spuriously. + */ +bool xs_transaction_end(struct xs_handle *h, xs_transaction_t t, + bool abort) +{ + char abortstr[2]; + + if (abort) + strcpy(abortstr, "F"); + else + strcpy(abortstr, "T"); + + return xs_bool(xs_single(h, t, XS_TRANSACTION_END, abortstr, NULL)); +} + +/* Introduce a new domain. + * This tells the store daemon about a shared memory page and event channel + * associated with a domain: the domain uses these to communicate. + */ +bool xs_introduce_domain(struct xs_handle *h, + unsigned int domid, unsigned long mfn, + unsigned int eventchn) +{ + char domid_str[MAX_STRLEN(domid)]; + char mfn_str[MAX_STRLEN(mfn)]; + char eventchn_str[MAX_STRLEN(eventchn)]; + struct iovec iov[3]; + + snprintf(domid_str, sizeof(domid_str), "%u", domid); + snprintf(mfn_str, sizeof(mfn_str), "%lu", mfn); + snprintf(eventchn_str, sizeof(eventchn_str), "%u", eventchn); + + iov[0].iov_base = domid_str; + iov[0].iov_len = strlen(domid_str) + 1; + iov[1].iov_base = mfn_str; + iov[1].iov_len = strlen(mfn_str) + 1; + iov[2].iov_base = eventchn_str; + iov[2].iov_len = strlen(eventchn_str) + 1; + + return xs_bool(xs_talkv(h, XBT_NULL, XS_INTRODUCE, iov, + ARRAY_SIZE(iov), NULL)); +} + +bool xs_set_target(struct xs_handle *h, + unsigned int domid, unsigned int target) +{ + char domid_str[MAX_STRLEN(domid)]; + char target_str[MAX_STRLEN(target)]; + struct iovec iov[2]; + + snprintf(domid_str, sizeof(domid_str), "%u", domid); + snprintf(target_str, sizeof(target_str), "%u", target); + + iov[0].iov_base = domid_str; + iov[0].iov_len = strlen(domid_str) + 1; + iov[1].iov_base = target_str; + iov[1].iov_len = strlen(target_str) + 1; + + return xs_bool(xs_talkv(h, XBT_NULL, XS_SET_TARGET, iov, + ARRAY_SIZE(iov), NULL)); +} + +static void * single_with_domid(struct xs_handle *h, + enum xsd_sockmsg_type type, + unsigned int domid) +{ + char domid_str[MAX_STRLEN(domid)]; + + snprintf(domid_str, sizeof(domid_str), "%u", domid); + + return xs_single(h, XBT_NULL, type, domid_str, NULL); +} + +bool xs_release_domain(struct xs_handle *h, unsigned int domid) +{ + return xs_bool(single_with_domid(h, XS_RELEASE, domid)); +} + +/* clear the shutdown bit for the given domain */ +bool xs_resume_domain(struct xs_handle *h, unsigned int domid) +{ + return xs_bool(single_with_domid(h, XS_RESUME, domid)); +} + +char *xs_get_domain_path(struct xs_handle *h, unsigned int domid) +{ + char domid_str[MAX_STRLEN(domid)]; + + snprintf(domid_str, sizeof(domid_str), "%u", domid); + + return xs_single(h, XBT_NULL, XS_GET_DOMAIN_PATH, domid_str, NULL); +} + +bool xs_path_is_subpath(const char *parent, const char *child) +{ + size_t childlen = strlen(child); + size_t parentlen = strlen(parent); + + if (childlen < parentlen) + return false; + + if (memcmp(child, parent, parentlen)) + return false; + + if (childlen > parentlen && child[parentlen] != '/') + return false; + + return true; +} + +bool xs_is_domain_introduced(struct xs_handle *h, unsigned int domid) +{ + char *domain = single_with_domid(h, XS_IS_DOMAIN_INTRODUCED, domid); + int rc = strcmp("F", domain); + + free(domain); + return rc; +} + +int xs_suspend_evtchn_port(int domid) +{ + char path[128]; + char *portstr; + int port; + unsigned int plen; + struct xs_handle *xs; + + xs = xs_daemon_open(); + if (!xs) + return -1; + + sprintf(path, "/local/domain/%d/device/suspend/event-channel", domid); + portstr = xs_read(xs, XBT_NULL, path, &plen); + xs_daemon_close(xs); + + if (!portstr || !plen) { + port = -1; + goto out; + } + + port = atoi(portstr); + +out: + free(portstr); + return port; +} + +char *xs_control_command(struct xs_handle *h, const char *cmd, + void *data, unsigned int len) +{ + struct iovec iov[2]; + + iov[0].iov_base = (void *)cmd; + iov[0].iov_len = strlen(cmd) + 1; + iov[1].iov_base = data; + iov[1].iov_len = len; + + return xs_talkv(h, XBT_NULL, XS_CONTROL, iov, + ARRAY_SIZE(iov), NULL); +} + +char *xs_debug_command(struct xs_handle *h, const char *cmd, + void *data, unsigned int len) +{ + return xs_control_command(h, cmd, data, len); +} + +static int read_message(struct xs_handle *h, int nonblocking) +{ + /* IMPORTANT: It is forbidden to call this function without + * acquiring the request lock and checking that h->read_thr_exists + * is false. See "Lock discipline" in struct xs_handle, above. */ + + /* If nonblocking==1, this function will always read either + * nothing, returning -1 and setting errno==EAGAIN, or we read + * whole amount requested. Ie as soon as we have the start of + * the message we block until we get all of it. + */ + + struct xs_stored_msg *msg = NULL; + char *body = NULL; + int saved_errno = 0; + int ret = -1; + + /* Allocate message structure and read the message header. */ + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto error; + cleanup_push_heap(msg); + if (!read_all(h->fd, &msg->hdr, sizeof(msg->hdr), nonblocking)) { /* Cancellation point */ + saved_errno = errno; + goto error_freemsg; + } + + /* Sanity check message body length. */ + if (msg->hdr.len > XENSTORE_PAYLOAD_MAX) { + saved_errno = E2BIG; + goto error_freemsg; + } + + /* Allocate and read the message body. */ + body = msg->body = malloc(msg->hdr.len + 1); + if (body == NULL) + goto error_freemsg; + cleanup_push_heap(body); + if (!read_all(h->fd, body, msg->hdr.len, 0)) { /* Cancellation point */ + saved_errno = errno; + goto error_freebody; + } + + body[msg->hdr.len] = '\0'; + + if (msg->hdr.type == XS_WATCH_EVENT) { + mutex_lock(&h->watch_mutex); + cleanup_push(pthread_mutex_unlock, &h->watch_mutex); + + /* Kick users out of their select() loop. */ + if (list_empty(&h->watch_list) && + (h->watch_pipe[1] != -1)) + while (write(h->watch_pipe[1], body, 1) != 1) /* Cancellation point */ + continue; + + list_add_tail(&msg->list, &h->watch_list); + + condvar_signal(&h->watch_condvar); + + cleanup_pop(1); + } else { + mutex_lock(&h->reply_mutex); + + /* There should only ever be one response pending! */ + if (!list_empty(&h->reply_list)) { + mutex_unlock(&h->reply_mutex); + saved_errno = EEXIST; + goto error_freebody; + } + + list_add_tail(&msg->list, &h->reply_list); + condvar_signal(&h->reply_condvar); + + mutex_unlock(&h->reply_mutex); + } + + ret = 0; + +error_freebody: + cleanup_pop_heap(ret == -1, body); +error_freemsg: + cleanup_pop_heap(ret == -1, msg); +error: + errno = saved_errno; + + return ret; +} + +#ifdef USE_PTHREAD +static void *read_thread(void *arg) +{ + struct xs_handle *h = arg; + int fd; + + while (read_message(h, 0) != -1) + continue; + + /* An error return from read_message leaves the socket in an undefined + * state; we might have read only the header and not the message after + * it, or (more commonly) the other end has closed the connection. + * Since further communication is unsafe, close the socket. + */ + fd = h->fd; + h->fd = -1; + close(fd); + + /* wake up all waiters */ + pthread_mutex_lock(&h->reply_mutex); + pthread_cond_broadcast(&h->reply_condvar); + pthread_mutex_unlock(&h->reply_mutex); + + pthread_mutex_lock(&h->watch_mutex); + pthread_cond_broadcast(&h->watch_condvar); + pthread_mutex_unlock(&h->watch_mutex); + + return NULL; +} +#endif + +char *expanding_buffer_ensure(struct expanding_buffer *ebuf, int min_avail) +{ + int want; + char *got; + + if (ebuf->avail >= min_avail) + return ebuf->buf; + + if (min_avail >= INT_MAX/3) + return 0; + + want = ebuf->avail + min_avail + 10; + got = realloc(ebuf->buf, want); + if (!got) + return 0; + + ebuf->buf = got; + ebuf->avail = want; + return ebuf->buf; +} + +char *sanitise_value(struct expanding_buffer *ebuf, + const char *val, unsigned len) +{ + int used, remain, c; + unsigned char *ip; + +#define ADD(c) (ebuf->buf[used++] = (c)) +#define ADDF(f,c) (used += sprintf(ebuf->buf+used, (f), (c))) + + assert(len < INT_MAX/5); + + ip = (unsigned char *)val; + used = 0; + remain = len; + + if (!expanding_buffer_ensure(ebuf, remain + 1)) + return NULL; + + while (remain-- > 0) { + c= *ip++; + + if (c >= ' ' && c <= '~' && c != '\\') { + ADD(c); + continue; + } + + if (!expanding_buffer_ensure(ebuf, used + remain + 5)) + /* for "<used>\\nnn<remain>\0" */ + return 0; + + ADD('\\'); + switch (c) { + case '\t': ADD('t'); break; + case '\n': ADD('n'); break; + case '\r': ADD('r'); break; + case '\\': ADD('\\'); break; + default: + if (c < 010) ADDF("%03o", c); + else ADDF("x%02x", c); + } + } + + ADD(0); + assert(used <= ebuf->avail); + return ebuf->buf; + +#undef ADD +#undef ADDF +} + +void unsanitise_value(char *out, unsigned *out_len_r, const char *in) +{ + const char *ip; + char *op; + unsigned c; + int n; + + for (ip = in, op = out; (c = *ip++); *op++ = c) { + if (c == '\\') { + c = *ip++; + +#define GETF(f) do { \ + n = 0; \ + sscanf(ip, f "%n", &c, &n); \ + ip += n; \ + } while (0) + + switch (c) { + case 't': c= '\t'; break; + case 'n': c= '\n'; break; + case 'r': c= '\r'; break; + case '\\': c= '\\'; break; + case 'x': GETF("%2x"); break; + case '0': case '4': + case '1': case '5': + case '2': case '6': + case '3': case '7': --ip; GETF("%3o"); break; + case 0: --ip; break; + default:; + } +#undef GETF + } + } + + *op = 0; + + if (out_len_r) + *out_len_r = op - out; +} + +/* + * Local variables: + * mode: C + * c-file-style: "linux" + * indent-tabs-mode: t + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/tools/libs/uselibs.mk b/tools/libs/uselibs.mk index 9619c576ba..a0fe0402ff 100644 --- a/tools/libs/uselibs.mk +++ b/tools/libs/uselibs.mk @@ -20,3 +20,5 @@ LIBS_LIBS += ctrl USELIBS_ctrl := toollog call evtchn gnttab foreignmemory devicemodel LIBS_LIBS += guest USELIBS_guest := evtchn ctrl +LIBS_LIBS += store +USELIBS_store := toolcore diff --git a/tools/python/setup.py b/tools/python/setup.py index 24b284af39..8254464aff 100644 --- a/tools/python/setup.py +++ b/tools/python/setup.py @@ -11,7 +11,7 @@ PATH_LIBXENTOOLLOG = XEN_ROOT + "/tools/libs/toollog" PATH_LIBXENEVTCHN = XEN_ROOT + "/tools/libs/evtchn" PATH_LIBXENCTRL = XEN_ROOT + "/tools/libs/ctrl" PATH_LIBXL = XEN_ROOT + "/tools/libxl" -PATH_XENSTORE = XEN_ROOT + "/tools/xenstore" +PATH_XENSTORE = XEN_ROOT + "/tools/libs/store" xc = Extension("xc", extra_compile_args = extra_compile_args, diff --git a/tools/xenstore/Makefile b/tools/xenstore/Makefile index 574be8d15c..9a0f0d012d 100644 --- a/tools/xenstore/Makefile +++ b/tools/xenstore/Makefile @@ -34,17 +34,7 @@ XENSTORED_OBJS_$(CONFIG_MiniOS) = xenstored_minios.o XENSTORED_OBJS += $(XENSTORED_OBJS_y) LDLIBS_xenstored += -lrt -ifneq ($(XENSTORE_STATIC_CLIENTS),y) -LIBXENSTORE := libxenstore.so -else -LIBXENSTORE := libxenstore.a -xenstore xenstore-control: CFLAGS += -static -endif - -ALL_TARGETS = libxenstore.a clients -ifneq ($(nosharedlibs),y) -ALL_TARGETS += libxenstore.so -endif +ALL_TARGETS = clients ifeq ($(XENSTORE_XENSTORED),y) ALL_TARGETS += xs_tdb_dump xenstored endif @@ -87,64 +77,21 @@ xenstored.a: $(XENSTORED_OBJS) $(CLIENTS): xenstore ln -f xenstore $@ -xenstore: xenstore_client.o $(LIBXENSTORE) +xenstore: xenstore_client.o $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) -xenstore-control: xenstore_control.o $(LIBXENSTORE) +xenstore-control: xenstore_control.o $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) xs_tdb_dump: xs_tdb_dump.o utils.o tdb.o talloc.o $(CC) $^ $(LDFLAGS) -o $@ $(APPEND_LDFLAGS) -libxenstore.so: libxenstore.so.$(MAJOR) - ln -sf $< $@ -libxenstore.so.$(MAJOR): libxenstore.so.$(MAJOR).$(MINOR) - ln -sf $< $@ - -xs.opic: CFLAGS += -DUSE_PTHREAD -ifeq ($(CONFIG_Linux),y) -xs.opic: CFLAGS += -DUSE_DLSYM -libxenstore.so.$(MAJOR).$(MINOR): APPEND_LDFLAGS += -ldl -PKG_CONFIG_LIBSPRIV := -ldl -endif - -libxenstore.so.$(MAJOR).$(MINOR): xs.opic xs_lib.opic - $(CC) $(LDFLAGS) $(PTHREAD_LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenstore.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) $(PTHREAD_LIBS) $(APPEND_LDFLAGS) - -libxenstore.a: xs.o xs_lib.o - $(AR) rcs $@ $^ - -PKG_CONFIG := xenstore.pc -PKG_CONFIG_NAME := Xenstore -PKG_CONFIG_DESC := The Xenstore library for Xen hypervisor -PKG_CONFIG_VERSION := $(MAJOR).$(MINOR) -PKG_CONFIG_USELIBS := $(SHLIB_libxenstore) -PKG_CONFIG_LIB := xenstore -PKG_CONFIG_REQPRIV := xenevtchn,xencontrol,xengnttab,xentoolcore - -ifneq ($(CONFIG_LIBXC_MINIOS),y) -PKG_CONFIG_INST := $(PKG_CONFIG) -$(PKG_CONFIG_INST): PKG_CONFIG_PREFIX = $(prefix) -$(PKG_CONFIG_INST): PKG_CONFIG_INCDIR = $(includedir) -$(PKG_CONFIG_INST): PKG_CONFIG_LIBDIR = $(libdir) -endif - -PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc)) - -$(PKG_CONFIG_LOCAL): PKG_CONFIG_PREFIX = $(XEN_ROOT) -$(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_libxenstore)/include -$(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR) -$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude) - -$(LIBXENSTORE): $(PKG_CONFIG_INST) $(PKG_CONFIG_LOCAL) - .PHONY: clean clean: - rm -f *.a *.o *.opic *.so* xenstored_probes.h + rm -f *.a *.o xenstored_probes.h rm -f xenstored xs_random xs_stress xs_crashme rm -f xs_tdb_dump xenstore-control init-xenstore-domain rm -f xenstore $(CLIENTS) - rm -f xenstore.pc $(RM) $(DEPS_RM) .PHONY: distclean @@ -161,8 +108,6 @@ tarball: clean .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(bindir) - $(INSTALL_DIR) $(DESTDIR)$(includedir) - $(INSTALL_DIR) $(DESTDIR)$(includedir)/xenstore-compat ifeq ($(XENSTORE_XENSTORED),y) $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_DIR) $(DESTDIR)$(XEN_LIB_STORED) @@ -173,32 +118,9 @@ endif set -e ; for c in $(CLIENTS) ; do \ ln -f $(DESTDIR)$(bindir)/xenstore $(DESTDIR)$(bindir)/$${c} ; \ done - $(INSTALL_DIR) $(DESTDIR)$(libdir) - $(INSTALL_SHLIB) libxenstore.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) - ln -sf libxenstore.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenstore.so.$(MAJOR) - ln -sf libxenstore.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenstore.so - $(INSTALL_DATA) libxenstore.a $(DESTDIR)$(libdir) - $(INSTALL_DATA) include/xenstore.h $(DESTDIR)$(includedir) - $(INSTALL_DATA) include/xenstore_lib.h $(DESTDIR)$(includedir) - $(INSTALL_DATA) include/compat/xs.h $(DESTDIR)$(includedir)/xenstore-compat/xs.h - $(INSTALL_DATA) include/compat/xs_lib.h $(DESTDIR)$(includedir)/xenstore-compat/xs_lib.h - ln -sf xenstore-compat/xs.h $(DESTDIR)$(includedir)/xs.h - ln -sf xenstore-compat/xs_lib.h $(DESTDIR)$(includedir)/xs_lib.h - $(INSTALL_DATA) xenstore.pc $(DESTDIR)$(PKG_INSTALLDIR) .PHONY: uninstall uninstall: - rm -f $(DESTDIR)$(PKG_INSTALLDIR)/xenstore.pc - rm -f $(DESTDIR)$(includedir)/xs_lib.h - rm -f $(DESTDIR)$(includedir)/xs.h - rm -f $(DESTDIR)$(includedir)/xenstore-compat/xs_lib.h - rm -f $(DESTDIR)$(includedir)/xenstore-compat/xs.h - rm -f $(DESTDIR)$(includedir)/xenstore_lib.h - rm -f $(DESTDIR)$(includedir)/xenstore.h - rm -f $(DESTDIR)$(libdir)/libxenstore.a - rm -f $(DESTDIR)$(libdir)/libxenstore.so - rm -f $(DESTDIR)$(libdir)/libxenstore.so.$(MAJOR) - rm -f $(DESTDIR)$(libdir)/libxenstore.so.$(MAJOR).$(MINOR) rm -f $(addprefix $(DESTDIR)$(bindir)/, $(CLIENTS)) rm -f $(DESTDIR)$(bindir)/xenstore rm -f $(DESTDIR)$(bindir)/xenstore-control diff --git a/tools/xenstore/include/compat/xs.h b/tools/xenstore/include/compat/xs.h deleted file mode 100644 index 99cf39bb1a..0000000000 --- a/tools/xenstore/include/compat/xs.h +++ /dev/null @@ -1,2 +0,0 @@ -#warning xs.h is deprecated use xenstore.h instead -#include <xenstore.h> diff --git a/tools/xenstore/include/compat/xs_lib.h b/tools/xenstore/include/compat/xs_lib.h deleted file mode 100644 index ad81b54c0e..0000000000 --- a/tools/xenstore/include/compat/xs_lib.h +++ /dev/null @@ -1,2 +0,0 @@ -#warning xs_lib.h is deprecated use xenstore_lib.h instead -#include <xenstore_lib.h> diff --git a/tools/xenstore/include/xenstore.h b/tools/xenstore/include/xenstore.h deleted file mode 100644 index 25b31881c8..0000000000 --- a/tools/xenstore/include/xenstore.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - Xen Store Daemon providing simple tree-like database. - Copyright (C) 2005 Rusty Russell IBM Corporation - - 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, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef XENSTORE_H -#define XENSTORE_H - -#include <xenstore_lib.h> - -#define XBT_NULL 0 - -#define XS_OPEN_READONLY (1UL<<0) -#define XS_OPEN_SOCKETONLY (1UL<<1) - -/* - * Setting XS_UNWATCH_FILTER arranges that after xs_unwatch, no - * related watch events will be delivered via xs_read_watch. But - * this relies on the couple token, subpath is unique. - * - * XS_UNWATCH_FILTER clear XS_UNWATCH_FILTER set - * - * Even after xs_unwatch, "stale" After xs_unwatch returns, no - * instances of the watch event watch events with the same - * may be delivered. token and with the same subpath - * will be delivered. - * - * A path and a subpath can be The application must avoid - * register with the same token. registering a path (/foo/) and - * a subpath (/foo/bar) with the - * same path until a successful - * xs_unwatch for the first watch - * has returned. - */ -#define XS_UNWATCH_FILTER (1UL<<2) - -struct xs_handle; -typedef uint32_t xs_transaction_t; - -/* IMPORTANT: For details on xenstore protocol limits, see - * docs/misc/xenstore.txt in the Xen public source repository, and use the - * XENSTORE_*_MAX limit macros defined in xen/io/xs_wire.h. - */ - -/* On failure, these routines set errno. */ - -/* Open a connection to the xs daemon. - * Attempts to make a connection over the socket interface, - * and if it fails, then over the xenbus interface. - * Mode 0 specifies read-write access, XS_OPEN_READONLY for - * read-only access. - * - * * Connections made with xs_open(0) (which might be shared page or - * socket based) are only guaranteed to work in the parent after - * fork. - * * Connections made with xs_open(XS_OPEN_SOCKETONLY) will be usable - * in either the parent or the child after fork, but not both. - * * xs_daemon_open*() and xs_domain_open() are deprecated synonyms - * for xs_open(0). - * * XS_OPEN_READONLY has no bearing on any of this. - * - * Returns a handle or NULL. - */ -struct xs_handle *xs_open(unsigned long flags); - -/* Close the connection to the xs daemon. */ -void xs_close(struct xs_handle *xsh /* NULL ok */); - -/* Connect to the xs daemon. - * Returns a handle or NULL. - * Deprecated, please use xs_open(0) instead - */ -struct xs_handle *xs_daemon_open(void); -struct xs_handle *xs_domain_open(void); - -/* Connect to the xs daemon (readonly for non-root clients). - * Returns a handle or NULL. - * Deprecated, please use xs_open(XS_OPEN_READONLY) instead - */ -struct xs_handle *xs_daemon_open_readonly(void); - -/* Close the connection to the xs daemon. - * Deprecated, please use xs_close() instead - */ -void xs_daemon_close(struct xs_handle *); - -/* Throw away the connection to the xs daemon, for use after fork(). */ -void xs_daemon_destroy_postfork(struct xs_handle *); - -/* Get contents of a directory. - * Returns a malloced array: call free() on it after use. - * Num indicates size. - * Returns NULL on failure. - */ -char **xs_directory(struct xs_handle *h, xs_transaction_t t, - const char *path, unsigned int *num); - -/* Get the value of a single file, nul terminated. - * Returns a malloced value: call free() on it after use. - * len indicates length in bytes, not including terminator. - * Returns NULL on failure. - */ -void *xs_read(struct xs_handle *h, xs_transaction_t t, - const char *path, unsigned int *len); - -/* Write the value of a single file. - * Returns false on failure. - */ -bool xs_write(struct xs_handle *h, xs_transaction_t t, - const char *path, const void *data, unsigned int len); - -/* Create a new directory. - * Returns false on failure, or success if it already exists. - */ -bool xs_mkdir(struct xs_handle *h, xs_transaction_t t, - const char *path); - -/* Destroy a file or directory (and children). - * Returns false on failure, or if it doesn't exist. - */ -bool xs_rm(struct xs_handle *h, xs_transaction_t t, - const char *path); - -/* Fake function which will always return false (required to let - * libxenstore remain at 3.0 version. - */ -bool xs_restrict(struct xs_handle *h, unsigned domid); - -/* Get permissions of node (first element is owner, first perms is "other"). - * Returns malloced array, or NULL: call free() after use. - */ -struct xs_permissions *xs_get_permissions(struct xs_handle *h, - xs_transaction_t t, - const char *path, unsigned int *num); - -/* Set permissions of node (must be owner). Returns false on failure. - * - * Domain 0 may read / write anywhere in the store, regardless of - * permission settings. - * - * Note: - * The perms array is a list of (domid, permissions) pairs. The first - * element in the list specifies the owner of the list, plus the flags - * for every domain not explicitly specified subsequently. The - * subsequent entries are normal capabilities. - * - * Example C code: - * - * struct xs_permissions perms[2]; - * - * perms[0].id = dm_domid; - * perms[0].perms = XS_PERM_NONE; - * perms[1].id = guest_domid; - * perms[1].perms = XS_PERM_READ; - * - * It means the owner of the path is domain $dm_domid (hence it always - * has read and write permission), all other domains (unless specified - * in subsequent pair) can neither read from nor write to that - * path. It then specifies domain $guest_domid can read from that - * path. - */ -bool xs_set_permissions(struct xs_handle *h, xs_transaction_t t, - const char *path, struct xs_permissions *perms, - unsigned int num_perms); - -/* Watch a node for changes (poll on fd to detect, or call read_watch()). - * When the node (or any child) changes, fd will become readable. - * Token is returned when watch is read, to allow matching. - * Returns false on failure. - */ -bool xs_watch(struct xs_handle *h, const char *path, const char *token); - -/* Return the FD to poll on to see if a watch has fired. */ -int xs_fileno(struct xs_handle *h); - -/* Check for node changes. On success, returns a non-NULL pointer ret - * such that ret[0] and ret[1] are valid C strings, namely the - * triggering path (see docs/misc/xenstore.txt) and the token (from - * xs_watch). On error return value is NULL setting errno. - * - * Callers should, after xs_fileno has become readable, repeatedly - * call xs_check_watch until it returns NULL and sets errno to EAGAIN. - * (If the fd became readable, xs_check_watch is allowed to make it no - * longer show up as readable even if future calls to xs_check_watch - * will return more watch events.) - * - * After the caller is finished with the returned information it - * should be freed all in one go with free(ret). - */ -char **xs_check_watch(struct xs_handle *h); - -/* Find out what node change was on (will block if nothing pending). - * Returns array containing the path and token, or NULL. - * Use XS_WATCH_* to access these elements. - * Call free() after use. - */ -char **xs_read_watch(struct xs_handle *h, unsigned int *num); - -/* Remove a watch on a node: implicitly acks any outstanding watch. - * Returns false on failure (no watch on that node). - */ -bool xs_unwatch(struct xs_handle *h, const char *path, const char *token); - -/* Start a transaction: changes by others will not be seen during this - * transaction, and changes will not be visible to others until end. - * Returns NULL on failure. - */ -xs_transaction_t xs_transaction_start(struct xs_handle *h); - -/* End a transaction. - * If abandon is true, transaction is discarded instead of committed. - * Returns false on failure: if errno == EAGAIN, you have to restart - * transaction. - */ -bool xs_transaction_end(struct xs_handle *h, xs_transaction_t t, - bool abort); - -/* Introduce a new domain. - * This tells the store daemon about a shared memory page, event channel and - * store path associated with a domain: the domain uses these to communicate. - */ -bool xs_introduce_domain(struct xs_handle *h, - unsigned int domid, - unsigned long mfn, - unsigned int eventchn); - -/* Set the target of a domain - * This tells the store daemon that a domain is targetting another one, so - * it should let it tinker with it. - */ -bool xs_set_target(struct xs_handle *h, - unsigned int domid, - unsigned int target); - -/* Resume a domain. - * Clear the shutdown flag for this domain in the store. - */ -bool xs_resume_domain(struct xs_handle *h, unsigned int domid); - -/* Release a domain. - * Tells the store domain to release the memory page to the domain. - */ -bool xs_release_domain(struct xs_handle *h, unsigned int domid); - -/* Query the home path of a domain. Call free() after use. - */ -char *xs_get_domain_path(struct xs_handle *h, unsigned int domid); - -/* Returns true if child is either equal to parent, or a node underneath - * parent; or false otherwise. Done by string comparison, so relative and - * absolute pathnames never in a parent/child relationship by this - * definition. Cannot fail. - */ -bool xs_path_is_subpath(const char *parent, const char *child); - -/* Return whether the domain specified has been introduced to xenstored. - */ -bool xs_is_domain_introduced(struct xs_handle *h, unsigned int domid); - -char *xs_control_command(struct xs_handle *h, const char *cmd, - void *data, unsigned int len); -/* Deprecated: use xs_control_command() instead. */ -char *xs_debug_command(struct xs_handle *h, const char *cmd, - void *data, unsigned int len); - -int xs_suspend_evtchn_port(int domid); -#endif /* XENSTORE_H */ - -/* - * Local variables: - * mode: C - * c-file-style: "linux" - * indent-tabs-mode: t - * c-basic-offset: 8 - * tab-width: 8 - * End: - */ diff --git a/tools/xenstore/include/xenstore_lib.h b/tools/xenstore/include/xenstore_lib.h deleted file mode 100644 index 0ffbae9eb5..0000000000 --- a/tools/xenstore/include/xenstore_lib.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - Common routines between Xen store user library and daemon. - Copyright (C) 2005 Rusty Russell IBM Corporation - - 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, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef XENSTORE_LIB_H -#define XENSTORE_LIB_H - -#include <stddef.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <stdint.h> -#include <xen/io/xs_wire.h> - -/* Bitmask of permissions. */ -enum xs_perm_type { - XS_PERM_NONE = 0, - XS_PERM_READ = 1, - XS_PERM_WRITE = 2, - /* Internal use. */ - XS_PERM_ENOENT_OK = 4, - XS_PERM_OWNER = 8, -}; - -struct xs_permissions -{ - unsigned int id; - enum xs_perm_type perms; -}; - -/* Header of the node record in tdb. */ -struct xs_tdb_record_hdr { - uint64_t generation; - uint32_t num_perms; - uint32_t datalen; - uint32_t childlen; - struct xs_permissions perms[0]; -}; - -/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */ -#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2) - -/* Path for various daemon things: env vars can override. */ -const char *xs_daemon_rootdir(void); -const char *xs_daemon_rundir(void); -const char *xs_daemon_socket(void); -const char *xs_daemon_socket_ro(void); -const char *xs_domain_dev(void); -const char *xs_daemon_tdb(void); - -/* Simple write function: loops for you. */ -bool xs_write_all(int fd, const void *data, unsigned int len); - -/* Convert strings to permissions. False if a problem. */ -bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, - const char *strings); - -/* Convert permissions to a string (up to len MAX_STRLEN(unsigned int)+1). */ -bool xs_perm_to_string(const struct xs_permissions *perm, - char *buffer, size_t buf_len); - -/* Given a string and a length, count how many strings (nul terms). */ -unsigned int xs_count_strings(const char *strings, unsigned int len); - -/* Sanitising (quoting) possibly-binary strings. */ -struct expanding_buffer { - char *buf; - int avail; -}; - -/* Ensure that given expanding buffer has at least min_avail characters. */ -char *expanding_buffer_ensure(struct expanding_buffer *, int min_avail); - -/* sanitise_value() may return NULL if malloc fails. */ -char *sanitise_value(struct expanding_buffer *, const char *val, unsigned len); - -/* *out_len_r on entry is ignored; out must be at least strlen(in)+1 bytes. */ -void unsanitise_value(char *out, unsigned *out_len_r, const char *in); - -#endif /* XENSTORE_LIB_H */ diff --git a/tools/xenstore/xenstore_lib.h b/tools/xenstore/xenstore_lib.h new file mode 100644 index 0000000000..0ffbae9eb5 --- /dev/null +++ b/tools/xenstore/xenstore_lib.h @@ -0,0 +1,94 @@ +/* + Common routines between Xen store user library and daemon. + Copyright (C) 2005 Rusty Russell IBM Corporation + + 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef XENSTORE_LIB_H +#define XENSTORE_LIB_H + +#include <stddef.h> +#include <stdbool.h> +#include <limits.h> +#include <errno.h> +#include <stdint.h> +#include <xen/io/xs_wire.h> + +/* Bitmask of permissions. */ +enum xs_perm_type { + XS_PERM_NONE = 0, + XS_PERM_READ = 1, + XS_PERM_WRITE = 2, + /* Internal use. */ + XS_PERM_ENOENT_OK = 4, + XS_PERM_OWNER = 8, +}; + +struct xs_permissions +{ + unsigned int id; + enum xs_perm_type perms; +}; + +/* Header of the node record in tdb. */ +struct xs_tdb_record_hdr { + uint64_t generation; + uint32_t num_perms; + uint32_t datalen; + uint32_t childlen; + struct xs_permissions perms[0]; +}; + +/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */ +#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2) + +/* Path for various daemon things: env vars can override. */ +const char *xs_daemon_rootdir(void); +const char *xs_daemon_rundir(void); +const char *xs_daemon_socket(void); +const char *xs_daemon_socket_ro(void); +const char *xs_domain_dev(void); +const char *xs_daemon_tdb(void); + +/* Simple write function: loops for you. */ +bool xs_write_all(int fd, const void *data, unsigned int len); + +/* Convert strings to permissions. False if a problem. */ +bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, + const char *strings); + +/* Convert permissions to a string (up to len MAX_STRLEN(unsigned int)+1). */ +bool xs_perm_to_string(const struct xs_permissions *perm, + char *buffer, size_t buf_len); + +/* Given a string and a length, count how many strings (nul terms). */ +unsigned int xs_count_strings(const char *strings, unsigned int len); + +/* Sanitising (quoting) possibly-binary strings. */ +struct expanding_buffer { + char *buf; + int avail; +}; + +/* Ensure that given expanding buffer has at least min_avail characters. */ +char *expanding_buffer_ensure(struct expanding_buffer *, int min_avail); + +/* sanitise_value() may return NULL if malloc fails. */ +char *sanitise_value(struct expanding_buffer *, const char *val, unsigned len); + +/* *out_len_r on entry is ignored; out must be at least strlen(in)+1 bytes. */ +void unsanitise_value(char *out, unsigned *out_len_r, const char *in); + +#endif /* XENSTORE_LIB_H */ diff --git a/tools/xenstore/xs.c b/tools/xenstore/xs.c deleted file mode 100644 index aa1d24b8b9..0000000000 --- a/tools/xenstore/xs.c +++ /dev/null @@ -1,1473 +0,0 @@ -/* - Xen Store Daemon interface providing simple tree-like database. - Copyright (C) 2005 Rusty Russell IBM Corporation - - 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, see <http://www.gnu.org/licenses/>. -*/ - -#define _GNU_SOURCE - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <sys/uio.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <string.h> -#include <unistd.h> -#include <stdbool.h> -#include <stdlib.h> -#include <assert.h> -#include <stdio.h> -#include <signal.h> -#include <stdint.h> -#include <errno.h> -#include "xenstore.h" -#include "list.h" -#include "utils.h" - -#include <xentoolcore_internal.h> - -struct xs_stored_msg { - struct list_head list; - struct xsd_sockmsg hdr; - char *body; -}; - -#ifdef USE_PTHREAD - -#include <pthread.h> - -#ifdef USE_DLSYM -#include <dlfcn.h> -#endif - -struct xs_handle { - /* Communications channel to xenstore daemon. */ - int fd; - Xentoolcore__Active_Handle tc_ah; /* for restrict */ - - /* - * A read thread which pulls messages off the comms channel and - * signals waiters. - */ - pthread_t read_thr; - int read_thr_exists; - - /* - * A list of fired watch messages, protected by a mutex. Users can - * wait on the conditional variable until a watch is pending. - */ - struct list_head watch_list; - pthread_mutex_t watch_mutex; - pthread_cond_t watch_condvar; - - /* Clients can select() on this pipe to wait for a watch to fire. */ - int watch_pipe[2]; - /* Filtering watch event in unwatch function? */ - bool unwatch_filter; - - /* - * A list of replies. Currently only one will ever be outstanding - * because we serialise requests. The requester can wait on the - * conditional variable for its response. - */ - struct list_head reply_list; - pthread_mutex_t reply_mutex; - pthread_cond_t reply_condvar; - - /* One request at a time. */ - pthread_mutex_t request_mutex; - - /* Lock discipline: - * Only holder of the request lock may write to h->fd. - * Only holder of the request lock may access read_thr_exists. - * If read_thr_exists==0, only holder of request lock may read h->fd; - * If read_thr_exists==1, only the read thread may read h->fd. - * Only holder of the reply lock may access reply_list. - * Only holder of the watch lock may access watch_list. - * Lock hierarchy: - * The order in which to acquire locks is - * request_mutex - * reply_mutex - * watch_mutex - */ -}; - -#define mutex_lock(m) pthread_mutex_lock(m) -#define mutex_unlock(m) pthread_mutex_unlock(m) -#define condvar_signal(c) pthread_cond_signal(c) -#define condvar_wait(c,m) pthread_cond_wait(c,m) -#define cleanup_push(f, a) \ - pthread_cleanup_push((void (*)(void *))(f), (void *)(a)) -/* - * Some definitions of pthread_cleanup_pop() are a macro starting with an - * end-brace. GCC then complains if we immediately precede that with a label. - * Hence we insert a dummy statement to appease the compiler in this situation. - */ -#define cleanup_pop(run) ((void)0); pthread_cleanup_pop(run) - -#define read_thread_exists(h) (h->read_thr_exists) - -/* Because pthread_cleanup_p* are not available when USE_PTHREAD is - * disabled, use these macros which convert appropriately. */ -#define cleanup_push_heap(p) cleanup_push(free, p) -#define cleanup_pop_heap(run, p) cleanup_pop((run)) - -static void *read_thread(void *arg); - -#else /* !defined(USE_PTHREAD) */ - -struct xs_handle { - int fd; - Xentoolcore__Active_Handle tc_ah; /* for restrict */ - struct list_head reply_list; - struct list_head watch_list; - /* Clients can select() on this pipe to wait for a watch to fire. */ - int watch_pipe[2]; - /* Filtering watch event in unwatch function? */ - bool unwatch_filter; -}; - -#define mutex_lock(m) ((void)0) -#define mutex_unlock(m) ((void)0) -#define condvar_signal(c) ((void)0) -#define condvar_wait(c,m) ((void)0) -#define cleanup_push(f, a) ((void)0) -#define cleanup_pop(run) ((void)0) -#define read_thread_exists(h) (0) - -#define cleanup_push_heap(p) ((void)0) -#define cleanup_pop_heap(run, p) do { if ((run)) free(p); } while(0) - -#endif - -static int read_message(struct xs_handle *h, int nonblocking); - -static bool setnonblock(int fd, int nonblock) { - int flags = fcntl(fd, F_GETFL); - if (flags == -1) - return false; - - if (nonblock) - flags |= O_NONBLOCK; - else - flags &= ~O_NONBLOCK; - - if (fcntl(fd, F_SETFL, flags) == -1) - return false; - - return true; -} - -int xs_fileno(struct xs_handle *h) -{ - char c = 0; - - mutex_lock(&h->watch_mutex); - - if ((h->watch_pipe[0] == -1) && (pipe(h->watch_pipe) != -1)) { - /* Kick things off if the watch list is already non-empty. */ - if (!list_empty(&h->watch_list)) - while (write(h->watch_pipe[1], &c, 1) != 1) - continue; - } - - mutex_unlock(&h->watch_mutex); - - return h->watch_pipe[0]; -} - -static int get_socket(const char *connect_to) -{ - struct sockaddr_un addr; - int sock, saved_errno, flags; - - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - return -1; - - if ((flags = fcntl(sock, F_GETFD)) < 0) - goto error; - flags |= FD_CLOEXEC; - if (fcntl(sock, F_SETFD, flags) < 0) - goto error; - - addr.sun_family = AF_UNIX; - if(strlen(connect_to) >= sizeof(addr.sun_path)) { - errno = EINVAL; - goto error; - } - strcpy(addr.sun_path, connect_to); - - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) - goto error; - - return sock; - -error: - saved_errno = errno; - close(sock); - errno = saved_errno; - return -1; -} - -static int get_dev(const char *connect_to) -{ - /* We cannot open read-only because requests are writes */ - return open(connect_to, O_RDWR); -} - -static int all_restrict_cb(Xentoolcore__Active_Handle *ah, domid_t domid) { - struct xs_handle *h = CONTAINER_OF(ah, *h, tc_ah); - return xentoolcore__restrict_by_dup2_null(h->fd); -} - -static struct xs_handle *get_handle(const char *connect_to) -{ - struct stat buf; - struct xs_handle *h = NULL; - int saved_errno; - - h = malloc(sizeof(*h)); - if (h == NULL) - goto err; - - memset(h, 0, sizeof(*h)); - h->fd = -1; - - h->tc_ah.restrict_callback = all_restrict_cb; - xentoolcore__register_active_handle(&h->tc_ah); - - if (stat(connect_to, &buf) != 0) - goto err; - - if (S_ISSOCK(buf.st_mode)) - h->fd = get_socket(connect_to); - else - h->fd = get_dev(connect_to); - - if (h->fd == -1) - goto err; - - INIT_LIST_HEAD(&h->reply_list); - INIT_LIST_HEAD(&h->watch_list); - - /* Watch pipe is allocated on demand in xs_fileno(). */ - h->watch_pipe[0] = h->watch_pipe[1] = -1; - - h->unwatch_filter = false; - -#ifdef USE_PTHREAD - pthread_mutex_init(&h->watch_mutex, NULL); - pthread_cond_init(&h->watch_condvar, NULL); - - pthread_mutex_init(&h->reply_mutex, NULL); - pthread_cond_init(&h->reply_condvar, NULL); - - pthread_mutex_init(&h->request_mutex, NULL);
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |