1
Description: python: Lots of fixes in C extension
2
Fixes a lot of issues found by a code review done by Barry Warsaw.
5
- Wrong signature for getters
7
- Various optimizations
8
- More consistent return values
9
- Proper exception handling
11
Signed-off-by: Stéphane Graber <stgraber@ubuntu.com>
12
Reported-by: Barry Warsaw <barry@ubuntu.com>
13
Acked-by: Barry Warsaw <barry@ubuntu.com>
14
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
15
Author: Stéphane Graber <stgraber@ubuntu.com>
19
diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c
20
index 06d3396..85bab11 100644
21
--- a/src/python-lxc/lxc.c
22
+++ b/src/python-lxc/lxc.c
25
* python-lxc: Python bindings for LXC
27
- * (C) Copyright Canonical Ltd. 2012
28
+ * (C) Copyright Canonical Ltd. 2012-2013
31
* Stéphane Graber <stgraber@ubuntu.com>
32
@@ -34,24 +34,40 @@ typedef struct {
35
convert_tuple_to_char_pointer_array(PyObject *argv) {
36
- int argc = PyTuple_Size(argv);
37
+ int argc = PyTuple_GET_SIZE(argv);
40
char **result = (char**) malloc(sizeof(char*)*argc + 1);
42
+ if (result == NULL) {
43
+ PyErr_SetNone(PyExc_MemoryError);
47
for (i = 0; i < argc; i++) {
48
- PyObject *pyobj = PyTuple_GetItem(argv, i);
49
+ PyObject *pyobj = PyTuple_GET_ITEM(argv, i);
50
+ assert(pyobj != NULL);
54
+ PyObject *pystr = NULL;
55
if (!PyUnicode_Check(pyobj)) {
56
PyErr_SetString(PyExc_ValueError, "Expected a string");
61
pystr = PyUnicode_AsUTF8String(pyobj);
62
+ if (pystr == NULL) {
63
+ PyErr_SetString(PyExc_ValueError, "Unable to convert to UTF-8");
68
str = PyBytes_AsString(pystr);
69
+ assert(str != NULL);
71
memcpy((char *) &result[i], (char *) &str, sizeof(str));
76
@@ -62,6 +78,7 @@ convert_tuple_to_char_pointer_array(PyObject *argv) {
78
Container_dealloc(Container* self)
80
+ lxc_container_put(self->container);
81
Py_TYPE(self)->tp_free((PyObject*)self);
84
@@ -80,18 +97,27 @@ Container_init(Container *self, PyObject *args, PyObject *kwds)
86
static char *kwlist[] = {"name", "config_path", NULL};
88
+ PyObject *fs_config_path = NULL;
89
char *config_path = NULL;
91
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist,
92
- &name, &config_path))
93
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O&", kwlist,
95
+ PyUnicode_FSConverter, &fs_config_path))
98
+ if (fs_config_path != NULL) {
99
+ config_path = PyBytes_AS_STRING(fs_config_path);
100
+ assert(config_path != NULL);
103
self->container = lxc_container_new(name, config_path);
104
if (!self->container) {
105
- fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, name);
106
+ Py_XDECREF(fs_config_path);
107
+ fprintf(stderr, "%d: error creating container %s\n", __LINE__, name);
111
+ Py_XDECREF(fs_config_path);
115
@@ -109,13 +135,14 @@ LXC_get_version(PyObject *self, PyObject *args)
117
// Container properties
119
-Container_config_file_name(Container *self, PyObject *args, PyObject *kwds)
120
+Container_config_file_name(Container *self, void *closure)
122
- return PyUnicode_FromString(self->container->config_file_name(self->container));
123
+ return PyUnicode_FromString(
124
+ self->container->config_file_name(self->container));
128
-Container_defined(Container *self, PyObject *args, PyObject *kwds)
129
+Container_defined(Container *self, void *closure)
131
if (self->container->is_defined(self->container)) {
133
@@ -125,19 +152,19 @@ Container_defined(Container *self, PyObject *args, PyObject *kwds)
137
-Container_init_pid(Container *self, PyObject *args, PyObject *kwds)
138
+Container_init_pid(Container *self, void *closure)
140
- return Py_BuildValue("i", self->container->init_pid(self->container));
141
+ return PyLong_FromLong(self->container->init_pid(self->container));
145
-Container_name(Container *self, PyObject *args, PyObject *kwds)
146
+Container_name(Container *self, void *closure)
148
return PyUnicode_FromString(self->container->name);
152
-Container_running(Container *self, PyObject *args, PyObject *kwds)
153
+Container_running(Container *self, void *closure)
155
if (self->container->is_running(self->container)) {
157
@@ -147,7 +174,7 @@ Container_running(Container *self, PyObject *args, PyObject *kwds)
161
-Container_state(Container *self, PyObject *args, PyObject *kwds)
162
+Container_state(Container *self, void *closure)
164
return PyUnicode_FromString(self->container->state(self->container));
166
@@ -159,9 +186,9 @@ Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds)
167
static char *kwlist[] = {"key", NULL};
170
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
171
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
176
if (self->container->clear_config_item(self->container, key)) {
178
@@ -175,25 +202,40 @@ Container_create(Container *self, PyObject *args, PyObject *kwds)
180
char* template_name = NULL;
181
char** create_args = {NULL};
182
- PyObject *vargs = NULL;
183
+ PyObject *retval = NULL, *vargs = NULL;
184
static char *kwlist[] = {"template", "args", NULL};
186
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
187
&template_name, &vargs))
191
- if (vargs && PyTuple_Check(vargs)) {
192
- create_args = convert_tuple_to_char_pointer_array(vargs);
193
- if (!create_args) {
195
+ if (PyTuple_Check(vargs)) {
196
+ create_args = convert_tuple_to_char_pointer_array(vargs);
197
+ if (!create_args) {
202
+ PyErr_SetString(PyExc_ValueError, "args needs to be a tuple");
207
- if (self->container->create(self->container, template_name, create_args)) {
209
+ if (self->container->create(self->container, template_name, create_args))
215
+ /* We cannot have gotten here unless vargs was given and create_args
216
+ * was successfully allocated.
227
@@ -222,23 +264,33 @@ Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds)
228
static char *kwlist[] = {"key", NULL};
231
+ PyObject *ret = NULL;
233
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
234
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
239
len = self->container->get_cgroup_item(self->container, key, NULL, 0);
244
+ PyErr_SetString(PyExc_KeyError, "Invalid cgroup entry");
248
char* value = (char*) malloc(sizeof(char)*len + 1);
249
- if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) {
252
+ return PyErr_NoMemory();
254
+ if (self->container->get_cgroup_item(self->container,
255
+ key, value, len + 1) != len) {
256
+ PyErr_SetString(PyExc_ValueError, "Unable to read config value");
261
- return PyUnicode_FromString(value);
262
+ ret = PyUnicode_FromString(value);
268
@@ -247,29 +299,40 @@ Container_get_config_item(Container *self, PyObject *args, PyObject *kwds)
269
static char *kwlist[] = {"key", NULL};
272
+ PyObject *ret = NULL;
274
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
279
len = self->container->get_config_item(self->container, key, NULL, 0);
284
+ PyErr_SetString(PyExc_KeyError, "Invalid configuration key");
288
char* value = (char*) malloc(sizeof(char)*len + 1);
289
- if (self->container->get_config_item(self->container, key, value, len + 1) != len) {
292
+ return PyErr_NoMemory();
294
+ if (self->container->get_config_item(self->container,
295
+ key, value, len + 1) != len) {
296
+ PyErr_SetString(PyExc_ValueError, "Unable to read config value");
301
- return PyUnicode_FromString(value);
302
+ ret = PyUnicode_FromString(value);
308
Container_get_config_path(Container *self, PyObject *args, PyObject *kwds)
310
- return PyUnicode_FromString(self->container->get_config_path(self->container));
311
+ return PyUnicode_FromString(
312
+ self->container->get_config_path(self->container));
316
@@ -278,39 +341,57 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds)
317
static char *kwlist[] = {"key", NULL};
320
+ PyObject *ret = NULL;
322
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
327
len = self->container->get_keys(self->container, key, NULL, 0);
332
+ PyErr_SetString(PyExc_KeyError, "Invalid configuration key");
336
char* value = (char*) malloc(sizeof(char)*len + 1);
337
- if (self->container->get_keys(self->container, key, value, len + 1) != len) {
340
+ return PyErr_NoMemory();
342
+ if (self->container->get_keys(self->container,
343
+ key, value, len + 1) != len) {
344
+ PyErr_SetString(PyExc_ValueError, "Unable to read config keys");
349
- return PyUnicode_FromString(value);
350
+ ret = PyUnicode_FromString(value);
356
Container_load_config(Container *self, PyObject *args, PyObject *kwds)
358
static char *kwlist[] = {"path", NULL};
359
+ PyObject *fs_path = NULL;
362
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
365
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist,
366
+ PyUnicode_FSConverter, &fs_path))
369
+ if (fs_path != NULL) {
370
+ path = PyBytes_AS_STRING(fs_path);
371
+ assert(path != NULL);
374
if (self->container->load_config(self->container, path)) {
375
+ Py_XDECREF(fs_path);
379
+ Py_XDECREF(fs_path);
383
@@ -318,16 +399,24 @@ static PyObject *
384
Container_save_config(Container *self, PyObject *args, PyObject *kwds)
386
static char *kwlist[] = {"path", NULL};
387
+ PyObject *fs_path = NULL;
390
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
393
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist,
394
+ PyUnicode_FSConverter, &fs_path))
397
+ if (fs_path != NULL) {
398
+ path = PyBytes_AS_STRING(fs_path);
399
+ assert(path != NULL);
402
if (self->container->save_config(self->container, path)) {
403
+ Py_XDECREF(fs_path);
407
+ Py_XDECREF(fs_path);
411
@@ -338,9 +427,9 @@ Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds)
415
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist,
416
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
421
if (self->container->set_cgroup_item(self->container, key, value)) {
423
@@ -356,9 +445,9 @@ Container_set_config_item(Container *self, PyObject *args, PyObject *kwds)
427
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist,
428
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
433
if (self->container->set_config_item(self->container, key, value)) {
435
@@ -373,9 +462,9 @@ Container_set_config_path(Container *self, PyObject *args, PyObject *kwds)
436
static char *kwlist[] = {"path", NULL};
439
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
440
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
445
if (self->container->set_config_path(self->container, path)) {
447
@@ -392,7 +481,7 @@ Container_shutdown(Container *self, PyObject *args, PyObject *kwds)
449
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
454
if (self->container->shutdown(self->container, timeout)) {
456
@@ -405,13 +494,13 @@ static PyObject *
457
Container_start(Container *self, PyObject *args, PyObject *kwds)
459
char** init_args = {NULL};
460
- PyObject *useinit = NULL, *vargs = NULL;
461
+ PyObject *useinit = NULL, *retval = NULL, *vargs = NULL;
462
int init_useinit = 0;
463
static char *kwlist[] = {"useinit", "cmd", NULL};
465
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
470
if (useinit && useinit == Py_True) {
472
@@ -426,11 +515,20 @@ Container_start(Container *self, PyObject *args, PyObject *kwds)
474
self->container->want_daemonize(self->container);
476
- if (self->container->start(self->container, init_useinit, init_args)) {
478
+ if (self->container->start(self->container, init_useinit, init_args))
484
+ /* We cannot have gotten here unless vargs was given and create_args
485
+ * was successfully allocated.
496
@@ -462,7 +560,7 @@ Container_wait(Container *self, PyObject *args, PyObject *kwds)
498
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
503
if (self->container->wait(self->container, state, timeout)) {
505
@@ -473,125 +571,143 @@ Container_wait(Container *self, PyObject *args, PyObject *kwds)
507
static PyGetSetDef Container_getseters[] = {
509
- (getter)Container_config_file_name, 0,
510
+ (getter)Container_config_file_name, NULL,
511
"Path to the container configuration",
514
- (getter)Container_defined, 0,
515
+ (getter)Container_defined, NULL,
516
"Boolean indicating whether the container configuration exists",
519
- (getter)Container_init_pid, 0,
520
+ (getter)Container_init_pid, NULL,
521
"PID of the container's init process in the host's PID namespace",
524
- (getter)Container_name, 0,
525
+ (getter)Container_name, NULL,
529
- (getter)Container_running, 0,
530
+ (getter)Container_running, NULL,
531
"Boolean indicating whether the container is running or not",
534
- (getter)Container_state, 0,
535
+ (getter)Container_state, NULL,
538
{NULL, NULL, NULL, NULL, NULL}
541
static PyMethodDef Container_methods[] = {
542
- {"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS | METH_KEYWORDS,
543
+ {"clear_config_item", (PyCFunction)Container_clear_config_item,
544
+ METH_VARARGS|METH_KEYWORDS,
545
"clear_config_item(key) -> boolean\n"
547
"Clear the current value of a config key."
549
- {"create", (PyCFunction)Container_create, METH_VARARGS | METH_KEYWORDS,
550
+ {"create", (PyCFunction)Container_create,
551
+ METH_VARARGS|METH_KEYWORDS,
552
"create(template, args = (,)) -> boolean\n"
554
"Create a new rootfs for the container, using the given template "
555
"and passing some optional arguments to it."
557
- {"destroy", (PyCFunction)Container_destroy, METH_NOARGS,
558
+ {"destroy", (PyCFunction)Container_destroy,
560
"destroy() -> boolean\n"
562
"Destroys the container."
564
- {"freeze", (PyCFunction)Container_freeze, METH_NOARGS,
565
+ {"freeze", (PyCFunction)Container_freeze,
567
"freeze() -> boolean\n"
569
"Freezes the container and returns its return code."
571
- {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS | METH_KEYWORDS,
572
+ {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item,
573
+ METH_VARARGS|METH_KEYWORDS,
574
"get_cgroup_item(key) -> string\n"
576
"Get the current value of a cgroup entry."
578
- {"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS | METH_KEYWORDS,
579
+ {"get_config_item", (PyCFunction)Container_get_config_item,
580
+ METH_VARARGS|METH_KEYWORDS,
581
"get_config_item(key) -> string\n"
583
"Get the current value of a config key."
585
- {"get_config_path", (PyCFunction)Container_get_config_path, METH_NOARGS,
586
+ {"get_config_path", (PyCFunction)Container_get_config_path,
588
"get_config_path() -> string\n"
590
"Return the LXC config path (where the containers are stored)."
592
- {"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS | METH_KEYWORDS,
593
+ {"get_keys", (PyCFunction)Container_get_keys,
594
+ METH_VARARGS|METH_KEYWORDS,
595
"get_keys(key) -> string\n"
597
"Get a list of valid sub-keys for a key."
599
- {"load_config", (PyCFunction)Container_load_config, METH_VARARGS | METH_KEYWORDS,
600
+ {"load_config", (PyCFunction)Container_load_config,
601
+ METH_VARARGS|METH_KEYWORDS,
602
"load_config(path = DEFAULT) -> boolean\n"
604
"Read the container configuration from its default "
605
"location or from an alternative location if provided."
607
- {"save_config", (PyCFunction)Container_save_config, METH_VARARGS | METH_KEYWORDS,
608
+ {"save_config", (PyCFunction)Container_save_config,
609
+ METH_VARARGS|METH_KEYWORDS,
610
"save_config(path = DEFAULT) -> boolean\n"
612
"Save the container configuration to its default "
613
"location or to an alternative location if provided."
615
- {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS | METH_KEYWORDS,
616
+ {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item,
617
+ METH_VARARGS|METH_KEYWORDS,
618
"set_cgroup_item(key, value) -> boolean\n"
620
"Set a cgroup entry to the provided value."
622
- {"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS | METH_KEYWORDS,
623
+ {"set_config_item", (PyCFunction)Container_set_config_item,
624
+ METH_VARARGS|METH_KEYWORDS,
625
"set_config_item(key, value) -> boolean\n"
627
"Set a config key to the provided value."
629
- {"set_config_path", (PyCFunction)Container_set_config_path, METH_VARARGS | METH_KEYWORDS,
630
+ {"set_config_path", (PyCFunction)Container_set_config_path,
631
+ METH_VARARGS|METH_KEYWORDS,
632
"set_config_path(path) -> boolean\n"
634
"Set the LXC config path (where the containers are stored)."
636
- {"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS | METH_KEYWORDS,
637
+ {"shutdown", (PyCFunction)Container_shutdown,
638
+ METH_VARARGS|METH_KEYWORDS,
639
"shutdown(timeout = -1) -> boolean\n"
641
"Sends SIGPWR to the container and wait for it to shutdown "
642
"unless timeout is set to a positive value, in which case "
643
"the container will be killed when the timeout is reached."
645
- {"start", (PyCFunction)Container_start, METH_VARARGS | METH_KEYWORDS,
646
+ {"start", (PyCFunction)Container_start,
647
+ METH_VARARGS|METH_KEYWORDS,
648
"start(useinit = False, cmd = (,)) -> boolean\n"
650
"Start the container, optionally using lxc-init and "
651
"an alternate init command, then returns its return code."
653
- {"stop", (PyCFunction)Container_stop, METH_NOARGS,
654
+ {"stop", (PyCFunction)Container_stop,
656
"stop() -> boolean\n"
658
"Stop the container and returns its return code."
660
- {"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS,
661
+ {"unfreeze", (PyCFunction)Container_unfreeze,
663
"unfreeze() -> boolean\n"
665
"Unfreezes the container and returns its return code."
667
- {"wait", (PyCFunction)Container_wait, METH_VARARGS | METH_KEYWORDS,
668
+ {"wait", (PyCFunction)Container_wait,
669
+ METH_VARARGS|METH_KEYWORDS,
670
"wait(state, timeout = -1) -> boolean\n"
672
"Wait for the container to reach a given state or timeout."