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