1 /*
2  * Python interface to the Xen Store Daemon.
3  *
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.
7  *
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.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Copyright (C) 2005 Mike Wray Hewlett-Packard
17  * Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
18  * Copyright (C) 2005 XenSource Ltd.
19  */
20 
21 #define PY_SSIZE_T_CLEAN
22 #include <Python.h>
23 
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 
33 #include <xenstore.h>
34 
35 /** @file
36  * Python interface to the Xen Store Daemon (xs).
37  */
38 
39 /* Needed for Python versions earlier than 2.3. */
40 #ifndef PyMODINIT_FUNC
41 #define PyMODINIT_FUNC DL_EXPORT(void)
42 #endif
43 
44 #define PKG "xen.lowlevel.xs"
45 #define CLS "xs"
46 
47 #if PY_MAJOR_VERSION < 3
48 /* Python 2 compatibility */
49 #define PyLong_FromLong PyInt_FromLong
50 #undef PyLong_Check
51 #define PyLong_Check PyInt_Check
52 #define PyLong_AsLong PyInt_AsLong
53 #endif
54 
55 static PyObject *xs_error;
56 
57 /** Python wrapper round an xs handle.
58  */
59 typedef struct XsHandle {
60     PyObject_HEAD;
61     struct xs_handle *xh;
62     PyObject *watches;
63 } XsHandle;
64 
xs_set_error(int value)65 static void xs_set_error(int value)
66 {
67 	errno = value;
68 	PyErr_SetFromErrno(xs_error);
69 }
70 
xshandle(XsHandle * self)71 static inline struct xs_handle *xshandle(XsHandle *self)
72 {
73     struct xs_handle *xh = self->xh;
74     if (!xh)
75 	xs_set_error(EINVAL);
76     return xh;
77 }
78 
79 static void remove_watch(XsHandle *xsh, PyObject *token);
80 
81 static PyObject *match_watch_by_token(XsHandle *self, char **xsval);
82 
83 static PyObject *none(bool result);
84 
85 static int parse_transaction_path(XsHandle *self, PyObject *args,
86                                   struct xs_handle **xh,
87                                   xs_transaction_t *th,
88                                   char **path);
89 
90 
91 #define xspy_read_doc "\n"                              \
92 	"Read data from a path.\n"                      \
93 	" transaction [string]: transaction handle\n"	\
94 	" path [string]:        xenstore path\n"	\
95 	"\n"                                            \
96 	"Returns: [string] data read.\n"                \
97 	"         None if key doesn't exist.\n"         \
98 	"Raises xen.lowlevel.xs.Error on error.\n"               \
99 	"\n"
100 
xspy_read(XsHandle * self,PyObject * args)101 static PyObject *xspy_read(XsHandle *self, PyObject *args)
102 {
103     struct xs_handle *xh;
104     xs_transaction_t th;
105     char *path;
106 
107     char *xsval;
108     unsigned int xsval_n;
109 
110     if (!parse_transaction_path(self, args, &xh, &th, &path))
111         return NULL;
112 
113     Py_BEGIN_ALLOW_THREADS
114     xsval = xs_read(xh, th, path, &xsval_n);
115     Py_END_ALLOW_THREADS
116     if (xsval) {
117         PyObject *val = PyBytes_FromStringAndSize(xsval, xsval_n);
118         free(xsval);
119         return val;
120     }
121     else {
122         return none(errno == ENOENT);
123     }
124 }
125 
126 
127 #define xspy_write_doc "\n"					\
128 	"Write data to a path.\n"				\
129 	" transaction [string]: transaction handle\n"           \
130 	" path   [string] : xenstore path to write to\n."	\
131 	" data   [string] : data to write.\n"			\
132 	"\n"							\
133 	"Returns None on success.\n"				\
134 	"Raises xen.lowlevel.xs.Error on error.\n"			\
135 	"\n"
136 
xspy_write(XsHandle * self,PyObject * args)137 static PyObject *xspy_write(XsHandle *self, PyObject *args)
138 {
139     static char *arg_spec = "sss#";
140     struct xs_handle *xh = xshandle(self);
141     xs_transaction_t th;
142     char *thstr;
143     char *path;
144     char *data;
145     Py_ssize_t data_n;
146     bool result;
147 
148     if (!xh)
149         return NULL;
150     if (!PyArg_ParseTuple(args, arg_spec, &thstr, &path, &data, &data_n))
151         return NULL;
152 
153     th = strtoul(thstr, NULL, 16);
154 
155     Py_BEGIN_ALLOW_THREADS
156     result = xs_write(xh, th, path, data, data_n);
157     Py_END_ALLOW_THREADS
158 
159     return none(result);
160 }
161 
162 
163 #define xspy_ls_doc "\n"					\
164 	"List a directory.\n"					\
165 	" transaction [string]: transaction handle\n"           \
166 	" path [string]:        path to list.\n"                \
167 	"\n"							\
168 	"Returns: [string array] list of subdirectory names.\n"	\
169 	"         None if key doesn't exist.\n"			\
170 	"Raises xen.lowlevel.xs.Error on error.\n"			\
171 	"\n"
172 
xspy_ls(XsHandle * self,PyObject * args)173 static PyObject *xspy_ls(XsHandle *self, PyObject *args)
174 {
175     struct xs_handle *xh;
176     xs_transaction_t th;
177     char *path;
178 
179     char **xsval;
180     unsigned int xsval_n;
181 
182     if (!parse_transaction_path(self, args, &xh, &th, &path))
183         return NULL;
184 
185     Py_BEGIN_ALLOW_THREADS
186     xsval = xs_directory(xh, th, path, &xsval_n);
187     Py_END_ALLOW_THREADS
188 
189     if (xsval) {
190         size_t i;
191         PyObject *val = PyList_New(xsval_n);
192         for (i = 0; i < xsval_n; i++)
193 #if PY_MAJOR_VERSION >= 3
194             PyList_SetItem(val, i, PyUnicode_FromString(xsval[i]));
195 #else
196             PyList_SetItem(val, i, PyBytes_FromString(xsval[i]));
197 #endif
198         free(xsval);
199         return val;
200     }
201     else {
202         return none(errno == ENOENT);
203     }
204 }
205 
206 
207 #define xspy_mkdir_doc "\n"					\
208 	"Make a directory.\n"					\
209 	" transaction [string]: transaction handle.\n"		\
210 	" path [string]       : path to directory to create.\n"	\
211 	"\n"							\
212 	"Returns None on success.\n"				\
213 	"Raises xen.lowlevel.xs.Error on error.\n"			\
214 	"\n"
215 
xspy_mkdir(XsHandle * self,PyObject * args)216 static PyObject *xspy_mkdir(XsHandle *self, PyObject *args)
217 {
218     struct xs_handle *xh;
219     xs_transaction_t th;
220     char *path;
221 
222     bool result;
223 
224     if (!parse_transaction_path(self, args, &xh, &th, &path))
225         return NULL;
226 
227     Py_BEGIN_ALLOW_THREADS
228     result = xs_mkdir(xh, th, path);
229     Py_END_ALLOW_THREADS
230 
231     return none(result);
232 }
233 
234 
235 #define xspy_rm_doc "\n"                                \
236 	"Remove a path.\n"                              \
237 	" transaction [string]: transaction handle\n"	\
238 	" path [string] : path to remove\n"             \
239 	"\n"                                            \
240 	"Returns None on success.\n"                    \
241 	"Raises xen.lowlevel.xs.Error on error.\n"               \
242 	"\n"
243 
xspy_rm(XsHandle * self,PyObject * args)244 static PyObject *xspy_rm(XsHandle *self, PyObject *args)
245 {
246     struct xs_handle *xh;
247     xs_transaction_t th;
248     char *path;
249 
250     bool result;
251 
252     if (!parse_transaction_path(self, args, &xh, &th, &path))
253         return NULL;
254 
255     Py_BEGIN_ALLOW_THREADS
256     result = xs_rm(xh, th, path);
257     Py_END_ALLOW_THREADS
258 
259     return none(result || errno == ENOENT);
260 }
261 
262 
263 #define xspy_get_permissions_doc "\n"                   \
264 	"Get the permissions for a path\n"              \
265 	" transaction [string]: transaction handle\n"	\
266 	" path [string]:        xenstore path.\n"       \
267 	"\n"                                            \
268 	"Returns: permissions array.\n"                 \
269 	"Raises xen.lowlevel.xs.Error on error.\n"               \
270 	"\n"
271 
xspy_get_permissions(XsHandle * self,PyObject * args)272 static PyObject *xspy_get_permissions(XsHandle *self, PyObject *args)
273 {
274     static char *arg_spec = "ss";
275     char *path = NULL;
276 
277     struct xs_handle *xh = xshandle(self);
278     struct xs_permissions *perms;
279     unsigned int perms_n = 0;
280     size_t i;
281 
282     xs_transaction_t th;
283     char *thstr;
284 
285     if (!xh)
286         return NULL;
287     if (!PyArg_ParseTuple(args, arg_spec, &thstr, &path))
288         return NULL;
289 
290     th = strtoul(thstr, NULL, 16);
291 
292     Py_BEGIN_ALLOW_THREADS
293     perms = xs_get_permissions(xh, th, path, &perms_n);
294     Py_END_ALLOW_THREADS
295 
296     if (perms) {
297         PyObject *val = PyList_New(perms_n);
298         for (i = 0; i < perms_n; i++) {
299             PyObject *p =
300                 Py_BuildValue("{s:i,s:i,s:i}",
301                               "dom",   perms[i].id,
302                               "read",  perms[i].perms & XS_PERM_READ,
303                               "write", perms[i].perms & XS_PERM_WRITE);
304             PyList_SetItem(val, i, p);
305         }
306 
307         free(perms);
308         return val;
309     }
310     else {
311         PyErr_SetFromErrno(xs_error);
312         return NULL;
313     }
314 }
315 
316 #define xspy_set_permissions_doc "\n"                   \
317 	"Set the permissions for a path\n"              \
318 	" transaction [string]: transaction handle\n"	\
319 	" path  [string]      : xenstore path.\n"	\
320 	" perms               : permissions.\n"         \
321 	"\n"                                            \
322 	"Returns None on success.\n"                    \
323 	"Raises xen.lowlevel.xs.Error on error.\n"               \
324 	"\n"
325 
xspy_set_permissions(XsHandle * self,PyObject * args)326 static PyObject *xspy_set_permissions(XsHandle *self, PyObject *args)
327 {
328     char *path;
329     PyObject *perms;
330     static char *perm_names[] = { "dom", "read", "write", NULL };
331     static char *perm_spec = "i|ii";
332 
333     struct xs_handle *xh = xshandle(self);
334     int i, result;
335     struct xs_permissions *xsperms = NULL;
336     int xsperms_n;
337     PyObject *tuple0 = NULL;
338 
339     xs_transaction_t th;
340     char *thstr;
341     PyObject *ret = NULL;
342 
343     if (!xh)
344         goto exit;
345     if (!PyArg_ParseTuple(args, "ssO", &thstr, &path, &perms))
346         goto exit;
347 
348     th = strtoul(thstr, NULL, 16);
349 
350     if (!PyList_Check(perms)) {
351 	xs_set_error(EINVAL);
352         goto exit;
353     }
354 
355     xsperms_n = PyList_Size(perms);
356     /* NB. alloc +1 so we can change the owner if necessary. */
357     xsperms = calloc(xsperms_n + 1, sizeof(struct xs_permissions));
358     if (!xsperms) {
359 	xs_set_error(ENOMEM);
360         goto exit;
361     }
362 
363     tuple0 = PyTuple_New(0);
364     if (!tuple0)
365         goto exit;
366 
367     for (i = 0; i < xsperms_n; i++) {
368         /* Read/write perms. Set these. */
369         int p_read = 0, p_write = 0;
370         PyObject *p = PyList_GetItem(perms, i);
371         if (!PyArg_ParseTupleAndKeywords(tuple0, p, perm_spec, perm_names,
372                                          &xsperms[i].id, &p_read, &p_write))
373             goto exit;
374         if (p_read)
375             xsperms[i].perms |= XS_PERM_READ;
376         if (p_write)
377             xsperms[i].perms |= XS_PERM_WRITE;
378     }
379 
380     /*
381      * Is the caller trying to restrict access to the first specified
382      * domain? If so then it cannot be owner, so we force dom0 as owner.
383      */
384     if (xsperms_n && xsperms[0].perms && xsperms[0].id) {
385         memmove(&xsperms[1], &xsperms[0], xsperms_n * sizeof(*xsperms));
386         xsperms[0].id = xsperms[0].perms = 0;
387         xsperms_n++;
388     }
389 
390     Py_BEGIN_ALLOW_THREADS
391     result = xs_set_permissions(xh, th, path, xsperms, xsperms_n);
392     Py_END_ALLOW_THREADS
393     if (!result) {
394         PyErr_SetFromErrno(xs_error);
395         goto exit;
396     }
397 
398     Py_INCREF(Py_None);
399     ret = Py_None;
400 
401  exit:
402     Py_XDECREF(tuple0);
403     free(xsperms);
404     return ret;
405 }
406 
407 #define xspy_watch_doc "\n"						\
408 	"Watch a path, get notifications when it changes.\n"		\
409 	" path     [string] : xenstore path.\n"				\
410 	" token    [string] : returned in watch notification.\n"	\
411 	"\n"								\
412 	"Returns None on success.\n"					\
413 	"Raises xen.lowlevel.xs.Error on error.\n"				\
414 	"\n"
415 
416 /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
417 #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
418 
xspy_watch(XsHandle * self,PyObject * args)419 static PyObject *xspy_watch(XsHandle *self, PyObject *args)
420 {
421     struct xs_handle *xh = xshandle(self);
422     char *path;
423     PyObject *token;
424     char token_str[MAX_STRLEN(unsigned long) + 1];
425     int result;
426     int i;
427 
428     if (!xh)
429         return NULL;
430     if (!PyArg_ParseTuple(args, "sO", &path, &token))
431         return NULL;
432 
433     /* Note that we have to store the watch token in the xs->watches list
434        before registering the watch with xs_watch, otherwise this function
435        races with xs_read_watch.
436     */
437 
438     for (i = 0; i < PyList_Size(self->watches); i++) {
439         if (PyList_GetItem(self->watches, i) == Py_None) {
440             PySequence_SetItem(self->watches, i, token);
441             break;
442         }
443     }
444     if (i == PyList_Size(self->watches))
445         PyList_Append(self->watches, token);
446 
447     snprintf(token_str, sizeof(token_str), "%li", (unsigned long)token);
448     Py_BEGIN_ALLOW_THREADS
449     result = xs_watch(xh, path, token_str);
450     Py_END_ALLOW_THREADS
451 
452     if (!result)
453         remove_watch(self, token);
454 
455     return none(result);
456 }
457 
458 
459 #define xspy_fileno_doc "\n"                              \
460 	"Return the FD to poll for notifications when watches fire.\n"   \
461 	"Returns: [int] file descriptor.\n"                \
462 	"\n"
463 
xspy_fileno(XsHandle * self)464 static PyObject *xspy_fileno(XsHandle *self)
465 {
466     struct xs_handle *xh = xshandle(self);
467     int fd;
468 
469     if (!xh)
470         return NULL;
471 
472     fd = xs_fileno(xh);
473 
474     return PyLong_FromLong(fd);
475 }
476 
477 
478 #define xspy_check_watch_doc "\n"				\
479 	"Check for watch notifications without blocking.\n"	\
480 	"\n"							\
481 	"Returns: [tuple] (path, token).\n"			\
482 	"         None if no watches have fired.\n"             \
483 	"Raises xen.lowlevel.xs.Error on error.\n"	        \
484 	"\n"
485 
xspy_check_watch(XsHandle * self,PyObject * args)486 static PyObject *xspy_check_watch(XsHandle *self, PyObject *args)
487 {
488     struct xs_handle *xh = xshandle(self);
489     PyObject *val = NULL;
490     char **xsval;
491 
492     if (!xh)
493         return NULL;
494 
495     xsval = xs_check_watch(xh);
496     if (!xsval) {
497         return none(errno == EAGAIN);
498     }
499 
500     val = match_watch_by_token(self, xsval);
501     free(xsval);
502     return val;
503 }
504 
505 #define xspy_read_watch_doc "\n"				\
506 	"Read a watch notification.\n"				\
507 	"\n"							\
508 	"Returns: [tuple] (path, token).\n"			\
509 	"Raises xen.lowlevel.xs.Error on error.\n"			\
510 	"\n"
511 
xspy_read_watch(XsHandle * self,PyObject * args)512 static PyObject *xspy_read_watch(XsHandle *self, PyObject *args)
513 {
514     struct xs_handle *xh = xshandle(self);
515     PyObject *val = NULL;
516     char **xsval;
517     unsigned int num;
518 
519     if (!xh)
520         return NULL;
521 
522 again:
523     Py_BEGIN_ALLOW_THREADS
524     xsval = xs_read_watch(xh, &num);
525     Py_END_ALLOW_THREADS
526     if (!xsval) {
527         PyErr_SetFromErrno(xs_error);
528         return val;
529     }
530 
531     val = match_watch_by_token(self, xsval);
532     free(xsval);
533 
534     if (!val && errno == EAGAIN) {
535         PyErr_Clear();
536         goto again;
537     }
538 
539     return val;
540 }
541 
542 #define xspy_unwatch_doc "\n"				\
543 	"Stop watching a path.\n"			\
544 	" path  [string] : xenstore path.\n"		\
545 	" token [string] : token from the watch.\n"	\
546 	"\n"						\
547 	"Returns None on success.\n"			\
548 	"Raises xen.lowlevel.xs.Error on error.\n"		\
549 	"\n"
550 
xspy_unwatch(XsHandle * self,PyObject * args)551 static PyObject *xspy_unwatch(XsHandle *self, PyObject *args)
552 {
553     struct xs_handle *xh = xshandle(self);
554     char *path;
555     PyObject *token;
556     char token_str[MAX_STRLEN(unsigned long) + 1];
557     int result;
558 
559     if (!xh)
560         return NULL;
561     if (!PyArg_ParseTuple(args, "sO", &path, &token))
562         return NULL;
563 
564     snprintf(token_str, sizeof(token_str), "%li", (unsigned long)token);
565     Py_BEGIN_ALLOW_THREADS
566     result = xs_unwatch(xh, path, token_str);
567     Py_END_ALLOW_THREADS
568 
569     remove_watch(self, token);
570 
571     return none(result);
572 }
573 
574 #define xspy_transaction_start_doc "\n"				\
575 	"Start a transaction.\n"				\
576 	"\n"							\
577 	"Returns transaction handle on success.\n"		\
578 	"Raises xen.lowlevel.xs.Error on error.\n"			\
579 	"\n"
580 
xspy_transaction_start(XsHandle * self)581 static PyObject *xspy_transaction_start(XsHandle *self)
582 {
583     struct xs_handle *xh = xshandle(self);
584     xs_transaction_t th;
585     char thstr[MAX_STRLEN(unsigned long) + 1];
586 
587     if (!xh)
588         return NULL;
589 
590     Py_BEGIN_ALLOW_THREADS
591     th = xs_transaction_start(xh);
592     Py_END_ALLOW_THREADS
593 
594     if (th == XBT_NULL) {
595         PyErr_SetFromErrno(xs_error);
596         return NULL;
597     }
598 
599     snprintf(thstr, sizeof(thstr), "%lX", (unsigned long)th);
600 #if PY_MAJOR_VERSION >= 3
601     return PyUnicode_FromString(thstr);
602 #else
603     return PyBytes_FromString(thstr);
604 #endif
605 }
606 
607 #define xspy_transaction_end_doc "\n"					\
608 	"End the current transaction.\n"				\
609 	"Attempts to commit the transaction unless abort is true.\n"	\
610 	" transaction [string] : transaction handle.\n"			\
611 	" abort [int]          : abort flag (default 0).\n"		\
612 	"\n"								\
613 	"Returns True on success, False if you need to try again.\n"	\
614 	"Raises xen.lowlevel.xs.Error on error.\n"				\
615 	"\n"
616 
xspy_transaction_end(XsHandle * self,PyObject * args,PyObject * kwds)617 static PyObject *xspy_transaction_end(XsHandle *self, PyObject *args,
618                                       PyObject *kwds)
619 {
620     static char *kwd_spec[] = { "transaction", "abort", NULL };
621     static char *arg_spec = "s|i";
622     int abort = 0;
623 
624     struct xs_handle *xh = xshandle(self);
625     bool result;
626 
627     xs_transaction_t th;
628     char *thstr;
629 
630     if (!xh)
631         return NULL;
632     if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
633                                      &thstr, &abort))
634         return NULL;
635 
636     th = strtoul(thstr, NULL, 16);
637 
638     Py_BEGIN_ALLOW_THREADS
639     result = xs_transaction_end(xh, th, abort);
640     Py_END_ALLOW_THREADS
641 
642     if (result) {
643         Py_INCREF(Py_True);
644         return Py_True;
645     }
646     else if (errno == EAGAIN) {
647         Py_INCREF(Py_False);
648         return Py_False;
649     }
650     else {
651         PyErr_SetFromErrno(xs_error);
652         return NULL;
653     }
654 }
655 
656 
657 #define xspy_introduce_domain_doc "\n"					\
658 	"Tell xenstore about a domain so it can talk to it.\n"		\
659 	" dom  [int]   : domain id\n"					\
660 	" page [long]  : address of domain's xenstore page\n"		\
661 	" port [int]   : port the domain is using for xenstore\n"	\
662 	"\n"								\
663 	"Returns None on success.\n"					\
664 	"Raises xen.lowlevel.xs.Error on error.\n"				\
665 	"\n"
666 
xspy_introduce_domain(XsHandle * self,PyObject * args)667 static PyObject *xspy_introduce_domain(XsHandle *self, PyObject *args)
668 {
669     uint32_t dom;
670     unsigned long page;
671     unsigned int port;
672 
673     struct xs_handle *xh = xshandle(self);
674     bool result = 0;
675 
676     if (!xh)
677         return NULL;
678     if (!PyArg_ParseTuple(args, "ili", &dom, &page, &port))
679         return NULL;
680 
681     Py_BEGIN_ALLOW_THREADS
682     result = xs_introduce_domain(xh, dom, page, port);
683     Py_END_ALLOW_THREADS
684 
685     return none(result);
686 }
687 
688 #define xspy_set_target_doc "\n"					\
689         "Tell xenstore that a domain is targetting another one so it\n" \
690         "should let it tinker with it.\n"	                        \
691 	" dom    [int]   : domain id\n"					\
692 	" target [int]   : domain id of the target\n"			\
693 	"\n"								\
694 	"Returns None on success.\n"					\
695 	"Raises xen.lowlevel.xs.Error on error.\n"			\
696 	"\n"
697 
xspy_set_target(XsHandle * self,PyObject * args)698 static PyObject *xspy_set_target(XsHandle *self, PyObject *args)
699 {
700     uint32_t dom;
701     uint32_t target;
702 
703     struct xs_handle *xh = xshandle(self);
704     bool result = 0;
705 
706     if (!xh)
707         return NULL;
708     if (!PyArg_ParseTuple(args, "ii", &dom, &target))
709         return NULL;
710 
711     Py_BEGIN_ALLOW_THREADS
712     result = xs_set_target(xh, dom, target);
713     Py_END_ALLOW_THREADS
714 
715     return none(result);
716 }
717 
718 #define xspy_resume_domain_doc "\n"                                \
719 	"Tell xenstore to clear its shutdown flag for a domain.\n" \
720 	"This ensures that a subsequent shutdown will fire the\n"  \
721 	"appropriate watches.\n"                                   \
722 	" dom [int]: domain id\n"			           \
723         "\n"						           \
724         "Returns None on success.\n"				   \
725         "Raises xen.lowlevel.xs.Error on error.\n"
726 
xspy_resume_domain(XsHandle * self,PyObject * args)727 static PyObject *xspy_resume_domain(XsHandle *self, PyObject *args)
728 {
729     uint32_t dom;
730 
731     struct xs_handle *xh = xshandle(self);
732     bool result = 0;
733 
734     if (!xh)
735         return NULL;
736     if (!PyArg_ParseTuple(args, "i", &dom))
737         return NULL;
738 
739     Py_BEGIN_ALLOW_THREADS
740     result = xs_resume_domain(xh, dom);
741     Py_END_ALLOW_THREADS
742 
743     return none(result);
744 }
745 
746 #define xspy_release_domain_doc "\n"					\
747 	"Tell xenstore to release its channel to a domain.\n"		\
748 	"Unless this is done the domain will not be released.\n"	\
749 	" dom [int]: domain id\n"					\
750 	"\n"								\
751 	"Returns None on success.\n"					\
752 	"Raises xen.lowlevel.xs.Error on error.\n"				\
753 	"\n"
754 
xspy_release_domain(XsHandle * self,PyObject * args)755 static PyObject *xspy_release_domain(XsHandle *self, PyObject *args)
756 {
757     uint32_t dom;
758 
759     struct xs_handle *xh = xshandle(self);
760     bool result = 0;
761 
762     if (!xh)
763         return NULL;
764     if (!PyArg_ParseTuple(args, "i", &dom))
765         return NULL;
766 
767     Py_BEGIN_ALLOW_THREADS
768     result = xs_release_domain(xh, dom);
769     Py_END_ALLOW_THREADS
770 
771     return none(result);
772 }
773 
774 
775 #define xspy_close_doc "\n"			\
776 	"Close the connection to xenstore.\n"	\
777 	"\n"					\
778 	"Returns None on success.\n"		\
779 	"Raises xen.lowlevel.xs.Error on error.\n"	\
780 	"\n"
781 
xspy_close(XsHandle * self)782 static PyObject *xspy_close(XsHandle *self)
783 {
784     struct xs_handle *xh = xshandle(self);
785     int i;
786 
787     if (!xh)
788         return NULL;
789 
790     for (i = 0; i < PyList_Size(self->watches); i++) {
791         /* TODO: xs_unwatch watches */
792         PySequence_SetItem(self->watches, i, Py_None);
793     }
794 
795     xs_close(xh);
796     self->xh = NULL;
797 
798     Py_INCREF(Py_None);
799     return Py_None;
800 }
801 
802 
803 #define xspy_get_domain_path_doc "\n"			\
804 	"Return store path of domain, whether or not the domain exists.\n" \
805 	" domid [int]: domain id\n"			\
806 	"\n"						\
807 	"Returns: [string] domain store path.\n"	\
808 	"Raises xen.lowlevel.xs.Error on error.\n"		\
809 	"\n"
810 
xspy_get_domain_path(XsHandle * self,PyObject * args)811 static PyObject *xspy_get_domain_path(XsHandle *self, PyObject *args)
812 {
813     struct xs_handle *xh = xshandle(self);
814     uint32_t domid;
815     char *xsval;
816 
817     if (!xh)
818         return NULL;
819     if (!PyArg_ParseTuple(args, "i", &domid))
820         return NULL;
821 
822     Py_BEGIN_ALLOW_THREADS
823     xsval = xs_get_domain_path(xh, domid);
824     Py_END_ALLOW_THREADS
825 
826     if (xsval) {
827 #if PY_MAJOR_VERSION >= 3
828         PyObject *val = PyUnicode_FromString(xsval);
829 #else
830         PyObject *val = PyBytes_FromString(xsval);
831 #endif
832         free(xsval);
833         return val;
834     }
835     else {
836         return none(errno == ENOENT);
837     }
838 }
839 
840 
841 /**
842  * Remove the given token from the watches list belonging to the given
843  * XsHandle, if present.
844  */
remove_watch(XsHandle * self,PyObject * token)845 static void remove_watch(XsHandle *self, PyObject *token)
846 {
847     int i;
848 
849     for (i = 0; i < PyList_Size(self->watches); i++) {
850         if (PyList_GetItem(self->watches, i) == token) {
851             PySequence_SetItem(self->watches, i, Py_None);
852             return;
853         }
854     }
855 }
856 
857 
858 /**
859  * Parse transaction and path arguments from the given args and kwds,
860  * convert the given self value to an xs_handle, and return all three by
861  * reference.
862  *
863  * @return 1 on success, in which case *xh, *th, and *path are valid, or 0 on
864  * failure.
865  */
parse_transaction_path(XsHandle * self,PyObject * args,struct xs_handle ** xh,xs_transaction_t * th,char ** path)866 static int parse_transaction_path(XsHandle *self, PyObject *args,
867                                   struct xs_handle **xh,
868                                   xs_transaction_t *th,
869                                   char **path)
870 {
871     char *thstr;
872 
873     *xh = xshandle(self);
874 
875     if (!*xh)
876         return 0;
877 
878     if (!PyArg_ParseTuple(args, "ss", &thstr, path))
879         return 0;
880 
881     *th = strtoul(thstr, NULL, 16);
882 
883     return 1;
884 }
885 
886 
match_watch_by_token(XsHandle * self,char ** xsval)887 static PyObject *match_watch_by_token(XsHandle *self, char **xsval)
888 {
889     PyObject *token;
890     int i;
891 
892     if (sscanf(xsval[XS_WATCH_TOKEN], "%li", (unsigned long *)&token) != 1) {
893         xs_set_error(EINVAL);
894         return NULL;
895     }
896     for (i = 0; i < PyList_Size(self->watches); i++) {
897         if (token == PyList_GetItem(self->watches, i))
898             break;
899     }
900     if (i == PyList_Size(self->watches)) {
901         /* We do not have a registered watch for the one that has just fired.
902            Ignore this -- a watch that has been recently deregistered can still
903            have watches in transit.
904         */
905         xs_set_error(EAGAIN);
906         return NULL;
907     }
908 
909     /* Create tuple (path, token). */
910     return Py_BuildValue("(sO)", xsval[XS_WATCH_PATH], token);
911 }
912 
913 
none(bool result)914 static PyObject *none(bool result)
915 {
916     if (result) {
917         Py_INCREF(Py_None);
918         return Py_None;
919     }
920     else {
921         PyErr_SetFromErrno(xs_error);
922         return NULL;
923     }
924 }
925 
926 
927 #define XSPY_METH(_name, _args) {               \
928     .ml_name  = #_name,				\
929     .ml_meth  = (PyCFunction) xspy_ ## _name,	\
930     .ml_flags = _args,                          \
931     .ml_doc   = xspy_ ## _name ## _doc }
932 
933 static PyMethodDef xshandle_methods[] = {
934     XSPY_METH(read,              METH_VARARGS),
935     XSPY_METH(write,             METH_VARARGS),
936     XSPY_METH(ls,                METH_VARARGS),
937     XSPY_METH(mkdir,             METH_VARARGS),
938     XSPY_METH(rm,                METH_VARARGS),
939     XSPY_METH(get_permissions,   METH_VARARGS),
940     XSPY_METH(set_permissions,   METH_VARARGS),
941     XSPY_METH(watch,             METH_VARARGS),
942     XSPY_METH(read_watch,        METH_NOARGS),
943     XSPY_METH(check_watch,       METH_NOARGS),
944     XSPY_METH(unwatch,           METH_VARARGS),
945     XSPY_METH(transaction_start, METH_NOARGS),
946     XSPY_METH(transaction_end,   METH_VARARGS | METH_KEYWORDS),
947     XSPY_METH(introduce_domain,  METH_VARARGS),
948     XSPY_METH(set_target,        METH_VARARGS),
949     XSPY_METH(resume_domain,     METH_VARARGS),
950     XSPY_METH(release_domain,    METH_VARARGS),
951     XSPY_METH(close,             METH_NOARGS),
952     XSPY_METH(get_domain_path,   METH_VARARGS),
953     XSPY_METH(fileno,            METH_NOARGS),
954     { NULL /* Sentinel. */ },
955 };
956 
957 static PyObject *
xshandle_new(PyTypeObject * type,PyObject * args,PyObject * kwds)958 xshandle_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
959 {
960     XsHandle *self = (XsHandle *)type->tp_alloc(type, 0);
961 
962     if (self == NULL)
963         return NULL;
964 
965     self->xh = NULL;
966     self->watches = PyList_New(0);
967     if (!self->watches)
968         goto fail;
969 
970     return (PyObject *)self;
971 fail:
972     /* Decreasing the object's reference to 0 will result in xshandle_dealloc
973        being called. */
974     Py_DECREF(self);
975     return NULL;
976 }
977 
978 static int
xshandle_init(XsHandle * self,PyObject * args,PyObject * kwds)979 xshandle_init(XsHandle *self, PyObject *args, PyObject *kwds)
980 {
981     static char *kwd_spec[] = { "readonly", NULL };
982     static char *arg_spec = "|i";
983     int readonly = 0;
984 
985     if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
986                                      &readonly))
987         goto fail;
988 
989     self->xh = xs_open(0);
990     if (!self->xh)
991         goto fail;
992 
993     return 0;
994 
995  fail:
996     PyErr_SetFromErrno(xs_error);
997     return -1;
998 }
999 
xshandle_dealloc(XsHandle * self)1000 static void xshandle_dealloc(XsHandle *self)
1001 {
1002     if (self->xh) {
1003         xs_close(self->xh);
1004         self->xh = NULL;
1005     }
1006 
1007     Py_XDECREF(self->watches);
1008 
1009     Py_TYPE(self)->tp_free((PyObject *)self);
1010 }
1011 
1012 static PyTypeObject xshandle_type = {
1013 #if PY_MAJOR_VERSION >= 3
1014     .ob_base = { PyObject_HEAD_INIT(NULL) },
1015 #else
1016     PyObject_HEAD_INIT(NULL)
1017 #endif
1018     .tp_name = PKG "." CLS,
1019     .tp_basicsize = sizeof(XsHandle),
1020     .tp_itemsize = 0,
1021     .tp_dealloc = (destructor)xshandle_dealloc,
1022     .tp_flags = Py_TPFLAGS_DEFAULT,
1023     .tp_doc = "Xenstore connections",
1024     .tp_methods = xshandle_methods,
1025     .tp_init = (initproc)xshandle_init,
1026     .tp_new = xshandle_new,
1027 };
1028 
1029 static PyMethodDef xs_methods[] = { { NULL } };
1030 
1031 #if PY_MAJOR_VERSION >= 3
1032 static PyModuleDef xs_module = {
1033     PyModuleDef_HEAD_INIT,
1034     PKG,     /* name */
1035     NULL,   /* docstring */
1036     -1,     /* size of per-interpreter state, -1 means the module use global
1037                variables */
1038     xs_methods
1039 };
1040 #endif
1041 
1042 #if PY_MAJOR_VERSION >= 3
1043 #define INITERROR return NULL
PyInit_xs(void)1044 PyMODINIT_FUNC PyInit_xs(void)
1045 #else
1046 #define INITERROR return
1047 PyMODINIT_FUNC initxs(void)
1048 #endif
1049 {
1050     PyObject* m;
1051 
1052     if (PyType_Ready(&xshandle_type) < 0)
1053         INITERROR;
1054 
1055 #if PY_MAJOR_VERSION >= 3
1056     m = PyModule_Create(&xs_module);
1057 #else
1058     m = Py_InitModule(PKG, xs_methods);
1059 #endif
1060 
1061     if (m == NULL)
1062         INITERROR;
1063 
1064     xs_error = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL);
1065     if (xs_error == NULL) {
1066         Py_DECREF(m);
1067         INITERROR;
1068     }
1069 
1070     Py_INCREF(&xshandle_type);
1071     PyModule_AddObject(m, CLS, (PyObject *)&xshandle_type);
1072 
1073     Py_INCREF(xs_error);
1074     PyModule_AddObject(m, "Error", xs_error);
1075 #if PY_MAJOR_VERSION >= 3
1076     return m;
1077 #endif
1078 }
1079 
1080 
1081 /*
1082  * Local variables:
1083  *  mode: C
1084  *  c-basic-offset: 4
1085  * End:
1086  */
1087