]> git.proxmox.com Git - mirror_lxc.git/blob - src/python-lxc/lxc.c
2abdc4cc0036dc422191637683d4c5a3fcdc12d9
[mirror_lxc.git] / src / python-lxc / lxc.c
1 /*
2 * python-lxc: Python bindings for LXC
3 *
4 * (C) Copyright Canonical Ltd. 2012
5 *
6 * Authors:
7 * Stéphane Graber <stgraber@ubuntu.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include <Python.h>
25 #include "structmember.h"
26 #include <lxc/lxccontainer.h>
27 #include <stdio.h>
28 #include <sys/wait.h>
29
30 typedef struct {
31 PyObject_HEAD
32 struct lxc_container *container;
33 } Container;
34
35 char**
36 convert_tuple_to_char_pointer_array(PyObject *argv) {
37 int argc = PyTuple_Size(argv);
38 int i;
39
40 char **result = (char**) malloc(sizeof(char*)*argc + 1);
41
42 for (i = 0; i < argc; i++) {
43 PyObject *pyobj = PyTuple_GetItem(argv, i);
44
45 char *str = NULL;
46 PyObject *pystr;
47 if (!PyUnicode_Check(pyobj)) {
48 PyErr_SetString(PyExc_ValueError, "Expected a string");
49 return NULL;
50 }
51
52 pystr = PyUnicode_AsUTF8String(pyobj);
53 str = PyBytes_AsString(pystr);
54 memcpy((char *) &result[i], (char *) &str, sizeof(str));
55 }
56
57 result[argc] = NULL;
58
59 return result;
60 }
61
62 static void
63 Container_dealloc(Container* self)
64 {
65 Py_TYPE(self)->tp_free((PyObject*)self);
66 }
67
68 static PyObject *
69 Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
70 {
71 Container *self;
72
73 self = (Container *)type->tp_alloc(type, 0);
74
75 return (PyObject *)self;
76 }
77
78 static int
79 Container_init(Container *self, PyObject *args, PyObject *kwds)
80 {
81 static char *kwlist[] = {"name", "config_path", NULL};
82 char *name = NULL;
83 char *config_path = NULL;
84
85 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist,
86 &name, &config_path))
87 return -1;
88
89 self->container = lxc_container_new(name, config_path);
90 if (!self->container) {
91 fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, name);
92 return -1;
93 }
94
95 return 0;
96 }
97
98 // Container properties
99 static PyObject *
100 Container_config_file_name(Container *self, PyObject *args, PyObject *kwds)
101 {
102 return PyUnicode_FromString(self->container->config_file_name(self->container));
103 }
104
105 static PyObject *
106 Container_defined(Container *self, PyObject *args, PyObject *kwds)
107 {
108 if (self->container->is_defined(self->container)) {
109 Py_RETURN_TRUE;
110 }
111
112 Py_RETURN_FALSE;
113 }
114
115 static PyObject *
116 Container_init_pid(Container *self, PyObject *args, PyObject *kwds)
117 {
118 return Py_BuildValue("i", self->container->init_pid(self->container));
119 }
120
121 static PyObject *
122 Container_name(Container *self, PyObject *args, PyObject *kwds)
123 {
124 return PyUnicode_FromString(self->container->name);
125 }
126
127 static PyObject *
128 Container_running(Container *self, PyObject *args, PyObject *kwds)
129 {
130 if (self->container->is_running(self->container)) {
131 Py_RETURN_TRUE;
132 }
133
134 Py_RETURN_FALSE;
135 }
136
137 static PyObject *
138 Container_state(Container *self, PyObject *args, PyObject *kwds)
139 {
140 return PyUnicode_FromString(self->container->state(self->container));
141 }
142
143 // Container Functions
144 static PyObject *
145 Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds)
146 {
147 static char *kwlist[] = {"key", NULL};
148 char *key = NULL;
149
150 if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
151 &key))
152 Py_RETURN_FALSE;
153
154 if (self->container->clear_config_item(self->container, key)) {
155 Py_RETURN_TRUE;
156 }
157
158 Py_RETURN_FALSE;
159 }
160
161 static PyObject *
162 Container_create(Container *self, PyObject *args, PyObject *kwds)
163 {
164 char* template_name = NULL;
165 char** create_args = {NULL};
166 PyObject *vargs = NULL;
167 static char *kwlist[] = {"template", "args", NULL};
168
169 if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
170 &template_name, &vargs))
171 Py_RETURN_FALSE;
172
173 if (vargs && PyTuple_Check(vargs)) {
174 create_args = convert_tuple_to_char_pointer_array(vargs);
175 if (!create_args) {
176 return NULL;
177 }
178 }
179
180 if (self->container->create(self->container, template_name, create_args)) {
181 Py_RETURN_TRUE;
182 }
183
184 Py_RETURN_FALSE;
185 }
186
187 static PyObject *
188 Container_destroy(Container *self, PyObject *args, PyObject *kwds)
189 {
190 if (self->container->destroy(self->container)) {
191 Py_RETURN_TRUE;
192 }
193
194 Py_RETURN_FALSE;
195 }
196
197 static PyObject *
198 Container_freeze(Container *self, PyObject *args, PyObject *kwds)
199 {
200 if (self->container->freeze(self->container)) {
201 Py_RETURN_TRUE;
202 }
203
204 Py_RETURN_FALSE;
205 }
206
207 static PyObject *
208 Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds)
209 {
210 static char *kwlist[] = {"key", NULL};
211 char* key = NULL;
212 int len = 0;
213
214 if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
215 &key))
216 Py_RETURN_FALSE;
217
218 len = self->container->get_cgroup_item(self->container, key, NULL, 0);
219
220 if (len <= 0) {
221 Py_RETURN_FALSE;
222 }
223
224 char* value = (char*) malloc(sizeof(char)*len + 1);
225 if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) {
226 Py_RETURN_FALSE;
227 }
228
229 return PyUnicode_FromString(value);
230 }
231
232 static PyObject *
233 Container_get_config_item(Container *self, PyObject *args, PyObject *kwds)
234 {
235 static char *kwlist[] = {"key", NULL};
236 char* key = NULL;
237 int len = 0;
238
239 if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
240 &key))
241 Py_RETURN_FALSE;
242
243 len = self->container->get_config_item(self->container, key, NULL, 0);
244
245 if (len <= 0) {
246 Py_RETURN_FALSE;
247 }
248
249 char* value = (char*) malloc(sizeof(char)*len + 1);
250 if (self->container->get_config_item(self->container, key, value, len + 1) != len) {
251 Py_RETURN_FALSE;
252 }
253
254 return PyUnicode_FromString(value);
255 }
256
257 static PyObject *
258 Container_get_config_path(Container *self, PyObject *args, PyObject *kwds)
259 {
260 return PyUnicode_FromString(self->container->get_config_path(self->container));
261 }
262
263 static PyObject *
264 Container_get_keys(Container *self, PyObject *args, PyObject *kwds)
265 {
266 static char *kwlist[] = {"key", NULL};
267 char* key = NULL;
268 int len = 0;
269
270 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
271 &key))
272 Py_RETURN_FALSE;
273
274 len = self->container->get_keys(self->container, key, NULL, 0);
275
276 if (len <= 0) {
277 Py_RETURN_FALSE;
278 }
279
280 char* value = (char*) malloc(sizeof(char)*len + 1);
281 if (self->container->get_keys(self->container, key, value, len + 1) != len) {
282 Py_RETURN_FALSE;
283 }
284
285 return PyUnicode_FromString(value);
286 }
287
288 static PyObject *
289 Container_load_config(Container *self, PyObject *args, PyObject *kwds)
290 {
291 static char *kwlist[] = {"path", NULL};
292 char* path = NULL;
293
294 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
295 &path))
296 Py_RETURN_FALSE;
297
298 if (self->container->load_config(self->container, path)) {
299 Py_RETURN_TRUE;
300 }
301
302 Py_RETURN_FALSE;
303 }
304
305 static PyObject *
306 Container_save_config(Container *self, PyObject *args, PyObject *kwds)
307 {
308 static char *kwlist[] = {"path", NULL};
309 char* path = NULL;
310
311 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
312 &path))
313 Py_RETURN_FALSE;
314
315 if (self->container->save_config(self->container, path)) {
316 Py_RETURN_TRUE;
317 }
318
319 Py_RETURN_FALSE;
320 }
321
322 static PyObject *
323 Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds)
324 {
325 static char *kwlist[] = {"key", "value", NULL};
326 char *key = NULL;
327 char *value = NULL;
328
329 if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist,
330 &key, &value))
331 Py_RETURN_FALSE;
332
333 if (self->container->set_cgroup_item(self->container, key, value)) {
334 Py_RETURN_TRUE;
335 }
336
337 Py_RETURN_FALSE;
338 }
339
340 static PyObject *
341 Container_set_config_item(Container *self, PyObject *args, PyObject *kwds)
342 {
343 static char *kwlist[] = {"key", "value", NULL};
344 char *key = NULL;
345 char *value = NULL;
346
347 if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist,
348 &key, &value))
349 Py_RETURN_FALSE;
350
351 if (self->container->set_config_item(self->container, key, value)) {
352 Py_RETURN_TRUE;
353 }
354
355 Py_RETURN_FALSE;
356 }
357
358 static PyObject *
359 Container_set_config_path(Container *self, PyObject *args, PyObject *kwds)
360 {
361 static char *kwlist[] = {"path", NULL};
362 char *path = NULL;
363
364 if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
365 &path))
366 Py_RETURN_FALSE;
367
368 if (self->container->set_config_path(self->container, path)) {
369 Py_RETURN_TRUE;
370 }
371
372 Py_RETURN_FALSE;
373 }
374
375 static PyObject *
376 Container_shutdown(Container *self, PyObject *args, PyObject *kwds)
377 {
378 static char *kwlist[] = {"timeout", NULL};
379 int timeout = -1;
380
381 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
382 &timeout))
383 Py_RETURN_FALSE;
384
385 if (self->container->shutdown(self->container, timeout)) {
386 Py_RETURN_TRUE;
387 }
388
389 Py_RETURN_FALSE;
390 }
391
392 static PyObject *
393 Container_start(Container *self, PyObject *args, PyObject *kwds)
394 {
395 char** init_args = {NULL};
396 PyObject *useinit = NULL, *vargs = NULL;
397 int init_useinit = 0;
398 static char *kwlist[] = {"useinit", "cmd", NULL};
399
400 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
401 &useinit, &vargs))
402 Py_RETURN_FALSE;
403
404 if (useinit && useinit == Py_True) {
405 init_useinit = 1;
406 }
407
408 if (vargs && PyTuple_Check(vargs)) {
409 init_args = convert_tuple_to_char_pointer_array(vargs);
410 if (!init_args) {
411 return NULL;
412 }
413 }
414
415 self->container->want_daemonize(self->container);
416
417 if (self->container->start(self->container, init_useinit, init_args)) {
418 Py_RETURN_TRUE;
419 }
420
421 Py_RETURN_FALSE;
422 }
423
424 static PyObject *
425 Container_stop(Container *self, PyObject *args, PyObject *kwds)
426 {
427 if (self->container->stop(self->container)) {
428 Py_RETURN_TRUE;
429 }
430
431 Py_RETURN_FALSE;
432 }
433
434 static PyObject *
435 Container_unfreeze(Container *self, PyObject *args, PyObject *kwds)
436 {
437 if (self->container->unfreeze(self->container)) {
438 Py_RETURN_TRUE;
439 }
440
441 Py_RETURN_FALSE;
442 }
443
444 static PyObject *
445 Container_wait(Container *self, PyObject *args, PyObject *kwds)
446 {
447 static char *kwlist[] = {"state", "timeout", NULL};
448 char *state = NULL;
449 int timeout = -1;
450
451 if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
452 &state, &timeout))
453 Py_RETURN_FALSE;
454
455 if (self->container->wait(self->container, state, timeout)) {
456 Py_RETURN_TRUE;
457 }
458
459 Py_RETURN_FALSE;
460 }
461
462 static PyGetSetDef Container_getseters[] = {
463 {"config_file_name",
464 (getter)Container_config_file_name, 0,
465 "Path to the container configuration",
466 NULL},
467 {"defined",
468 (getter)Container_defined, 0,
469 "Boolean indicating whether the container configuration exists",
470 NULL},
471 {"init_pid",
472 (getter)Container_init_pid, 0,
473 "PID of the container's init process in the host's PID namespace",
474 NULL},
475 {"name",
476 (getter)Container_name, 0,
477 "Container name",
478 NULL},
479 {"running",
480 (getter)Container_running, 0,
481 "Boolean indicating whether the container is running or not",
482 NULL},
483 {"state",
484 (getter)Container_state, 0,
485 "Container state",
486 NULL},
487 };
488
489 static PyMethodDef Container_methods[] = {
490 {"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS | METH_KEYWORDS,
491 "clear_config_item(key) -> boolean\n"
492 "\n"
493 "Clear the current value of a config key."
494 },
495 {"create", (PyCFunction)Container_create, METH_VARARGS | METH_KEYWORDS,
496 "create(template, args = (,)) -> boolean\n"
497 "\n"
498 "Create a new rootfs for the container, using the given template "
499 "and passing some optional arguments to it."
500 },
501 {"destroy", (PyCFunction)Container_destroy, METH_NOARGS,
502 "destroy() -> boolean\n"
503 "\n"
504 "Destroys the container."
505 },
506 {"freeze", (PyCFunction)Container_freeze, METH_NOARGS,
507 "freeze() -> boolean\n"
508 "\n"
509 "Freezes the container and returns its return code."
510 },
511 {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS | METH_KEYWORDS,
512 "get_cgroup_item(key) -> string\n"
513 "\n"
514 "Get the current value of a cgroup entry."
515 },
516 {"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS | METH_KEYWORDS,
517 "get_config_item(key) -> string\n"
518 "\n"
519 "Get the current value of a config key."
520 },
521 {"get_config_path", (PyCFunction)Container_get_config_path, METH_NOARGS,
522 "get_config_path() -> string\n"
523 "\n"
524 "Return the LXC config path (where the containers are stored)."
525 },
526 {"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS | METH_KEYWORDS,
527 "get_keys(key) -> string\n"
528 "\n"
529 "Get a list of valid sub-keys for a key."
530 },
531 {"load_config", (PyCFunction)Container_load_config, METH_VARARGS | METH_KEYWORDS,
532 "load_config(path = DEFAULT) -> boolean\n"
533 "\n"
534 "Read the container configuration from its default "
535 "location or from an alternative location if provided."
536 },
537 {"save_config", (PyCFunction)Container_save_config, METH_VARARGS | METH_KEYWORDS,
538 "save_config(path = DEFAULT) -> boolean\n"
539 "\n"
540 "Save the container configuration to its default "
541 "location or to an alternative location if provided."
542 },
543 {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS | METH_KEYWORDS,
544 "set_cgroup_item(key, value) -> boolean\n"
545 "\n"
546 "Set a cgroup entry to the provided value."
547 },
548 {"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS | METH_KEYWORDS,
549 "set_config_item(key, value) -> boolean\n"
550 "\n"
551 "Set a config key to the provided value."
552 },
553 {"set_config_path", (PyCFunction)Container_set_config_path, METH_VARARGS | METH_KEYWORDS,
554 "set_config_path(path) -> boolean\n"
555 "\n"
556 "Set the LXC config path (where the containers are stored)."
557 },
558 {"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS | METH_KEYWORDS,
559 "shutdown(timeout = -1) -> boolean\n"
560 "\n"
561 "Sends SIGPWR to the container and wait for it to shutdown "
562 "unless timeout is set to a positive value, in which case "
563 "the container will be killed when the timeout is reached."
564 },
565 {"start", (PyCFunction)Container_start, METH_VARARGS | METH_KEYWORDS,
566 "start(useinit = False, cmd = (,)) -> boolean\n"
567 "\n"
568 "Start the container, optionally using lxc-init and "
569 "an alternate init command, then returns its return code."
570 },
571 {"stop", (PyCFunction)Container_stop, METH_NOARGS,
572 "stop() -> boolean\n"
573 "\n"
574 "Stop the container and returns its return code."
575 },
576 {"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS,
577 "unfreeze() -> boolean\n"
578 "\n"
579 "Unfreezes the container and returns its return code."
580 },
581 {"wait", (PyCFunction)Container_wait, METH_VARARGS | METH_KEYWORDS,
582 "wait(state, timeout = -1) -> boolean\n"
583 "\n"
584 "Wait for the container to reach a given state or timeout."
585 },
586 {NULL} /* Sentinel */
587 };
588
589 static PyTypeObject _lxc_ContainerType = {
590 PyVarObject_HEAD_INIT(NULL, 0)
591 "lxc.Container", /* tp_name */
592 sizeof(Container), /* tp_basicsize */
593 0, /* tp_itemsize */
594 (destructor)Container_dealloc, /* tp_dealloc */
595 0, /* tp_print */
596 0, /* tp_getattr */
597 0, /* tp_setattr */
598 0, /* tp_reserved */
599 0, /* tp_repr */
600 0, /* tp_as_number */
601 0, /* tp_as_sequence */
602 0, /* tp_as_mapping */
603 0, /* tp_hash */
604 0, /* tp_call */
605 0, /* tp_str */
606 0, /* tp_getattro */
607 0, /* tp_setattro */
608 0, /* tp_as_buffer */
609 Py_TPFLAGS_DEFAULT |
610 Py_TPFLAGS_BASETYPE, /* tp_flags */
611 "Container objects", /* tp_doc */
612 0, /* tp_traverse */
613 0, /* tp_clear */
614 0, /* tp_richcompare */
615 0, /* tp_weaklistoffset */
616 0, /* tp_iter */
617 0, /* tp_iternext */
618 Container_methods, /* tp_methods */
619 0, /* tp_members */
620 Container_getseters, /* tp_getset */
621 0, /* tp_base */
622 0, /* tp_dict */
623 0, /* tp_descr_get */
624 0, /* tp_descr_set */
625 0, /* tp_dictoffset */
626 (initproc)Container_init, /* tp_init */
627 0, /* tp_alloc */
628 Container_new, /* tp_new */
629 };
630
631 static PyModuleDef _lxcmodule = {
632 PyModuleDef_HEAD_INIT,
633 "_lxc",
634 "Binding for liblxc in python",
635 -1,
636 NULL, NULL, NULL, NULL, NULL
637 };
638
639 PyMODINIT_FUNC
640 PyInit__lxc(void)
641 {
642 PyObject* m;
643
644 if (PyType_Ready(&_lxc_ContainerType) < 0)
645 return NULL;
646
647 m = PyModule_Create(&_lxcmodule);
648 if (m == NULL)
649 return NULL;
650
651 Py_INCREF(&_lxc_ContainerType);
652 PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType);
653 return m;
654 }