XenStore Reference

From Xen
Revision as of 10:04, 25 February 2015 by Ijc (talk | contribs) (Reference xenstore-paths doc instead of duplicating it)


About

This document describes the format of the entries in XenStore, how and what they're used for, and how third-party apps should use XenStore as a management interface. There are also code examples provided showing how to use the XenStore API. The xenstore is maintained by the Xenstored.

Overview

XenStore is a hierarchical namespace (similar to sysfs or Open Firmware) which is shared between domains. The interdomain communication primitives exposed by Xen are very low-level (virtual IRQ and shared memory). XenStore is implemented on top of these primitives and provides some higher level operations (read a key, write a key, enumerate a directory, notify when a key changes value).

XenStore is a database, hosted by domain 0, that supports transactions and atomic operations. It's accessible by either a Unix domain socket in Domain-0, a kernel-level API, or an ioctl interface via /proc/xen/xenbus. XenStore should always be accessed through the functions defined in <xs.h>. XenStore is used to store information about the domains during their execution and as a mechanism of creating and controlling Domain-U devices.

XenBus is the in-kernel API used by virtual I/O drivers to interact with XenStore.

Contents

Canonical information on the paths used within xenstore are contained in the xen.git documentation tree, see: http://xenbits.xen.org/docs/unstable/misc/xenstore-paths.html

Interacting with the XenStore

The XenStore interface provides transaction based reads and writes to points in the xenstore hierarchy. Watches can be set at points in the hierarchy and an individual watch will be triggered when anything at or below that point in the hierachy changes. A watch is registered with a callback function and a "token". The "token" can be a pointer to any piece of data. The callback function is invoked with the of the changed node and the "token".

The interface is centered around the idea of a central polling loop that reads watches, providing the path, callback, and token, and invoking the callback.

API Usage Examples

These code snippets should provide a helpful starting point.

C

#include <xs.h> // don't forget to link with libxenstore.so

struct xs_handle *xs;
xs_transaction_t th;
char *path;
int fd;
fd_set set;
int er;
struct timeval tv = {.tv_sec = 0, .tv_usec = 0 };
char **vec;
unsigned int num_strings;
char * buf;
unsigned int len;
/* Get a connection to the daemon */
xs = xs_daemon_open();
if ( xs == NULL ) error();
/* Get the local domain path */
path = xs_get_domain_path(xs, domid); // replace "domid" with a valid domain ID (or one which will become valid)
if ( path == NULL ) error();
/* Make space for our node on the path */
path = realloc(path, strlen(path) + strlen("/mynode") + 1);
if ( path == NULL ) error();
strcat(path, "/mynode");
/* Create a watch on /local/domain/%d/mynode. */
er = xs_watch(xs, path, "mytoken");
if ( er == 0 ) error();
/* We are notified of read availability on the watch via the
 * file descriptor.
 */
fd = xs_fileno(xs);
while (1)
{
    /* TODO (TimPost), show a simpler example with poll()
     * in a modular style, using a simple callback. Most
     * people think 'inotify' when they see 'watches'. */
    FD_ZERO(&set);
    FD_SET(fd, &set);
    /* Poll for data. */
    if ( select(fd + 1, &set, NULL, NULL, &tv) > 0
         && FD_ISSET(fd, &set))
    {
        /* num_strings will be set to the number of elements in vec
         * (typically, 2 - the watched path and the token) */
        vec = xs_read_watch(xs, &num_strings);
        if ( !vec ) error();
        printf("vec contents: %s|%s\n", vec[XS_WATCH_PATH],
                                        vec[XS_WATCH_TOKEN]);
        /* Prepare a transaction and do a read. */
        th = xs_transaction_start(xs);
        buf = xs_read(xs, th, vec[XS_WATCH_PATH], &len);
        xs_transaction_end(xs, th, false);
        if ( buf )
        {
            printf("buflen: %d\nbuf: %s\n", len, buf);
        }
        /* Prepare a transaction and do a write. */
        th = xs_transaction_start(xs);
        er = xs_write(xs, th, path, "somestuff", strlen("somestuff"));
        xs_transaction_end(xs, th, false);
        if ( er == 0 ) error();
    }
}
/* Cleanup */
close(fd);
xs_daemon_close(xs);
free(path);

Python

#!python
# xsutil provides access to xshandle() which allows you to use something closer to the C-style API,
# however it does not support polling in the same manner.
from xen.xend.xenstore.xsutil import *
# xswatch provides a callback interface for the watches.  I similar interface exists for C within xenbus.
from xen.xend.xenstore.xswatch import *

xs = xshandle() # From xsutil
path = xs.get_domain_path(1) + "/mynode" # replace 1 with a domain ID which is valid (or will become valid)

# Watch functions take the path as the first argument,
# all other arguments that are passed via the xswatch call are also included.
# Note that "path" does not point to the watched path but to the changed
# path (i.e. the watched path or any node below)!
def watch_func(path, xs):
    # Read the data
    th = xs.transaction_start()
    buf = xs.read(th, path)
    xs.transaction_end(th)
    print("Got %s" % buf)
    # Write back "somestuff" (as we change the path value, our callback function will then be called recursively because we return True!)
    th = xs.transaction_start()
    xs.write(th, path, "somestuff")
    xs.transaction_end(th)

    # The watch will be removed if False is returned (e.g. if you want the callback to be called only once)
    return True

# This will start a thread which waits for changes in watched paths
# and calls the appropriate callback functions if something changed
mywatch = xswatch(path, watch_func, xs) # pass "xs" as argument to the watch function

You can use direct Read/Write or gather calls via xstransact.

By default the python xsutil.xshandle() is a shared global handle. xswatch uses this handle with a blocking read_watch call. Because the read_watch function is protected by a per-handle mutex, multiple calls will be interleaved and you probably do not want this behavior. If you would like a blocking mechanism, you might consider introducing a semaphore in the callback function that can be used to block code execution. You need to be sure to handle failure cases and not block indefinitely. For instance, the "@releaseDomain" watch will be triggered on domain destruction for watches within the /local/domain/* trees.

It is also possible -- currently indirectly -- to get a fresh XenStore handle within python and block on read_watch in the main execution path. This may be necessary if you want to block waiting for a XenStore node value in a code path initialed by an xswatch callback.