diff options
Diffstat (limited to 'tools/xenstore/xs.c')
-rw-r--r-- | tools/xenstore/xs.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/tools/xenstore/xs.c b/tools/xenstore/xs.c new file mode 100644 index 0000000000..d5058abfb3 --- /dev/null +++ b/tools/xenstore/xs.c @@ -0,0 +1,551 @@ +/* + Xen Store Daemon interface providing simple tree-like database. + Copyright (C) 2005 Rusty Russell IBM Corporation + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.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 "xs.h" +#include "xenstored.h" +#include "xs_lib.h" +#include "utils.h" + +struct xs_handle +{ + int fd; +}; + +/* Get the socket from the store daemon handle. + */ +int xs_fileno(struct xs_handle *h) +{ + return h->fd; +} + +static struct xs_handle *get_socket(const char *connect_to) +{ + struct sockaddr_un addr; + int sock, saved_errno; + struct xs_handle *h = NULL; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return NULL; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, connect_to); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + h = malloc(sizeof(*h)); + if (h) { + h->fd = sock; + return h; + } + } + + saved_errno = errno; + close(sock); + free(h); + errno = saved_errno; + return NULL; +} + +struct xs_handle *xs_daemon_open(void) +{ + return get_socket(xs_daemon_socket()); +} + +struct xs_handle *xs_daemon_open_readonly(void) +{ + return get_socket(xs_daemon_socket_ro()); +} + +void xs_daemon_close(struct xs_handle *h) +{ + if (h->fd >= 0) + close(h->fd); + free(h); +} + +static bool read_all(int fd, void *data, unsigned int len) +{ + while (len) { + int done; + + done = read(fd, data, len); + if (done < 0) { + if (errno == EINTR) + continue; + return false; + } + if (done == 0) { + /* It closed fd on us? EBADF is appropriate. */ + errno = EBADF; + return false; + } + data += done; + len -= done; + } + + return true; +} + +#ifdef XSTEST +#define read_all read_all_choice +#define 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; +} + +static void *read_reply(int fd, enum xsd_sockmsg_type *type, unsigned int *len) +{ + struct xsd_sockmsg msg; + void *ret; + int saved_errno; + + if (!read_all(fd, &msg, sizeof(msg))) + return NULL; + + ret = malloc(msg.len); + if (!ret) + return NULL; + + if (!read_all(fd, ret, msg.len)) { + saved_errno = errno; + free(ret); + errno = saved_errno; + return NULL; + } + + *type = msg.type; + if (len) + *len = msg.len; + return ret; +} + +/* Send message to xs, get malloc'ed reply. NULL and set errno on error. */ +static void *xs_talkv(struct xs_handle *h, 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.type = type; + msg.len = 0; + for (i = 0; i < num_vecs; i++) + msg.len += iovec[i].iov_len; + + ignorepipe.sa_handler = SIG_IGN; + sigemptyset(&ignorepipe.sa_mask); + ignorepipe.sa_flags = 0; + sigaction(SIGPIPE, &ignorepipe, &oldact); + + if (!write_all(h->fd, &msg, sizeof(msg))) + goto fail; + + for (i = 0; i < num_vecs; i++) + if (!write_all(h->fd, iovec[i].iov_base, iovec[i].iov_len)) + goto fail; + + /* Watches can have fired before reply comes: daemon detects + * and re-transmits, so we can ignore this. */ + do { + free(ret); + ret = read_reply(h->fd, &msg.type, len); + if (!ret) + goto fail; + } while (msg.type == XS_WATCH_EVENT); + + sigaction(SIGPIPE, &oldact, NULL); + if (msg.type == XS_ERROR) { + saved_errno = get_error(ret); + free(ret); + errno = saved_errno; + return NULL; + } + + assert(msg.type == type); + return ret; + +fail: + /* We're in a bad state, so close fd. */ + saved_errno = errno; + sigaction(SIGPIPE, &oldact, NULL); + 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, 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, type, &iovec, 1, len); +} + +static bool xs_bool(char *reply) +{ + if (!reply) + return false; + free(reply); + return true; +} + +char **xs_directory(struct xs_handle *h, const char *path, unsigned int *num) +{ + char *strings, *p, **ret; + unsigned int len; + + strings = xs_single(h, XS_DIRECTORY, path, &len); + if (!strings) + return NULL; + + /* Count the strings. */ + *num = 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; +} + +/* Get the value of a single file. + * Returns a malloced value: call free() on it after use. + * len indicates length in bytes. + */ +void *xs_read(struct xs_handle *h, const char *path, unsigned int *len) +{ + return xs_single(h, XS_READ, path, len); +} + +/* Write the value of a single file. + * Returns false on failure. createflags can be 0, O_CREAT, or O_CREAT|O_EXCL. + */ +bool xs_write(struct xs_handle *h, const char *path, + const void *data, unsigned int len, int createflags) +{ + const char *flags; + struct iovec iovec[3]; + + /* Format: Flags (as string), path, data. */ + if (createflags == 0) + flags = XS_WRITE_NONE; + else if (createflags == O_CREAT) + flags = XS_WRITE_CREATE; + else if (createflags == (O_CREAT|O_EXCL)) + flags = XS_WRITE_CREATE_EXCL; + else { + errno = EINVAL; + return false; + } + + iovec[0].iov_base = (void *)path; + iovec[0].iov_len = strlen(path) + 1; + iovec[1].iov_base = (void *)flags; + iovec[1].iov_len = strlen(flags) + 1; + iovec[2].iov_base = (void *)data; + iovec[2].iov_len = len; + + return xs_bool(xs_talkv(h, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL)); +} + +/* Create a new directory. + * Returns false on failure. + */ +bool xs_mkdir(struct xs_handle *h, const char *path) +{ + return xs_bool(xs_single(h, XS_MKDIR, path, NULL)); +} + +/* Destroy a file or directory (directories must be empty). + * Returns false on failure. + */ +bool xs_rm(struct xs_handle *h, const char *path) +{ + return xs_bool(xs_single(h, 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, + const char *path, + unsigned int *num) +{ + char *strings; + unsigned int len; + struct xs_permissions *ret; + + strings = xs_single(h, XS_GET_PERMS, path, &len); + if (!strings) + return NULL; + + /* Count the strings: each one perms then domid. */ + *num = 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 (!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, 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(domid_t)+1]; + + if (!perm_to_string(&perms[i], 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, 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; +} + +/* 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. + * Priority indicates order if multiple watchers: higher is first. + * Returns false on failure. + */ +bool xs_watch(struct xs_handle *h, const char *path, unsigned int priority) +{ + char prio[MAX_STRLEN(priority)]; + struct iovec iov[2]; + + sprintf(prio, "%u", priority); + iov[0].iov_base = (void *)path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = prio; + iov[1].iov_len = strlen(prio) + 1; + + return xs_bool(xs_talkv(h, XS_WATCH, iov, ARRAY_SIZE(iov), NULL)); +} + +/* Find out what node change was on (will block if nothing pending). + * Returns malloced path, or NULL: call free() after use. + */ +char *xs_read_watch(struct xs_handle *h) +{ + struct xsd_sockmsg msg; + char *path; + + if (!read_all(h->fd, &msg, sizeof(msg))) + return NULL; + + assert(msg.type == XS_WATCH_EVENT); + path = malloc(msg.len); + if (!path) + return NULL; + + if (!read_all(h->fd, path, msg.len)) { + free_no_errno(path); + return NULL; + } + return path; +} + +/* Acknowledge watch on node. Watches must be acknowledged before + * any other watches can be read. + * Returns false on failure. + */ +bool xs_acknowledge_watch(struct xs_handle *h) +{ + return xs_bool(xs_single(h, XS_WATCH_ACK, "OK", NULL)); +} + +/* 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) +{ + return xs_bool(xs_single(h, XS_UNWATCH, path, NULL)); +} + +/* Start a transaction: changes by others will not be seen during this + * transaction, and changes will not be visible to others until end. + * Transaction only applies to the given subtree. + * You can only have one transaction at any time. + * Returns false on failure. + */ +bool xs_transaction_start(struct xs_handle *h, const char *subtree) +{ + return xs_bool(xs_single(h, XS_TRANSACTION_START, subtree, NULL)); +} + +/* 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, bool abort) +{ + char abortstr[2]; + + if (abort) + strcpy(abortstr, "F"); + else + strcpy(abortstr, "T"); + return xs_bool(xs_single(h, 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, + domid_t domid, + unsigned long mfn, + unsigned int eventchn, + const char *path) +{ + char domid_str[MAX_STRLEN(domid)]; + char mfn_str[MAX_STRLEN(mfn)]; + char eventchn_str[MAX_STRLEN(eventchn)]; + struct iovec iov[4]; + + sprintf(domid_str, "%u", domid); + sprintf(mfn_str, "%lu", mfn); + sprintf(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; + iov[3].iov_base = (char *)path; + iov[3].iov_len = strlen(path) + 1; + + return xs_bool(xs_talkv(h, XS_INTRODUCE, iov, ARRAY_SIZE(iov), NULL)); +} + +bool xs_release_domain(struct xs_handle *h, + domid_t domid) +{ + char domid_str[MAX_STRLEN(domid)]; + + sprintf(domid_str, "%u", domid); + + return xs_bool(xs_single(h, XS_RELEASE, domid_str, NULL)); +} + +bool xs_shutdown(struct xs_handle *h) +{ + bool ret = xs_bool(xs_single(h, XS_SHUTDOWN, "", NULL)); + if (ret) { + char c; + /* Wait for it to actually shutdown. */ + read(h->fd, &c, 1); + } + return ret; +} + +/* Only useful for DEBUG versions */ +char *xs_debug_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, XS_DEBUG, iov, ARRAY_SIZE(iov), NULL); +} |