/* *crun - OCI runtime written in C * *Copyright (C) 2018, 2019 Giuseppe Scrivano *crun is free software; you can redistribute it and/or modify *it under the terms of the GNU Lesser General Public License as published by *the Free Software Foundation; either version 2.1 of the License, or *(at your option) any later version. * *crun 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 crun. If not, see . */ /* An example of using this module: import python_crun import json spec = json.loads(python_crun.spec()) spec['root']['path'] = '/path/to/the/rootfs' spec['process']['args'] = ['/bin/echo', 'hello from a container'] ctr = python_crun.load_from_memory(json.dumps(spec)) ctx = python_crun.make_context("test-container") python_crun.set_verbosity(python_crun.VERBOSITY_ERROR) python_crun.run(ctx, ctr) */ #include #include #include #include #include #include #define CONTEXT_OBJ_TAG "crun-context" #define CONTAINER_OBJ_TAG "crun-container" static PyObject * set_error (libcrun_error_t *err) { if ((*err)->status == 0) PyErr_SetString (PyExc_RuntimeError, (*err)->msg); else { cleanup_free char *msg = NULL; asprintf (&msg, "%s: %s", (*err)->msg, strerror ((*err)->status)); if (msg == NULL) return NULL; PyErr_SetString (PyExc_RuntimeError, msg); } crun_error_release (err); return NULL; } static void free_container (PyObject *ptr) { libcrun_container_t *ctr = PyCapsule_GetPointer (ptr, CONTAINER_OBJ_TAG); free_oci_container (ctr->container_def); } static PyObject * container_load_from_file (PyObject *self, PyObject *args) { libcrun_error_t err; const char *path; libcrun_container_t *ctr; if (!PyArg_ParseTuple (args, "s", &path)) return NULL; ctr = libcrun_container_load_from_file (path, &err); if (ctr == NULL) return set_error (&err); return PyCapsule_New (ctr, CONTAINER_OBJ_TAG, free_container); } static PyObject * container_load_from_memory (PyObject *self, PyObject *args) { libcrun_error_t err; const char *def; libcrun_container_t *ctr; if (!PyArg_ParseTuple (args, "s", &def)) return NULL; ctr = libcrun_container_load_from_memory (def, &err); if (ctr == NULL) return set_error (&err); return PyCapsule_New (ctr, CONTAINER_OBJ_TAG, free_container); } static void free_context (void *ptr) { libcrun_context_t *ctx = ptr; char *id = (char *) ctx->id; free (ctx->state_root); free (ctx->notify_socket); free (id); free (ctx); } static PyObject * make_context (PyObject *self, PyObject *args, PyObject *kwargs) { char *id = NULL; char *bundle = NULL; char *state_root = NULL; char *notify_socket = NULL; static char *kwlist[] = { "id", "state-root", "systemd-cgroup", "notify-socket", "detach", NULL }; libcrun_context_t *ctx = malloc (sizeof (*ctx)); if (ctx == NULL) return NULL; memset (ctx, 0, sizeof (*ctx)); ctx->fifo_exec_wait_fd = -1; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "s|sssbsb", kwlist, &id, &bundle, &state_root, &ctx->systemd_cgroup, ¬ify_socket, &ctx->detach)) return NULL; ctx->id = xstrdup (id); ctx->bundle = xstrdup (bundle ? bundle : "."); ctx->state_root = xstrdup (state_root); ctx->notify_socket = xstrdup (notify_socket); return PyCapsule_New (ctx, CONTEXT_OBJ_TAG, NULL); } static PyObject * container_run (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; PyObject *ctr_obj = NULL; libcrun_container_t *ctr; libcrun_context_t *ctx; int ret; if (!PyArg_ParseTuple (args, "OO", &ctx_obj, &ctr_obj)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; ctr = PyCapsule_GetPointer (ctr_obj, CONTAINER_OBJ_TAG); if (ctr == NULL) return NULL; Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_run (ctx, ctr, LIBCRUN_RUN_OPTIONS_PREFORK, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); return PyLong_FromLong (ret); } static PyObject * container_create (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; PyObject *ctr_obj = NULL; libcrun_container_t *ctr; libcrun_context_t *ctx; int ret; if (!PyArg_ParseTuple (args, "OO", &ctx_obj, &ctr_obj)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; ctr = PyCapsule_GetPointer (ctr_obj, CONTAINER_OBJ_TAG); if (ctr == NULL) return NULL; Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_create (ctx, ctr, LIBCRUN_RUN_OPTIONS_PREFORK, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); return PyLong_FromLong (ret); } static PyObject * container_delete (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; char *id = NULL; bool force; libcrun_context_t *ctx; int ret; if (!PyArg_ParseTuple (args, "Osn", &ctx_obj, &id, &force)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_delete (ctx, NULL, id, force, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); Py_RETURN_NONE; } static PyObject * container_kill (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; char *id = NULL; int signal; libcrun_context_t *ctx; int ret; if (!PyArg_ParseTuple (args, "Osi", &ctx_obj, &id, &signal)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_kill (ctx, id, signal, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); Py_RETURN_NONE; } static PyObject * container_start (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; char *id = NULL; libcrun_context_t *ctx; int ret; if (!PyArg_ParseTuple (args, "Os", &ctx_obj, &id)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_start (ctx, id, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); return PyLong_FromLong (ret); } static PyObject * containers_list (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; libcrun_context_t *ctx; libcrun_container_list_t *containers, *it; PyObject *retobj; Py_ssize_t i = 0; int ret; if (!PyArg_ParseTuple (args, "O", &ctx_obj)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; Py_BEGIN_ALLOW_THREADS; ret = libcrun_get_containers_list (&containers, ctx->state_root, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); i = 0; for (it = containers; it; it = it->next) i++; retobj = PyList_New (i); if (retobj == NULL) return NULL; i = 0; for (it = containers; it; it = it->next) PyList_SetItem (retobj, i++, PyUnicode_FromString (it->name)); libcrun_free_containers_list (containers); return retobj; } static PyObject * container_status (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; libcrun_context_t *ctx; char *id = NULL; libcrun_container_status_t status; cleanup_free char *buffer = NULL; FILE *memfile; int ret; if (!PyArg_ParseTuple (args, "Os", &ctx_obj, &id)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; buffer = malloc (4096); if (buffer == NULL) return NULL; /* A bit silly (and expensive), libcrun_container_state needs a refactoring to make this nicer. */ memset (buffer, 0, 4096); memfile = fmemopen (buffer, 4095, "w"); Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_state (ctx, id, memfile, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); fclose (memfile); return PyUnicode_FromString (buffer); } static PyObject * container_update (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; libcrun_context_t *ctx; char *id = NULL; char *content = NULL; yajl_val tree = NULL; int ret; parser_error parser_err = NULL; struct parser_context parser_ctx = { 0, stderr }; oci_container_process *process = NULL; if (!PyArg_ParseTuple (args, "Oss", &ctx_obj, &id, &content)) return NULL; ctx = PyCapsule_GetPointer (ctx_obj, CONTEXT_OBJ_TAG); if (ctx == NULL) return NULL; ret = parse_json_file (&tree, content, &parser_ctx, &err); if (UNLIKELY (ret < 0)) return set_error (&err); process = make_oci_container_process (tree, &parser_ctx, &parser_err); yajl_tree_free (tree); if (process == NULL) { cleanup_free char *msg = NULL; asprintf (&msg, "cannot parse process: %s", parser_err); if (msg == NULL) return NULL; free (parser_err); PyErr_SetString (PyExc_RuntimeError, msg); return NULL; } Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_exec (ctx, id, process, &err); Py_END_ALLOW_THREADS; free_oci_container_process (process); if (ret < 0) return set_error (&err); Py_RETURN_NONE; } static PyObject * container_spec (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; libcrun_context_t *ctx; char *id = NULL; libcrun_container_status_t status; cleanup_free char *buffer = NULL; FILE *memfile; int ret; buffer = malloc (4096); if (buffer == NULL) return NULL; memfile = fmemopen (buffer, 4095, "w"); Py_BEGIN_ALLOW_THREADS; ret = libcrun_container_spec (geteuid () == 0, memfile, &err); Py_END_ALLOW_THREADS; if (ret < 0) return set_error (&err); buffer[ret] = '\0'; fclose (memfile); return PyUnicode_FromString (buffer); } static PyObject * get_verbosity (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; libcrun_context_t *ctx; int verbosity; if (!PyArg_ParseTuple (args, "i", &verbosity)) return NULL; return PyLong_FromLong (libcrun_get_verbosity (verbosity)); } static PyObject * set_verbosity (PyObject *self, PyObject *args) { libcrun_error_t err; PyObject *ctx_obj = NULL; libcrun_context_t *ctx; int verbosity; if (!PyArg_ParseTuple (args, "i", &verbosity)) return NULL; libcrun_set_verbosity (verbosity); Py_RETURN_NONE; } static PyMethodDef CrunMethods[] = { {"load_from_file", container_load_from_file, METH_VARARGS, "Load an OCI container from file."}, {"load_from_memory", container_load_from_memory, METH_VARARGS, "Load an OCI container from memory."}, {"run", container_run, METH_VARARGS, "Run a container."}, {"create", container_run, METH_VARARGS, "Create a container."}, {"delete", container_delete, METH_VARARGS, "Delete a container."}, {"kill", container_kill, METH_VARARGS, "Kill a container."}, {"list", containers_list, METH_VARARGS, "List the containers."}, {"status", container_status, METH_VARARGS, "Get the status of a container."}, {"update", container_update, METH_VARARGS, "Update the constraints of a container."}, {"spec", container_spec, METH_VARARGS, "Generate a new configuration file."}, {"make_context", (PyCFunction) make_context, METH_VARARGS | METH_KEYWORDS, "Create a context object."}, {"set_verbosity", set_verbosity, METH_VARARGS, "Set the logging verbosity."}, {"get_verbosity", get_verbosity, METH_VARARGS, "Get the logging verbosity."}, {"spec", container_spec, METH_VARARGS, "Generate a new configuration file."}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initpython_crun (void) { PyObject *module = Py_InitModule ("python_crun", CrunMethods); (void) PyModule_AddIntConstant (module, "VERBOSITY_ERROR", LIBCRUN_VERBOSITY_ERROR); (void) PyModule_AddIntConstant (module, "VERBOSITY_WARNING", LIBCRUN_VERBOSITY_WARNING); }