2
* Python interface to the Xen Store Daemon.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of version 2.1 of the GNU Lesser General Public
6
* License as published by the Free Software Foundation.
8
* This library is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public
14
* License along with this library; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
* Copyright (C) 2005 Mike Wray Hewlett-Packard
18
* Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
19
* Copyright (C) 2005 XenSource Ltd.
28
#include <sys/types.h>
37
* Python interface to the Xen Store Daemon (xs).
40
/* Needed for Python versions earlier than 2.3. */
41
#ifndef PyMODINIT_FUNC
42
#define PyMODINIT_FUNC DL_EXPORT(void)
45
#define PKG "xen.lowlevel.xs"
48
static PyObject *xs_error;
50
/** Python wrapper round an xs handle.
52
typedef struct XsHandle {
58
static void xs_set_error(int value)
61
PyErr_SetFromErrno(xs_error);
64
static inline struct xs_handle *xshandle(XsHandle *self)
66
struct xs_handle *xh = self->xh;
72
static void remove_watch(XsHandle *xsh, PyObject *token);
74
static PyObject *none(bool result);
76
static int parse_transaction_path(XsHandle *self, PyObject *args,
77
struct xs_handle **xh,
82
#define xspy_read_doc "\n" \
83
"Read data from a path.\n" \
84
" transaction [string]: transaction handle\n" \
85
" path [string]: xenstore path\n" \
87
"Returns: [string] data read.\n" \
88
" None if key doesn't exist.\n" \
89
"Raises xen.lowlevel.xs.Error on error.\n" \
92
static PyObject *xspy_read(XsHandle *self, PyObject *args)
101
if (!parse_transaction_path(self, args, &xh, &th, &path))
104
Py_BEGIN_ALLOW_THREADS
105
xsval = xs_read(xh, th, path, &xsval_n);
108
PyObject *val = PyString_FromStringAndSize(xsval, xsval_n);
113
return none(errno == ENOENT);
118
#define xspy_write_doc "\n" \
119
"Write data to a path.\n" \
120
" transaction [string]: transaction handle\n" \
121
" path [string] : xenstore path to write to\n." \
122
" data [string] : data to write.\n" \
124
"Returns None on success.\n" \
125
"Raises xen.lowlevel.xs.Error on error.\n" \
128
static PyObject *xspy_write(XsHandle *self, PyObject *args)
130
static char *arg_spec = "sss#";
131
struct xs_handle *xh = xshandle(self);
141
if (!PyArg_ParseTuple(args, arg_spec, &thstr, &path, &data, &data_n))
144
th = strtoul(thstr, NULL, 16);
146
Py_BEGIN_ALLOW_THREADS
147
result = xs_write(xh, th, path, data, data_n);
154
#define xspy_ls_doc "\n" \
155
"List a directory.\n" \
156
" transaction [string]: transaction handle\n" \
157
" path [string]: path to list.\n" \
159
"Returns: [string array] list of subdirectory names.\n" \
160
" None if key doesn't exist.\n" \
161
"Raises xen.lowlevel.xs.Error on error.\n" \
164
static PyObject *xspy_ls(XsHandle *self, PyObject *args)
166
struct xs_handle *xh;
171
unsigned int xsval_n;
173
if (!parse_transaction_path(self, args, &xh, &th, &path))
176
Py_BEGIN_ALLOW_THREADS
177
xsval = xs_directory(xh, th, path, &xsval_n);
182
PyObject *val = PyList_New(xsval_n);
183
for (i = 0; i < xsval_n; i++)
184
PyList_SetItem(val, i, PyString_FromString(xsval[i]));
189
return none(errno == ENOENT);
194
#define xspy_mkdir_doc "\n" \
195
"Make a directory.\n" \
196
" path [string]: path to directory to create.\n" \
198
"Returns None on success.\n" \
199
"Raises xen.lowlevel.xs.Error on error.\n" \
202
static PyObject *xspy_mkdir(XsHandle *self, PyObject *args)
204
struct xs_handle *xh;
210
if (!parse_transaction_path(self, args, &xh, &th, &path))
213
Py_BEGIN_ALLOW_THREADS
214
result = xs_mkdir(xh, th, path);
221
#define xspy_rm_doc "\n" \
223
" transaction [string]: transaction handle\n" \
224
" path [string] : path to remove\n" \
226
"Returns None on success.\n" \
227
"Raises xen.lowlevel.xs.Error on error.\n" \
230
static PyObject *xspy_rm(XsHandle *self, PyObject *args)
232
struct xs_handle *xh;
238
if (!parse_transaction_path(self, args, &xh, &th, &path))
241
Py_BEGIN_ALLOW_THREADS
242
result = xs_rm(xh, th, path);
245
return none(result || errno == ENOENT);
249
#define xspy_get_permissions_doc "\n" \
250
"Get the permissions for a path\n" \
251
" transaction [string]: transaction handle\n" \
252
" path [string]: xenstore path.\n" \
254
"Returns: permissions array.\n" \
255
"Raises xen.lowlevel.xs.Error on error.\n" \
258
static PyObject *xspy_get_permissions(XsHandle *self, PyObject *args)
260
static char *arg_spec = "ss";
263
struct xs_handle *xh = xshandle(self);
264
struct xs_permissions *perms;
265
unsigned int perms_n = 0;
273
if (!PyArg_ParseTuple(args, arg_spec, &thstr, &path))
276
th = strtoul(thstr, NULL, 16);
278
Py_BEGIN_ALLOW_THREADS
279
perms = xs_get_permissions(xh, th, path, &perms_n);
283
PyObject *val = PyList_New(perms_n);
284
for (i = 0; i < perms_n; i++) {
286
Py_BuildValue("{s:i,s:i,s:i}",
288
"read", perms[i].perms & XS_PERM_READ,
289
"write", perms[i].perms & XS_PERM_WRITE);
290
PyList_SetItem(val, i, p);
297
PyErr_SetFromErrno(xs_error);
302
#define xspy_set_permissions_doc "\n" \
303
"Set the permissions for a path\n" \
304
" transaction [string]: transaction handle\n" \
305
" path [string] : xenstore path.\n" \
306
" perms : permissions.\n" \
308
"Returns None on success.\n" \
309
"Raises xen.lowlevel.xs.Error on error.\n" \
312
static PyObject *xspy_set_permissions(XsHandle *self, PyObject *args)
316
static char *perm_names[] = { "dom", "read", "write", NULL };
317
static char *perm_spec = "i|ii";
319
struct xs_handle *xh = xshandle(self);
321
struct xs_permissions *xsperms = NULL;
323
PyObject *tuple0 = NULL;
327
PyObject *ret = NULL;
331
if (!PyArg_ParseTuple(args, "ssO", &thstr, &path, &perms))
334
th = strtoul(thstr, NULL, 16);
336
if (!PyList_Check(perms)) {
337
xs_set_error(EINVAL);
341
xsperms_n = PyList_Size(perms);
342
/* NB. alloc +1 so we can change the owner if necessary. */
343
xsperms = calloc(xsperms_n + 1, sizeof(struct xs_permissions));
345
xs_set_error(ENOMEM);
349
tuple0 = PyTuple_New(0);
353
for (i = 0; i < xsperms_n; i++) {
354
/* Read/write perms. Set these. */
355
int p_read = 0, p_write = 0;
356
PyObject *p = PyList_GetItem(perms, i);
357
if (!PyArg_ParseTupleAndKeywords(tuple0, p, perm_spec, perm_names,
358
&xsperms[i].id, &p_read, &p_write))
361
xsperms[i].perms |= XS_PERM_READ;
363
xsperms[i].perms |= XS_PERM_WRITE;
367
* Is the caller trying to restrict access to the first specified
368
* domain? If so then it cannot be owner, so we force dom0 as owner.
370
if (xsperms_n && xsperms[0].perms && xsperms[0].id) {
371
memmove(&xsperms[1], &xsperms[0], xsperms_n * sizeof(*xsperms));
372
xsperms[0].id = xsperms[0].perms = 0;
376
Py_BEGIN_ALLOW_THREADS
377
result = xs_set_permissions(xh, th, path, xsperms, xsperms_n);
380
PyErr_SetFromErrno(xs_error);
393
#define xspy_watch_doc "\n" \
394
"Watch a path, get notifications when it changes.\n" \
395
" path [string] : xenstore path.\n" \
396
" token [string] : returned in watch notification.\n" \
398
"Returns None on success.\n" \
399
"Raises xen.lowlevel.xs.Error on error.\n" \
402
/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
403
#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
405
static PyObject *xspy_watch(XsHandle *self, PyObject *args)
407
struct xs_handle *xh = xshandle(self);
410
char token_str[MAX_STRLEN(unsigned long) + 1];
416
if (!PyArg_ParseTuple(args, "sO", &path, &token))
419
/* Note that we have to store the watch token in the xs->watches list
420
before registering the watch with xs_watch, otherwise this function
421
races with xs_read_watch.
424
for (i = 0; i < PyList_Size(self->watches); i++) {
425
if (PyList_GetItem(self->watches, i) == Py_None) {
426
PySequence_SetItem(self->watches, i, token);
430
if (i == PyList_Size(self->watches))
431
PyList_Append(self->watches, token);
433
snprintf(token_str, sizeof(token_str), "%li", (unsigned long)token);
434
Py_BEGIN_ALLOW_THREADS
435
result = xs_watch(xh, path, token_str);
439
remove_watch(self, token);
445
#define xspy_read_watch_doc "\n" \
446
"Read a watch notification.\n" \
448
"Returns: [tuple] (path, token).\n" \
449
"Raises xen.lowlevel.xs.Error on error.\n" \
452
static PyObject *xspy_read_watch(XsHandle *self, PyObject *args)
454
struct xs_handle *xh = xshandle(self);
455
PyObject *val = NULL;
465
Py_BEGIN_ALLOW_THREADS
466
xsval = xs_read_watch(xh, &num);
469
PyErr_SetFromErrno(xs_error);
472
if (sscanf(xsval[XS_WATCH_TOKEN], "%li", (unsigned long *)&token) != 1) {
473
xs_set_error(EINVAL);
476
for (i = 0; i < PyList_Size(self->watches); i++) {
477
if (token == PyList_GetItem(self->watches, i))
480
if (i == PyList_Size(self->watches)) {
481
/* We do not have a registered watch for the one that has just fired.
482
Ignore this -- a watch that has been recently deregistered can still
483
have watches in transit. This is a blocking method, so go back to
489
/* Create tuple (path, token). */
490
val = Py_BuildValue("(sO)", xsval[XS_WATCH_PATH], token);
496
#define xspy_unwatch_doc "\n" \
497
"Stop watching a path.\n" \
498
" path [string] : xenstore path.\n" \
499
" token [string] : token from the watch.\n" \
501
"Returns None on success.\n" \
502
"Raises xen.lowlevel.xs.Error on error.\n" \
505
static PyObject *xspy_unwatch(XsHandle *self, PyObject *args)
507
struct xs_handle *xh = xshandle(self);
510
char token_str[MAX_STRLEN(unsigned long) + 1];
515
if (!PyArg_ParseTuple(args, "sO", &path, &token))
518
snprintf(token_str, sizeof(token_str), "%li", (unsigned long)token);
519
Py_BEGIN_ALLOW_THREADS
520
result = xs_unwatch(xh, path, token_str);
523
remove_watch(self, token);
528
#define xspy_transaction_start_doc "\n" \
529
"Start a transaction.\n" \
531
"Returns transaction handle on success.\n" \
532
"Raises xen.lowlevel.xs.Error on error.\n" \
535
static PyObject *xspy_transaction_start(XsHandle *self)
537
struct xs_handle *xh = xshandle(self);
539
char thstr[MAX_STRLEN(unsigned long) + 1];
544
Py_BEGIN_ALLOW_THREADS
545
th = xs_transaction_start(xh);
548
if (th == XBT_NULL) {
549
PyErr_SetFromErrno(xs_error);
553
snprintf(thstr, sizeof(thstr), "%lX", (unsigned long)th);
554
return PyString_FromString(thstr);
557
#define xspy_transaction_end_doc "\n" \
558
"End the current transaction.\n" \
559
"Attempts to commit the transaction unless abort is true.\n" \
560
" abort [int]: abort flag (default 0).\n" \
562
"Returns True on success, False if you need to try again.\n" \
563
"Raises xen.lowlevel.xs.Error on error.\n" \
566
static PyObject *xspy_transaction_end(XsHandle *self, PyObject *args,
569
static char *kwd_spec[] = { "transaction", "abort", NULL };
570
static char *arg_spec = "s|i";
573
struct xs_handle *xh = xshandle(self);
581
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
585
th = strtoul(thstr, NULL, 16);
587
Py_BEGIN_ALLOW_THREADS
588
result = xs_transaction_end(xh, th, abort);
595
else if (errno == EAGAIN) {
600
PyErr_SetFromErrno(xs_error);
606
#define xspy_introduce_domain_doc "\n" \
607
"Tell xenstore about a domain so it can talk to it.\n" \
608
" dom [int] : domain id\n" \
609
" page [long] : address of domain's xenstore page\n" \
610
" port [int] : port the domain is using for xenstore\n" \
612
"Returns None on success.\n" \
613
"Raises xen.lowlevel.xs.Error on error.\n" \
616
static PyObject *xspy_introduce_domain(XsHandle *self, PyObject *args)
622
struct xs_handle *xh = xshandle(self);
627
if (!PyArg_ParseTuple(args, "ili", &dom, &page, &port))
630
Py_BEGIN_ALLOW_THREADS
631
result = xs_introduce_domain(xh, dom, page, port);
637
#define xspy_set_target_doc "\n" \
638
"Tell xenstore that a domain is targetting another one so it\n" \
639
"should let it tinker with it.\n" \
640
" dom [int] : domain id\n" \
641
" target [int] : domain id of the target\n" \
643
"Returns None on success.\n" \
644
"Raises xen.lowlevel.xs.Error on error.\n" \
647
static PyObject *xspy_set_target(XsHandle *self, PyObject *args)
652
struct xs_handle *xh = xshandle(self);
657
if (!PyArg_ParseTuple(args, "ii", &dom, &target))
660
Py_BEGIN_ALLOW_THREADS
661
result = xs_set_target(xh, dom, target);
667
#define xspy_resume_domain_doc "\n" \
668
"Tell xenstore to clear its shutdown flag for a domain.\n" \
669
"This ensures that a subsequent shutdown will fire the\n" \
670
"appropriate watches.\n" \
671
" dom [int]: domain id\n" \
673
"Returns None on success.\n" \
674
"Raises xen.lowlevel.xs.Error on error.\n"
676
static PyObject *xspy_resume_domain(XsHandle *self, PyObject *args)
680
struct xs_handle *xh = xshandle(self);
685
if (!PyArg_ParseTuple(args, "i", &dom))
688
Py_BEGIN_ALLOW_THREADS
689
result = xs_resume_domain(xh, dom);
695
#define xspy_release_domain_doc "\n" \
696
"Tell xenstore to release its channel to a domain.\n" \
697
"Unless this is done the domain will not be released.\n" \
698
" dom [int]: domain id\n" \
700
"Returns None on success.\n" \
701
"Raises xen.lowlevel.xs.Error on error.\n" \
704
static PyObject *xspy_release_domain(XsHandle *self, PyObject *args)
708
struct xs_handle *xh = xshandle(self);
713
if (!PyArg_ParseTuple(args, "i", &dom))
716
Py_BEGIN_ALLOW_THREADS
717
result = xs_release_domain(xh, dom);
724
#define xspy_close_doc "\n" \
725
"Close the connection to xenstore.\n" \
727
"Returns None on success.\n" \
728
"Raises xen.lowlevel.xs.Error on error.\n" \
731
static PyObject *xspy_close(XsHandle *self)
733
struct xs_handle *xh = xshandle(self);
739
for (i = 0; i < PyList_Size(self->watches); i++) {
740
/* TODO: xs_unwatch watches */
741
PySequence_SetItem(self->watches, i, Py_None);
752
#define xspy_get_domain_path_doc "\n" \
753
"Return store path of domain, whether or not the domain exists.\n" \
754
" domid [int]: domain id\n" \
756
"Returns: [string] domain store path.\n" \
757
"Raises xen.lowlevel.xs.Error on error.\n" \
760
static PyObject *xspy_get_domain_path(XsHandle *self, PyObject *args)
762
struct xs_handle *xh = xshandle(self);
768
if (!PyArg_ParseTuple(args, "i", &domid))
771
Py_BEGIN_ALLOW_THREADS
772
xsval = xs_get_domain_path(xh, domid);
776
PyObject *val = PyString_FromString(xsval);
781
return none(errno == ENOENT);
787
* Remove the given token from the watches list belonging to the given
788
* XsHandle, if present.
790
static void remove_watch(XsHandle *self, PyObject *token)
794
for (i = 0; i < PyList_Size(self->watches); i++) {
795
if (PyList_GetItem(self->watches, i) == token) {
796
PySequence_SetItem(self->watches, i, Py_None);
804
* Parse transaction and path arguments from the given args and kwds,
805
* convert the given self value to an xs_handle, and return all three by
808
* @return 1 on success, in which case *xh, *th, and *path are valid, or 0 on
811
static int parse_transaction_path(XsHandle *self, PyObject *args,
812
struct xs_handle **xh,
813
xs_transaction_t *th,
818
*xh = xshandle(self);
823
if (!PyArg_ParseTuple(args, "ss", &thstr, path))
826
*th = strtoul(thstr, NULL, 16);
832
static PyObject *none(bool result)
839
PyErr_SetFromErrno(xs_error);
845
#define XSPY_METH(_name, _args) { \
847
.ml_meth = (PyCFunction) xspy_ ## _name, \
849
.ml_doc = xspy_ ## _name ## _doc }
851
static PyMethodDef xshandle_methods[] = {
852
XSPY_METH(read, METH_VARARGS),
853
XSPY_METH(write, METH_VARARGS),
854
XSPY_METH(ls, METH_VARARGS),
855
XSPY_METH(mkdir, METH_VARARGS),
856
XSPY_METH(rm, METH_VARARGS),
857
XSPY_METH(get_permissions, METH_VARARGS),
858
XSPY_METH(set_permissions, METH_VARARGS),
859
XSPY_METH(watch, METH_VARARGS),
860
XSPY_METH(read_watch, METH_NOARGS),
861
XSPY_METH(unwatch, METH_VARARGS),
862
XSPY_METH(transaction_start, METH_NOARGS),
863
XSPY_METH(transaction_end, METH_VARARGS | METH_KEYWORDS),
864
XSPY_METH(introduce_domain, METH_VARARGS),
865
XSPY_METH(set_target, METH_VARARGS),
866
XSPY_METH(resume_domain, METH_VARARGS),
867
XSPY_METH(release_domain, METH_VARARGS),
868
XSPY_METH(close, METH_NOARGS),
869
XSPY_METH(get_domain_path, METH_VARARGS),
870
{ NULL /* Sentinel. */ },
873
static PyObject *xshandle_getattr(PyObject *self, char *name)
875
return Py_FindMethod(xshandle_methods, self, name);
879
xshandle_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
881
XsHandle *self = (XsHandle *)type->tp_alloc(type, 0);
887
self->watches = PyList_New(0);
891
return (PyObject *)self;
893
/* Decreasing the object's reference to 0 will result in xshandle_dealloc
900
xshandle_init(XsHandle *self, PyObject *args, PyObject *kwds)
902
static char *kwd_spec[] = { "readonly", NULL };
903
static char *arg_spec = "|i";
906
if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
910
self->xh = (readonly ? xs_daemon_open_readonly() : xs_daemon_open());
917
PyErr_SetFromErrno(xs_error);
921
static void xshandle_dealloc(XsHandle *self)
924
xs_daemon_close(self->xh);
928
Py_XDECREF(self->watches);
930
self->ob_type->tp_free((PyObject *)self);
933
static PyTypeObject xshandle_type = {
934
PyObject_HEAD_INIT(NULL)
939
(destructor)xshandle_dealloc, /* tp_dealloc */
941
xshandle_getattr, /* tp_getattr */
942
NULL, /* tp_setattr */
943
NULL, /* tp_compare */
945
NULL, /* tp_as_number */
946
NULL, /* tp_as_sequence */
947
NULL, /* tp_as_mapping */
951
NULL, /* tp_getattro */
952
NULL, /* tp_setattro */
953
NULL, /* tp_as_buffer */
954
Py_TPFLAGS_DEFAULT, /* tp_flags */
955
"Xenstore connections", /* tp_doc */
956
NULL, /* tp_traverse */
958
NULL, /* tp_richcompare */
959
0, /* tp_weaklistoffset */
961
NULL, /* tp_iternext */
962
xshandle_methods, /* tp_methods */
963
NULL, /* tp_members */
964
NULL, /* tp_getset */
967
NULL, /* tp_descr_get */
968
NULL, /* tp_descr_set */
969
0, /* tp_dictoffset */
970
(initproc)xshandle_init, /* tp_init */
972
xshandle_new, /* tp_new */
975
static PyMethodDef xs_methods[] = { { NULL } };
977
PyMODINIT_FUNC initxs(void)
981
if (PyType_Ready(&xshandle_type) < 0)
984
m = Py_InitModule(PKG, xs_methods);
989
xs_error = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL);
991
Py_INCREF(&xshandle_type);
992
PyModule_AddObject(m, CLS, (PyObject *)&xshandle_type);
995
PyModule_AddObject(m, "Error", xs_error);