# QMK Keyboard Guidelines Since starting, QMK has grown by leaps and bounds thanks to people like you who contribute to creating and maintaining our community keyboards. As we've grown we've discovered some patterns that work well, and ask that you conform to them to make it easier for other people to benefit from your hard work. ## Naming Your Keyboard/Project All keyboard names are in lower case, consisting only of letters, numbers, and underscore (`_`). Names may not begin with an underscore. Forward slash (`/`) is used as a sub-folder separation character. The names `test`, `keyboard`, and `all` are reserved for make commands and may not be used as a keyboard or subfolder name. Valid Examples: * `412_64` * `chimera_ortho` * `clueboard/66/rev3` * `planck` * `v60_type_r` ## Sub-folders QMK uses sub-folders both for organization and to share code between revisions of the same keyboard. You can nest folders up to 4 levels deep: qmk_firmware/keyboards/top_folder/sub_1/sub_2/sub_3/sub_4 If a sub-folder has a `rules.mk` file it will be considered a compilable keyboard. It will be available in QMK Configurator and tested with `make all`. If you are using a folder to organize several keyboards from the same maker you should not have a `rules.mk` file. Example: Clueboard uses sub-folders for both purposes, organization and keyboard revisions. * [`qmk_firmware`](https://github.com/qmk/qmk_firmware/tree/master) * [`keyboards`](https://github.com/qmk/qmk_firmware/tree/master/keyboards) * [`clueboard`](https://github.com/qmk/qmk_firmware/tree/master/keyboards/clueboard) ← This is the organization folder, there's no `rules.mk` file * [`60`](https://github.com/qmk/qmk_firmware/tree/master/keyboards/clueboard/60) ← This is a compilable keyboard, it has a `rules.mk` file * [`66`](https://github.com/qmk/qmk_firmware/tree/master/keyboards/clueboard/66) ← This is also compilable- it uses `DEFAULT_FOLDER` to specify `rev3` as the default revision * [`rev1`](https://github.com/qmk/qmk_firmware/tree/master/keyboards/clueboard/66/rev1) ← compilable: `make clueboard/66/rev1` * [`rev2`](https://github.com/qmk/qmk_firmware/tree/master/keyboards/clueboard/66/rev2) ← compilable: `make clueboard/66/rev2` * [`rev3`](https://github.com/qmk/qmk_firmware/tree/master/keyboards/clueboard/66/rev3) ← compilable: `make clueboard/66/rev3` or `make clueboard/66` ## Keyboard Folder Structure Your keyboard should be located in `qmk_firmware/keyboards/` and the folder name should be your keyboard's name as described in the previous section. Inside this folder should be several files: * `readme.md` * `info.json` * `config.h` * `rules.mk` * `.c` * `.h` ### `readme.md` All projects need to have a `readme.md` file that explains what the keyboard is, who made it and where it's available. If applicable, it should also contain links to more information, such as the maker's website. Please follow the [published template](documentation_templates.md#keyboard-readmemd-template). ### `info.json` This file is used by the [QMK API](https://github.com/qmk/qmk_api). It contains the information [QMK Configurator](https://config.qmk.fm/) needs to display a representation of your keyboard. You can also set metadata here. For more information see the [reference page](reference_info_json.md). ### `config.h` All projects need to have a `config.h` file that sets things like the matrix size, product name, USB VID/PID, description and other settings. In general, use this file to set essential information and defaults for your keyboard that will always work. ### `rules.mk` The presence of this file means that the folder is a keyboard target and can be used in `make` commands. This is where you setup the build environment for your keyboard and configure the default set of features. ### `` This is where you will write custom code for your keyboard. Typically you will write code to initialize and interface with the hardware in your keyboard. If your keyboard consists of only a key matrix with no LEDs, speakers, or other auxiliary hardware this file can be blank. The following functions are typically defined in this file: * `void matrix_init_kb(void)` * `void matrix_scan_kb(void)` * `bool process_record_kb(uint16_t keycode, keyrecord_t *record)` * `void led_set_kb(uint8_t usb_led)` ### `` This file is used to define the matrix for your keyboard. You should define at least one C macro which translates an array into a matrix representing the physical switch matrix for your keyboard. If it's possible to build your keyboard with multiple layouts you should define additional macros. If you have only a single layout you should call this macro `LAYOUT`. When defining multiple layouts you should have a base layout, named `LAYOUT_all`, that supports all possible switch positions on your matrix, even if that layout is impossible to build physically. This is the macro you should use in your `default` keymap. You should then have additional keymaps named `default_` that use your other layout macros. This will make it easier for people to use the layouts you define. Layout macro names are entirely lowercase, except for the word `LAYOUT` at the front. As an example, if you have a 60% PCB that supports ANSI and ISO you might define the following layouts and keymaps: | Layout Name | Keymap Name | Description | |-------------|-------------|-------------| | LAYOUT_all | default | A layout that supports both ISO and ANSI | | LAYOUT_ansi | default_ansi | An ANSI layout | | LAYOUT_iso | default_iso | An ISO layout | ## Image/Hardware Files In an effort to keep the repo size down we're no longer accepting binary files of any format, with few exceptions. Hosting them elsewhere (such as ) and linking them in the `readme.md` is preferred. Hardware files (such as plates, cases, pcb) can be contributed to the [qmk.fm repo](https://github.com/qmk/qmk.fm) and they will be made available on [qmk.fm](http://qmk.fm). Downloadable files are stored in `//` (name follows the same format as above) which are served at `http://qmk.fm//`, and pages are generated from `/_pages//` which are served at the same location (.md files are generated into .html files through Jekyll). Check out the `lets_split` folder for an example. ## Keyboard Defaults Given the amount of functionality that QMK exposes it's very easy to confuse new users. When putting together the default firmware for your keyboard we recommend limiting your enabled features and options to the minimal set needed to support your hardware. Recommendations for specific features follow. ### Bootmagic and Command [Bootmagic](feature_bootmagic.md) and [Command](feature_command.md) are two related features that allow a user to control their keyboard in non-obvious ways. We recommend you think long and hard about if you're going to enable either feature, and how you will expose this functionality. Keep in mind that users who want this functionality can enable it in their personal keymaps without affecting all the novice users who may be using your keyboard as their first programmable board. By far the most common problem new users encounter is accidentally triggering Bootmagic while they're plugging in their keyboard. They're holding the keyboard by the bottom, unknowingly pressing in alt and spacebar, and then they find that these keys have been swapped on them. We recommend leaving this feature disabled by default, but if you do turn it on consider setting `BOOTMAGIC_KEY_SALT` to a key that is hard to press while plugging your keyboard in. If your keyboard does not have 2 shift keys you should provide a working default for `IS_COMMAND`, even when you have set `COMMAND_ENABLE = no`. This will give your users a default to conform to if they do enable Command. ## Custom Keyboard Programming As documented on [Customizing Functionality](custom_quantum_functions.md) you can define custom functions for your keyboard. Please keep in mind that your users may want to customize that behavior as well, and make it possible for them to do that. If you are providing a custom function, for example `process_record_kb()`, make sure that your function calls the `_user()` version of the call too. You should also take into account the return value of the `_user()` version, and only run your custom code if the user returns `true`. ## Non-Production/Handwired Projects We're happy to accept any project that uses QMK, including prototypes and handwired ones, but we have a separate `/keyboards/handwired/` folder for them, so the main `/keyboards/` folder doesn't get overcrowded. If a prototype project becomes a production project at some point in the future, we'd be happy to move it to the main `/keyboards/` folder! ## Warnings as Errors When developing your keyboard, keep in mind that all warnings will be treated as errors - these small warnings can build-up and cause larger errors down the road (and keeping them is generally a bad practice). ## Copyright Blurb If you're adapting your keyboard's setup from another project, but not using the same code, but sure to update the copyright header at the top of the files to show your name, in this format: Copyright 2017 Your Name If you are modifying someone else's code and have made only trivial changes you should leave their name in the copyright statement. If you have done significant work on the file you should add your name to theirs, like so: Copyright 2017 Their Name Your Name The year should be the first year the file is created. If work was done to that file in later years you can reflect that by appending the second year to the first, like so: Copyright 2015-2017 Your Name ## License The core of QMK is licensed under the [GNU General Public License](https://www.gnu.org/licenses/licenses.en.html). If you are shipping binaries for AVR processors you may choose either [GPLv2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) or [GPLv3](https://www.gnu.org/licenses/gpl.html). If you are shipping binaries for ARM processors you must choose [GPL Version 3](https://www.gnu.org/licenses/gpl.html) to comply with the [ChibiOS](http://www.chibios.org) GPLv3 license. If your keyboard makes use of the [uGFX](https://ugfx.io) features within QMK you must comply with the [uGFX License](https://ugfx.io/license.html), which requires a separate commercial license before selling a device containing uGFX. ## Technical Details If you're looking for more information on making your keyboard work with QMK, [check out the hardware section](hardware.md)! n213' href='#n213'>213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
/* 
 * Python interface to the Xen Store Daemon.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Copyright (C) 2005 Mike Wray Hewlett-Packard
 * Copyright (C) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
 *
 */

#include <Python.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "xs.h"

/** @file
 * Python interface to the Xen Store Daemon (xs).
 */

/* Needed for Python versions earlier than 2.3. */
#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC DL_EXPORT(void)
#endif

#define PYPKG    "xen.lowlevel.xs"

/** Python wrapper round an xs handle.
 */
typedef struct XsHandle {
    PyObject_HEAD;
    struct xs_handle *xh;
    PyObject *watches;
} XsHandle;

static inline struct xs_handle *xshandle(PyObject *self)
{
    struct xs_handle *xh = ((XsHandle*)self)->xh;
    if (!xh)
        PyErr_SetString(PyExc_RuntimeError, "invalid xenstore daemon handle");
    return xh;
}

static inline PyObject *pyvalue_int(int val) {
    return (val
            ? PyInt_FromLong(val)
            : PyErr_SetFromErrno(PyExc_RuntimeError));
}

static inline PyObject *pyvalue_str(char *val) {
    return (val
            ? PyString_FromString(val)
            : PyErr_SetFromErrno(PyExc_RuntimeError));
}

#define xspy_read_doc "\n"			\
	"Read data from a path.\n"		\
	" path [string]: xenstore path\n"	\
	"\n"					\
	"Returns: [string] data read.\n"	\
	"         None if key doesn't exist.\n"	\
	"Raises RuntimeError on error.\n"	\
	"\n"

static PyObject *xspy_read(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", NULL };
    static char *arg_spec = "ss";
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    char *xsval = NULL;
    unsigned int xsval_n = 0;
    PyObject *val = NULL;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    xsval = xs_read(xh, th, path, &xsval_n);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        if (errno == ENOENT) {
            Py_INCREF(Py_None);
            val = Py_None;
        } else
            PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    val = PyString_FromStringAndSize(xsval, xsval_n);
 exit:
    if (xsval)
        free(xsval);
    return val;
}

#define xspy_write_doc "\n"					\
	"Write data to a path.\n"				\
	" path   [string] : xenstore path to write to\n."	\
	" data   [string] : data to write.\n"			\
	"\n"							\
	"Returns None on success.\n"				\
	"Raises RuntimeError on error.\n"			\
	"\n"

static PyObject *xspy_write(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", "data", NULL };
    static char *arg_spec = "sss#";
    char *path = NULL;
    char *data = NULL;
    int data_n = 0;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path, &data, &data_n))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    xsval = xs_write(xh, th, path, data, data_n);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_ls_doc "\n"					\
	"List a directory.\n"					\
	" path [string]: path to list.\n"			\
	"\n"							\
	"Returns: [string array] list of subdirectory names.\n"	\
	"         None if key doesn't exist.\n"			\
	"Raises RuntimeError on error.\n"			\
	"\n"

static PyObject *xspy_ls(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", NULL };
    static char *arg_spec = "ss";
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    char **xsval = NULL;
    unsigned int xsval_n = 0;
    int i;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path))
        goto exit;


    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    xsval = xs_directory(xh, th, path, &xsval_n);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        if (errno == ENOENT) {
            Py_INCREF(Py_None);
            val = Py_None;
        } else
            PyErr_SetFromErrno(PyExc_RuntimeError);
	goto exit;
    }
    val = PyList_New(xsval_n);
    for (i = 0; i < xsval_n; i++)
        PyList_SetItem(val, i, PyString_FromString(xsval[i]));
    free(xsval);
 exit:
    return val;
}

#define xspy_mkdir_doc "\n"					\
	"Make a directory.\n"					\
	" path [string]: path to directory to create.\n"	\
	"\n"							\
	"Returns None on success.\n"				\
	"Raises RuntimeError on error.\n"			\
	"\n"

static PyObject *xspy_mkdir(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", NULL };
    static char *arg_spec = "ss";
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    xsval = xs_mkdir(xh, th, path);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_rm_doc "\n"			\
	"Remove a path.\n"			\
	" path [string] : path to remove\n"	\
	"\n"					\
	"Returns None on success.\n"		\
	"Raises RuntimeError on error.\n"	\
	"\n"

static PyObject *xspy_rm(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", NULL };
    static char *arg_spec = "ss";
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    xsval = xs_rm(xh, th, path);
    Py_END_ALLOW_THREADS
    if (!xsval && errno != ENOENT) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_get_permissions_doc "\n"		\
	"Get the permissions for a path\n"	\
	" path [string]: xenstore path.\n"	\
	"\n"					\
	"Returns: permissions array.\n"		\
	"Raises RuntimeError on error.\n"	\
	"\n"

static PyObject *xspy_get_permissions(PyObject *self, PyObject *args,
                                      PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", NULL };
    static char *arg_spec = "ss";
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    struct xs_permissions *perms;
    unsigned int perms_n = 0;
    int i;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    perms = xs_get_permissions(xh, th, path, &perms_n);
    Py_END_ALLOW_THREADS
    if (!perms) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    val = PyList_New(perms_n);
    for (i = 0; i < perms_n; i++, perms++) {
        PyObject *p = Py_BuildValue("{s:i,s:i,s:i}",
                                    "dom",   perms->id,
                                    "read",  (perms->perms & XS_PERM_READ),
                                    "write",  (perms->perms & XS_PERM_WRITE));
        PyList_SetItem(val, i, p);
    }
 exit:
    return val;
}

#define xspy_set_permissions_doc "\n"		\
	"Set the permissions for a path\n"	\
	" path  [string] : xenstore path.\n"	\
	" perms          : permissions.\n"	\
	"\n"					\
	"Returns None on success.\n"		\
	"Raises RuntimeError on error.\n"	\
	"\n"

static PyObject *xspy_set_permissions(PyObject *self, PyObject *args,
                                      PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "path", "perms", NULL };
    static char *arg_spec = "ssO";
    char *path = NULL;
    PyObject *perms = NULL;
    static char *perm_names[] = { "dom", "read", "write", NULL };
    static char *perm_spec = "i|iiii";

    struct xs_handle *xh = xshandle(self);
    int i, xsval;
    struct xs_permissions *xsperms = NULL;
    int xsperms_n = 0;
    PyObject *tuple0 = NULL;
    PyObject *val = NULL;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &path, &perms))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    if (!PyList_Check(perms)) {
        PyErr_SetString(PyExc_RuntimeError, "perms must be a list");
        goto exit;
    }
    xsperms_n = PyList_Size(perms);
    xsperms = calloc(xsperms_n, sizeof(struct xs_permissions));
    if (!xsperms) {
        PyErr_SetString(PyExc_RuntimeError, "out of memory");
        goto exit;
    }
    tuple0 = PyTuple_New(0);
    if (!tuple0)
        goto exit;
    for (i = 0; i < xsperms_n; i++) {
        /* Domain the permissions apply to. */
        int dom = 0;
        /* Read/write perms. Set these. */
        int p_read = 0, p_write = 0;
        PyObject *p = PyList_GetItem(perms, i);
        if (!PyArg_ParseTupleAndKeywords(tuple0, p, perm_spec, perm_names,
                                         &dom, &p_read, &p_write))
            goto exit;
        xsperms[i].id = dom;
        if (p_read)
            xsperms[i].perms |= XS_PERM_READ;
        if (p_write)
            xsperms[i].perms |= XS_PERM_WRITE;
    }
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_set_permissions(xh, th, path, xsperms, xsperms_n);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    Py_XDECREF(tuple0);
    if (xsperms)
        free(xsperms);
    return val;
}

#define xspy_watch_doc "\n"						\
	"Watch a path, get notifications when it changes.\n"		\
	" path     [string] : xenstore path.\n"				\
	" token    [string] : returned in watch notification.\n"	\
	"\n"								\
	"Returns None on success.\n"					\
	"Raises RuntimeError on error.\n"				\
	"\n"

/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)

static PyObject *xspy_watch(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "path", "token", NULL };
    static char *arg_spec = "sO";
    char *path = NULL;
    PyObject *token;
    char token_str[MAX_STRLEN(unsigned long) + 1];
    int i;

    XsHandle *xsh = (XsHandle *)self;
    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, 
                                     &path, &token))
        goto exit;
    Py_INCREF(token);
    sprintf(token_str, "%li", (unsigned long)token);
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_watch(xh, path, token_str);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        Py_DECREF(token);
        goto exit;
    }

    for (i = 0; i < PyList_Size(xsh->watches); i++) {
        if (PyList_GetItem(xsh->watches, i) == Py_None) {
            PyList_SetItem(xsh->watches, i, token);
            break;
        }
    }
    if (i == PyList_Size(xsh->watches))
        PyList_Append(xsh->watches, token);
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_read_watch_doc "\n"				\
	"Read a watch notification.\n"				\
	"\n"							\
	"Returns: [tuple] (path, token).\n"			\
	"Raises RuntimeError on error.\n"			\
	"\n"

static PyObject *xspy_read_watch(PyObject *self, PyObject *args,
                                 PyObject *kwds)
{
    static char *kwd_spec[] = { NULL };
    static char *arg_spec = "";

    XsHandle *xsh = (XsHandle *)self;
    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    char **xsval = NULL;
    PyObject *token;
    int i;
    unsigned int num;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
        goto exit;
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_read_watch(xh, &num);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    if (sscanf(xsval[XS_WATCH_TOKEN], "%li", (unsigned long *)&token) != 1) {
        PyErr_SetString(PyExc_RuntimeError, "invalid token");
        goto exit;
    }
    for (i = 0; i < PyList_Size(xsh->watches); i++) {
        if (token == PyList_GetItem(xsh->watches, i))
            break;
    }
    if (i == PyList_Size(xsh->watches)) {
        PyErr_SetString(PyExc_RuntimeError, "invalid token");
        goto exit;
    }
    /* Create tuple (path, token). */
    val = Py_BuildValue("(sO)", xsval[XS_WATCH_PATH], token);
 exit:
    if (xsval)
        free(xsval);
    return val;
}

#define xspy_unwatch_doc "\n"				\
	"Stop watching a path.\n"			\
	" path  [string] : xenstore path.\n"		\
	" token [string] : token from the watch.\n"	\
	"\n"						\
	"Returns None on success.\n"			\
	"Raises RuntimeError on error.\n"		\
	"\n"

static PyObject *xspy_unwatch(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "path", "token", NULL };
    static char *arg_spec = "sO";
    char *path = NULL;
    PyObject *token;
    char token_str[MAX_STRLEN(unsigned long) + 1];
    int i;

    XsHandle *xsh = (XsHandle *)self;
    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path,
                                     &token))
        goto exit;
    sprintf(token_str, "%li", (unsigned long)token);
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_unwatch(xh, path, token_str);
    Py_END_ALLOW_THREADS
    if (!xsval)
        PyErr_SetFromErrno(PyExc_RuntimeError);
    else {
        Py_INCREF(Py_None);
        val = Py_None;
    }
    for (i = 0; i < PyList_Size(xsh->watches); i++) {
        if (token == PyList_GetItem(xsh->watches, i)) {
            Py_INCREF(Py_None);
            PyList_SetItem(xsh->watches, i, Py_None);
            break;
        }
    }
 exit:
    return val;
}

#define xspy_transaction_start_doc "\n"				\
	"Start a transaction.\n"				\
	"\n"							\
	"Returns transaction handle on success.\n"		\
	"Raises RuntimeError on error.\n"			\
	"\n"

static PyObject *xspy_transaction_start(PyObject *self, PyObject *args,
                                        PyObject *kwds)
{
    static char *kwd_spec[] = { NULL };
    static char *arg_spec = "";
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    struct xs_transaction_handle *th;
    char thstr[20];

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &path))
        goto exit;
    Py_BEGIN_ALLOW_THREADS
    th = xs_transaction_start(xh);
    Py_END_ALLOW_THREADS
    if (th == NULL) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }

    sprintf(thstr, "%lX", (unsigned long)th);
    val = PyString_FromString(thstr);
 exit:
    return val;
}

#define xspy_transaction_end_doc "\n"					\
	"End the current transaction.\n"				\
	"Attempts to commit the transaction unless abort is true.\n"	\
	" abort [int]: abort flag (default 0).\n"			\
	"\n"								\
	"Returns True on success, False if you need to try again.\n"	\
	"Raises RuntimeError on error.\n"				\
	"\n"

static PyObject *xspy_transaction_end(PyObject *self, PyObject *args,
                                      PyObject *kwds)
{
    static char *kwd_spec[] = { "transaction", "abort", NULL };
    static char *arg_spec = "s|i";
    int abort = 0;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    struct xs_transaction_handle *th;
    char *thstr;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &thstr, &abort))
        goto exit;

    th = (struct xs_transaction_handle *)strtoul(thstr, NULL, 16);

    Py_BEGIN_ALLOW_THREADS
    xsval = xs_transaction_end(xh, th, abort);
    Py_END_ALLOW_THREADS
    if (!xsval) {
	if (errno == EAGAIN) {
	    Py_INCREF(Py_False);
	    val = Py_False;
	    goto exit;
	}
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_True);
    val = Py_True;
 exit:
    return val;
}

#define xspy_introduce_domain_doc "\n"					\
	"Tell xenstore about a domain so it can talk to it.\n"		\
	" dom  [int]   : domain id\n"					\
	" page [long]  : address of domain's xenstore page\n"		\
	" port [int]   : port the domain is using for xenstore\n"	\
	" path [string]: path to the domain's data in xenstore\n"	\
	"\n"								\
	"Returns None on success.\n"					\
	"Raises RuntimeError on error.\n"				\
	"\n"

static PyObject *xspy_introduce_domain(PyObject *self, PyObject *args,
                                       PyObject *kwds)
{
    static char *kwd_spec[] = { "dom", "page", "port", "path", NULL };
    static char *arg_spec = "iiis|";
    domid_t dom = 0;
    unsigned long page = 0;
    unsigned int port = 0;
    char *path = NULL;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &dom, &page, &port, &path))
        goto exit;
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_introduce_domain(xh, dom, page, port, path);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_release_domain_doc "\n"					\
	"Tell xenstore to release its channel to a domain.\n"		\
	"Unless this is done the domain will not be released.\n"	\
	" dom [int]: domain id\n"					\
	"\n"								\
	"Returns None on success.\n"					\
	"Raises RuntimeError on error.\n"				\
	"\n"

static PyObject *xspy_release_domain(PyObject *self, PyObject *args,
                                     PyObject *kwds)
{
    static char *kwd_spec[] = { "dom", NULL };
    static char *arg_spec = "i|";
    domid_t dom;

    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;
    int xsval = 0;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &dom))
        goto exit;
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_release_domain(xh, dom);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_close_doc "\n"			\
	"Close the connection to xenstore.\n"	\
	"\n"					\
	"Returns None on success.\n"		\
	"Raises RuntimeError on error.\n"	\
	"\n"

static PyObject *xspy_close(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { NULL };
    static char *arg_spec = "";
    int i;

    XsHandle *xsh = (XsHandle *)self;
    struct xs_handle *xh = xshandle(self);
    PyObject *val = NULL;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec))
        goto exit;
    for (i = 0; i < PyList_Size(xsh->watches); i++) {
        /* TODO: xs_unwatch watches */
        Py_INCREF(Py_None);
        PyList_SetItem(xsh->watches, i, Py_None);
    }
    xs_daemon_close(xh);
    xsh->xh = NULL;
    Py_INCREF(Py_None);
    val = Py_None;
 exit:
    return val;
}

#define xspy_get_domain_path_doc "\n"			\
	"Return store path of domain.\n"		\
	" domid [int]: domain id\n"			\
	"\n"						\
	"Returns: [string] domain store path.\n"	\
	"         None if domid doesn't exist.\n"	\
	"Raises RuntimeError on error.\n"		\
	"\n"

static PyObject *xspy_get_domain_path(PyObject *self, PyObject *args,
				      PyObject *kwds)
{
    static char *kwd_spec[] = { "domid", NULL };
    static char *arg_spec = "i";
    int domid = 0;

    struct xs_handle *xh = xshandle(self);
    char *xsval = NULL;
    PyObject *val = NULL;

    if (!xh)
        goto exit;
    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &domid))
        goto exit;
    Py_BEGIN_ALLOW_THREADS
    xsval = xs_get_domain_path(xh, domid);
    Py_END_ALLOW_THREADS
    if (!xsval) {
        if (errno == ENOENT) {
            Py_INCREF(Py_None);
            val = Py_None;
        } else
            PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    val = PyString_FromString(xsval);
    free(xsval);
 exit:
    return val;
}

#define XSPY_METH(_name) {			\
    .ml_name  = #_name,				\
    .ml_meth  = (PyCFunction) xspy_ ## _name,	\
    .ml_flags = (METH_VARARGS | METH_KEYWORDS),	\
    .ml_doc   = xspy_ ## _name ## _doc }

static PyMethodDef xshandle_methods[] = {
     XSPY_METH(read),
     XSPY_METH(write),
     XSPY_METH(ls),
     XSPY_METH(mkdir),
     XSPY_METH(rm),
     XSPY_METH(get_permissions),
     XSPY_METH(set_permissions),
     XSPY_METH(watch),
     XSPY_METH(read_watch),
     XSPY_METH(unwatch),
     XSPY_METH(transaction_start),
     XSPY_METH(transaction_end),
     XSPY_METH(introduce_domain),
     XSPY_METH(release_domain),
     XSPY_METH(close),
     XSPY_METH(get_domain_path),
     { /* Terminator. */ },
};

static PyObject *xshandle_getattr(PyObject *self, char *name)
{
    PyObject *val = NULL;
    val = Py_FindMethod(xshandle_methods, self, name);
    return val;
}

static void xshandle_dealloc(PyObject *self)
{
    XsHandle *xh = (XsHandle*)self;
    if (xh->xh) {
        xs_daemon_close(xh->xh);
        xh->xh = NULL;
    }
    PyObject_Del(self);
}

static PyTypeObject xshandle_type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "xshandle",
    sizeof(XsHandle),
    0,
    xshandle_dealloc,   /* tp_dealloc     */
    NULL,               /* tp_print       */
    xshandle_getattr,   /* tp_getattr     */
    NULL,               /* tp_setattr     */
    NULL,               /* tp_compare     */
    NULL,               /* tp_repr        */
    NULL,               /* tp_as_number   */
    NULL,               /* tp_as_sequence */
    NULL,               /* tp_as_mapping  */
    NULL                /* tp_hash        */
};

static PyObject *xshandle_open(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwd_spec[] = { "readonly", NULL };
    static char *arg_spec = "|i";
    int readonly = 0;

    XsHandle *xsh = NULL;
    PyObject *val = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec,
                                     &readonly))
        return NULL;

    xsh = PyObject_New(XsHandle, &xshandle_type);
    if (!xsh)
        return NULL;
    xsh->watches = PyList_New(0);
    if (!xsh->watches)
        goto exit;
    xsh->xh = (readonly ? xs_daemon_open_readonly() : xs_daemon_open());
    if (!xsh->xh) {
        Py_DECREF(xsh->watches);
        PyErr_SetFromErrno(PyExc_RuntimeError);
        goto exit;
    }
    val = (PyObject *)xsh;
    return val;
 exit:
    PyObject_Del(xsh);
    return NULL;
}

static PyMethodDef xs_methods[] = {
    { .ml_name  = "open",
      .ml_meth  = (PyCFunction)xshandle_open,
      .ml_flags = (METH_VARARGS | METH_KEYWORDS), 
      .ml_doc   = "\n"
      "Open a connection to the xenstore daemon.\n"
      "Returns: xs connection object.\n"
      "Raises RuntimeError on error.\n"
      "\n"
    },
    { /* Terminator. */ }
};

PyMODINIT_FUNC initxs (void)
{
    PyObject *module;

    module = Py_InitModule(PYPKG, xs_methods);
}