diff --git a/.gitignore b/.gitignore index d48daf2cc..0d266c200 100644 --- a/.gitignore +++ b/.gitignore @@ -67,10 +67,6 @@ src/lxc/version.h src/lxc/cmd/lxc-checkconfig src/lxc/cmd/lxc-update-config -src/python-lxc/build/ -src/python-lxc/lxc/__pycache__/ -src/python-lxc/lxc.egg-info/ - src/tests/lxc-test-device-add-remove src/tests/lxc-test-attach src/tests/lxc-test-apparmor diff --git a/Makefile.am b/Makefile.am index 14712bc43..dd892c820 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,10 +13,6 @@ EXTRA_DIST = \ RPMARGS = -if ENABLE_PYTHON -RPMARGS += --with python -endif - pcdatadir = $(libdir)/pkgconfig pcdata_DATA = lxc.pc diff --git a/configure.ac b/configure.ac index 8c1f806aa..1cf1a4ba9 100644 --- a/configure.ac +++ b/configure.ac @@ -363,29 +363,6 @@ AC_ARG_ENABLE([examples], [], [enable_examples=yes]) AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"]) -# Python3 module and scripts -AC_ARG_ENABLE([python], - [AC_HELP_STRING([--enable-python], [enable python binding [default=auto]])], - [], [enable_python=auto]) - -if test "x$enable_python" = "xauto"; then - PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[enable_python=yes],[enable_python=no]) - if test "$CC" = "clang"; then - enable_python=no - fi -fi - -if test "x$enable_python" = "xyes" && test "$CC" = "clang"; then - AC_MSG_ERROR([Python3 is incompatible with the clang compiler]) -fi - -AM_CONDITIONAL([ENABLE_PYTHON], [test "x$enable_python" = "xyes"]) - -AM_COND_IF([ENABLE_PYTHON], - [AM_PATH_PYTHON([3.2], [], [AC_MSG_ERROR([You must install python3])]) - PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[],[AC_MSG_ERROR([You must install python3-dev])]) - AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])]) - # Enable dumping stack traces AC_ARG_ENABLE([mutex-debugging], [AC_HELP_STRING([--enable-mutex-debugging], [Makes mutexes to report error and provide stack trace [default=no]])], @@ -894,7 +871,6 @@ AC_CONFIG_FILES([ src/lxc/cmd/lxc-checkconfig src/lxc/cmd/lxc-update-config src/lxc/version.h - src/python-lxc/Makefile src/tests/Makefile src/tests/lxc-test-usernic @@ -957,9 +933,6 @@ PAM: - PAM module: $enable_pam - cgroup PAM module: $pamdir -Bindings: - - python3: $enable_python - Documentation: - examples: $enable_examples - API documentation: $enable_api_docs diff --git a/doc/ko/Makefile.am b/doc/ko/Makefile.am index b4ae2c57b..2b196830a 100644 --- a/doc/ko/Makefile.am +++ b/doc/ko/Makefile.am @@ -41,13 +41,6 @@ man_MANS = \ \ lxc.7 -if ENABLE_DEPRECATED - man_MANS += lxc-clone.1 -if ENABLE_PYTHON - man_MANS += lxc-start-ephemeral.1 -endif -endif - %.1 : %.sgml $(db2xman) --encoding=UTF-8 $< test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true diff --git a/lxc.spec.in b/lxc.spec.in index f750b1e18..004ced268 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -20,8 +20,6 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -%global with_python %{?_with_python: 1} %{?!_with_python: 0} - # Set with_systemd on distros that use it, so we can install the service # file, otherwise the sysvinit script will be installed %if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1210 @@ -93,12 +91,6 @@ BuildRequires: libseccomp-devel %endif %endif -%if %{with_python} -Requires: python3 -BuildRequires: python3-devel -BuildRequires: python3-setuptools -%endif - %description Containers are insulated areas inside a system, which have their own namespace for filesystem, network, PID, IPC, CPU and memory allocation and which can be @@ -127,9 +119,6 @@ development of the Linux containers. %setup -q -n %{name}-%{version}%{?beta_dot} %build PATH=$PATH:/usr/sbin:/sbin %configure $args \ -%if %{with_python} - --enable-python \ -%endif %if "x%{_unitdir}" != "x" --with-systemdsystemunitdir=%{_unitdir} \ %endif @@ -266,10 +255,6 @@ fi %attr(555,root,root) %{_libexecdir}/%{name}/lxc-containers %endif -%if %{with_python} -%{python3_sitearch}/* -%endif - %files devel %defattr(-,root,root) %{_includedir}/%{name}/* diff --git a/src/Makefile.am b/src/Makefile.am index 4e4d66b5e..ca3b09203 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = lxc tests python-lxc +SUBDIRS = lxc tests diff --git a/src/python-lxc/Makefile.am b/src/python-lxc/Makefile.am deleted file mode 100644 index 38b1d62f7..000000000 --- a/src/python-lxc/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -if ENABLE_PYTHON - -if HAVE_DEBIAN - DISTSETUPOPTS=--install-layout=deb -else - DISTSETUPOPTS= -endif - -if ENABLE_RPATH - RPATHOPTS=-R $(libdir) -else - RPATHOPTS= -endif - -CALL_SETUP_PY := cd @srcdir@ && $(PYTHON) setup.py build -b @abs_builddir@/build egg_info -e @abs_builddir@ - -all: - $(CALL_SETUP_PY) build_ext -I @abs_top_srcdir@/src -L @abs_top_builddir@/src/lxc/.libs/ $(RPATHOPTS) --no-pkg-config - -DESTDIR = / # default - -install: - $(CALL_SETUP_PY) install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS) --root=$(DESTDIR) - -clean-local: - rm -rf @builddir@/build - -endif -EXTRA_DIST = \ - setup.py \ - lxc.c \ - lxc/__init__.py \ - examples/api_test.py \ - examples/pyconsole.py \ - examples/pyconsole-vte.py diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py deleted file mode 100755 index 1934a238c..000000000 --- a/src/python-lxc/examples/api_test.py +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# api_test.py: Test/demo of the python3-lxc API -# -# (C) Copyright Canonical Ltd. 2012 -# -# Authors: -# Stéphane Graber -# -# This library 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. -# -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -# USA -# - -import lxc -import uuid -import os -import subprocess -import sys -import time - -# Let's pick a random name, avoiding clashes -CONTAINER_NAME = str(uuid.uuid1()) -CLONE_NAME = str(uuid.uuid1()) -RENAME_NAME = str(uuid.uuid1()) - -## Instantiate the container instance -print("Getting instance for '%s'" % CONTAINER_NAME) -container = lxc.Container(CONTAINER_NAME) - -# A few basic checks of the current state -assert(container.config_file_name == "%s/%s/config" % - (lxc.default_config_path, CONTAINER_NAME)) -assert(not container.defined) -assert(container.init_pid == -1) -assert(container.name == CONTAINER_NAME) -assert(not container.running) -assert(container.state == "STOPPED") - -# Try to get the host architecture for dpkg systems -arch = "i386" -try: - with open(os.path.devnull, "w") as devnull: - dpkg = subprocess.Popen(['dpkg', '--print-architecture'], - stderr=devnull, stdout=subprocess.PIPE, - universal_newlines=True) - - if dpkg.wait() == 0: - arch = dpkg.stdout.read().strip() -except: - pass - -## Create a rootfs -print("Creating rootfs using 'download', arch=%s" % arch) -container.create("download", 0, - {"dist": "ubuntu", - "release": "xenial", - "arch": arch}) - -assert(container.defined) -assert(container.name == CONTAINER_NAME - == container.get_config_item("lxc.uts.name")) -assert(container.name in lxc.list_containers()) - -## Test the config -print("Testing the configuration") -capdrop = container.get_config_item("lxc.cap.drop") -container.clear_config_item("lxc.cap.drop") -container.set_config_item("lxc.cap.drop", capdrop[:-1]) -container.append_config_item("lxc.cap.drop", capdrop[-1]) -container.save_config() - -# A few basic checks of the current state -assert(isinstance(capdrop, list)) -assert(capdrop == container.get_config_item("lxc.cap.drop")) - -## Test the networking -print("Testing the networking") - -# A few basic checks of the current state -assert("name" in container.get_keys("lxc.net.0")) -assert(len(container.network) == 1) - -## Starting the container -print("Starting the container") -container.start() -container.wait("RUNNING", 3) - -# A few basic checks of the current state -assert(container.init_pid > 1) -assert(container.running) -assert(container.state == "RUNNING") - - -## Checking IP address -print("Getting the interface names") -assert(set(container.get_interfaces()) == set(('lo', 'eth0'))) - -## Checking IP address -print("Getting the IP addresses") - -count = 0 -ips = [] -while not ips or count == 10: - ips = container.get_ips() - time.sleep(1) - count += 1 - -if os.geteuid(): - container.attach_wait(lxc.attach_run_command, ["ifconfig", "eth0"], - namespaces=(lxc.CLONE_NEWUSER + lxc.CLONE_NEWNET - + lxc.CLONE_NEWUTS)) -else: - container.attach_wait(lxc.attach_run_command, ["ifconfig", "eth0"], - namespaces=(lxc.CLONE_NEWNET + lxc.CLONE_NEWUTS)) - -# A few basic checks of the current state -assert(len(ips) > 0) - -## Test running config -assert(container.name == CONTAINER_NAME - == container.get_config_item("lxc.uts.name") - == container.get_running_config_item("lxc.uts.name")) - -## Testing cgroups a bit -print("Testing cgroup API") -max_mem = container.get_cgroup_item("memory.max_usage_in_bytes") -current_limit = container.get_cgroup_item("memory.limit_in_bytes") -assert(container.set_cgroup_item("memory.limit_in_bytes", max_mem)) -assert(container.get_cgroup_item("memory.limit_in_bytes") != current_limit) - -## Freezing the container -print("Freezing the container") -container.freeze() -container.wait("FROZEN", 3) - -# A few basic checks of the current state -assert(container.init_pid > 1) -assert(container.running) -assert(container.state == "FROZEN") - -## Unfreezing the container -print("Unfreezing the container") -container.unfreeze() -container.wait("RUNNING", 3) - -# A few basic checks of the current state -assert(container.init_pid > 1) -assert(container.running) -assert(container.state == "RUNNING") - -if len(sys.argv) > 1 and sys.argv[1] == "--with-console": - ## Attaching to tty1 - print("Attaching to tty1") - container.console(tty=1) - -## Shutting down the container -print("Shutting down the container") -if not container.shutdown(3): - container.stop() - -if container.running: - print("Stopping the container") - container.stop() - container.wait("STOPPED", 3) - -# A few basic checks of the current state -assert(container.init_pid == -1) -assert(not container.running) -assert(container.state == "STOPPED") - -## Snapshotting the container -print("Snapshotting the container") -assert(not container.snapshot_list()) -assert(container.snapshot() == "snap0") -assert(len(container.snapshot_list()) == 1) -assert(container.snapshot_restore("snap0") is True) -assert(container.snapshot_destroy("snap0") is True) - -## Cloning the container -print("Cloning the container as '%s'" % CLONE_NAME) -clone = container.clone(CLONE_NAME) -assert(clone is not False) - -print ("Renaming the clone to '%s'" % RENAME_NAME) -rename = clone.rename(RENAME_NAME) -rename.start() -rename.stop() -rename.destroy() - -## Destroy the container -print("Destroying the container") -container.destroy() - -assert(not container.defined) diff --git a/src/python-lxc/examples/pyconsole-vte.py b/src/python-lxc/examples/pyconsole-vte.py deleted file mode 100755 index 8a98359af..000000000 --- a/src/python-lxc/examples/pyconsole-vte.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# pyconsole-vte: Example program showing use of console functions -# in the lxc python binding -# -# (C) Copyright Oracle. 2013 -# -# Authors: -# Dwight Engen -# -# This library 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. -# -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -# USA -# - -import gtk -import vte -import lxc -import sys - - -def gtk_exit_cb(terminal): - gtk.main_quit() - - -def vte_con(ct, ttynum): - print("Doing console in a VTE widget...") - masterfd = ct.console_getfd(ttynum) - term = vte.Terminal() - term.set_cursor_blinks(True) - term.set_scrollback_lines(1000) - term.connect('eof', gtk_exit_cb) - - term.set_pty(masterfd) - term.feed_child('\n') - #term.feed_child('ps aux\n') - - vscrollbar = gtk.VScrollbar() - vscrollbar.set_adjustment(term.get_adjustment()) - - hbox = gtk.HBox() - hbox.pack_start(term) - hbox.pack_start(vscrollbar) - - window = gtk.Window() - window.add(hbox) - window.connect('delete-event', lambda window, event: gtk.main_quit()) - window.show_all() - gtk.main() - print("Console done") - -if __name__ == '__main__': - ttynum = -1 - if len(sys.argv) < 2: - sys.exit("Usage: %s container-name [ttynum]" % sys.argv[0]) - if len(sys.argv) > 2: - ttynum = int(sys.argv[2]) - - ct = lxc.Container(sys.argv[1]) - - print("Container:%s tty:%d" % (ct.name, ttynum)) - if not ct.defined: - sys.exit("Container %s not defined" % ct.name) - if not ct.running: - sys.exit("Container %s not running" % ct.name) - - vte_con(ct, ttynum) diff --git a/src/python-lxc/examples/pyconsole.py b/src/python-lxc/examples/pyconsole.py deleted file mode 100755 index 2b0cd14ab..000000000 --- a/src/python-lxc/examples/pyconsole.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# pyconsole: Example program showing use of console functions -# in the lxc python binding -# -# (C) Copyright Oracle. 2013 -# -# Authors: -# Dwight Engen -# -# This library 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. -# -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -# USA -# - -import lxc -import sys -import time - -if __name__ == '__main__': - ttynum = -1 - escape = 1 - if len(sys.argv) < 2: - sys.exit("Usage: %s container-name [ttynum [escape]]" % sys.argv[0]) - if len(sys.argv) > 2: - ttynum = int(sys.argv[2]) - if len(sys.argv) > 3: - escape = ord(sys.argv[3]) - ord('a') + 1 - - ct = lxc.Container(sys.argv[1]) - - print("Container:%s tty:%d Ctrl-%c q to quit" % - (ct.name, ttynum, ord('a') + escape-1)) - time.sleep(1) - if not ct.defined: - sys.exit("Container %s not defined" % ct.name) - if not ct.running: - sys.exit("Container %s not running" % ct.name) - - ct.console(ttynum, 0, 1, 2, escape) - print("Console done") diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c deleted file mode 100644 index 70176cdc6..000000000 --- a/src/python-lxc/lxc.c +++ /dev/null @@ -1,2031 +0,0 @@ -/* - * python-lxc: Python bindings for LXC - * - * (C) Copyright Canonical Ltd. 2012-2013 - * - * Authors: - * Stéphane Graber - * - * This library 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. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include -#include "structmember.h" -#include -#include -#include -#include - -/* - * CLONE_* definitions copied from lxc/namespace.h - */ -#ifndef CLONE_FS -# define CLONE_FS 0x00000200 -#endif -#ifndef CLONE_NEWNS -# define CLONE_NEWNS 0x00020000 -#endif -#ifndef CLONE_NEWCGROUP -# define CLONE_NEWCGROUP 0x02000000 -#endif -#ifndef CLONE_NEWUTS -# define CLONE_NEWUTS 0x04000000 -#endif -#ifndef CLONE_NEWIPC -# define CLONE_NEWIPC 0x08000000 -#endif -#ifndef CLONE_NEWUSER -# define CLONE_NEWUSER 0x10000000 -#endif -#ifndef CLONE_NEWPID -# define CLONE_NEWPID 0x20000000 -#endif -#ifndef CLONE_NEWNET -# define CLONE_NEWNET 0x40000000 -#endif - -/* From sys/personality.h */ -#define PER_LINUX 0x0000 -#define PER_LINUX32 0x0008 - -/* Helper functions */ - -/* Copied from lxc/utils.c */ -static int lxc_wait_for_pid_status(pid_t pid) -{ - int status, ret; - -again: - ret = waitpid(pid, &status, 0); - if (ret == -1) { - if (errno == EINTR) - goto again; - return -1; - } - if (ret != pid) - goto again; - return status; -} - -/* Copied from lxc/confile.c, with HAVE_SYS_PERSONALITY_H check removed */ -signed long lxc_config_parse_arch(const char *arch) -{ - struct per_name { - char *name; - unsigned long per; - } pername[] = { - { "x86", PER_LINUX32 }, - { "linux32", PER_LINUX32 }, - { "i386", PER_LINUX32 }, - { "i486", PER_LINUX32 }, - { "i586", PER_LINUX32 }, - { "i686", PER_LINUX32 }, - { "athlon", PER_LINUX32 }, - { "linux64", PER_LINUX }, - { "x86_64", PER_LINUX }, - { "amd64", PER_LINUX }, - }; - size_t len = sizeof(pername) / sizeof(pername[0]); - - size_t i; - - for (i = 0; i < len; i++) { - if (!strcmp(pername[i].name, arch)) - return pername[i].per; - } - - return -1; -} - -char** -convert_tuple_to_char_pointer_array(PyObject *argv) { - int argc; - int i, j; - char **result; - - /* not a list or tuple */ - if (!PyList_Check(argv) && !PyTuple_Check(argv)) { - PyErr_SetString(PyExc_TypeError, "Expected list or tuple."); - return NULL; - } - - argc = PySequence_Fast_GET_SIZE(argv); - - result = (char**) calloc(argc + 1, sizeof(char*)); - - if (result == NULL) { - PyErr_SetNone(PyExc_MemoryError); - return NULL; - } - - for (i = 0; i < argc; i++) { - char *str = NULL; - PyObject *pystr = NULL; - PyObject *pyobj = PySequence_Fast_GET_ITEM(argv, i); - assert(pyobj != NULL); - - if (!PyUnicode_Check(pyobj)) { - PyErr_SetString(PyExc_ValueError, "Expected a string"); - goto error; - } - - pystr = PyUnicode_AsUTF8String(pyobj); - if (!pystr) { - /* Maybe it wasn't UTF-8 encoded. An exception is already set. */ - goto error; - } - - str = PyBytes_AsString(pystr); - if (!str) { - /* Maybe pystr wasn't a valid object. An exception is already set. - */ - Py_DECREF(pystr); - goto error; - } - - /* We must make a copy of str, because it points into internal memory - * which we do not own. Assume it's NULL terminated, otherwise we'd - * have to use PyUnicode_AsUTF8AndSize() and be explicit about copying - * the memory. - */ - result[i] = strdup(str); - - /* Do not decref pyobj since we stole a reference by using - * PyTuple_GET_ITEM(). - */ - Py_DECREF(pystr); - if (result[i] == NULL) { - PyErr_SetNone(PyExc_MemoryError); - goto error; - } - } - - result[argc] = NULL; - return result; - -error: - /* We can only iterate up to but not including i because malloc() does not - * initialize its memory. Thus if we got here, i points to the index - * after the last strdup'd entry in result. - */ - for (j = 0; j < i; j++) - free(result[j]); - free(result); - return NULL; -} - -struct lxc_attach_python_payload { - PyObject *fn; - PyObject *arg; -}; - -static int lxc_attach_python_exec(void* _payload) -{ - /* This function is the first one to be called after attaching to a - * container. As lxc_attach() calls fork() PyOS_AfterFork should be called - * in the new process if the Python interpreter will continue to be used. - */ - PyOS_AfterFork(); - - struct lxc_attach_python_payload *payload = - (struct lxc_attach_python_payload *)_payload; - PyObject *result = PyObject_CallFunctionObjArgs(payload->fn, - payload->arg, NULL); - - if (!result) { - PyErr_Print(); - return -1; - } - if (PyLong_Check(result)) - return (int)PyLong_AsLong(result); - else - return -1; -} - -static void lxc_attach_free_options(lxc_attach_options_t *options); - -static lxc_attach_options_t *lxc_attach_parse_options(PyObject *kwds) -{ - static char *kwlist[] = {"attach_flags", "namespaces", "personality", - "initial_cwd", "uid", "gid", "env_policy", - "extra_env_vars", "extra_keep_env", "stdin", - "stdout", "stderr", NULL}; - long temp_uid, temp_gid; - int temp_env_policy; - PyObject *extra_env_vars_obj = NULL; - PyObject *extra_keep_env_obj = NULL; - PyObject *stdin_obj = NULL; - PyObject *stdout_obj = NULL; - PyObject *stderr_obj = NULL; - PyObject *initial_cwd_obj = NULL; - PyObject *dummy = NULL; - bool parse_result; - - lxc_attach_options_t default_options = LXC_ATTACH_OPTIONS_DEFAULT; - lxc_attach_options_t *options = malloc(sizeof(*options)); - - if (!options) { - PyErr_SetNone(PyExc_MemoryError); - return NULL; - } - memcpy(options, &default_options, sizeof(*options)); - - /* we need some dummy variables because we can't be sure - * the data types match completely */ - temp_uid = -1; - temp_gid = -1; - temp_env_policy = options->env_policy; - - /* we need a dummy tuple */ - dummy = PyTuple_New(0); - - parse_result = PyArg_ParseTupleAndKeywords(dummy, kwds, "|iilO&lliOOOOO", - kwlist, &options->attach_flags, - &options->namespaces, - &options->personality, - PyUnicode_FSConverter, - &initial_cwd_obj, &temp_uid, - &temp_gid, &temp_env_policy, - &extra_env_vars_obj, - &extra_keep_env_obj, - &stdin_obj, &stdout_obj, - &stderr_obj); - - /* immediately get rid of the dummy tuple */ - Py_DECREF(dummy); - - if (!parse_result) { - lxc_attach_free_options(options); - return NULL; - } - - /* duplicate the string, so we don't depend on some random Python object */ - if (initial_cwd_obj != NULL) { - options->initial_cwd = strndup(PyBytes_AsString(initial_cwd_obj), - PyBytes_Size(initial_cwd_obj)); - Py_DECREF(initial_cwd_obj); - } - - /* do the type conversion from the types that match the parse string */ - if (temp_uid != -1) options->uid = (uid_t)temp_uid; - if (temp_gid != -1) options->gid = (gid_t)temp_gid; - options->env_policy = (lxc_attach_env_policy_t)temp_env_policy; - - if (extra_env_vars_obj) - options->extra_env_vars = - convert_tuple_to_char_pointer_array(extra_env_vars_obj); - if (extra_keep_env_obj) - options->extra_keep_env = - convert_tuple_to_char_pointer_array(extra_keep_env_obj); - if (stdin_obj) { - options->stdin_fd = PyObject_AsFileDescriptor(stdin_obj); - if (options->stdin_fd < 0) { - lxc_attach_free_options(options); - return NULL; - } - } - if (stdout_obj) { - options->stdout_fd = PyObject_AsFileDescriptor(stdout_obj); - if (options->stdout_fd < 0) { - lxc_attach_free_options(options); - return NULL; - } - } - if (stderr_obj) { - options->stderr_fd = PyObject_AsFileDescriptor(stderr_obj); - if (options->stderr_fd < 0) { - lxc_attach_free_options(options); - return NULL; - } - } - - return options; -} - -void lxc_attach_free_options(lxc_attach_options_t *options) -{ - int i; - if (!options) - return; - free(options->initial_cwd); - if (options->extra_env_vars) { - for (i = 0; options->extra_env_vars[i]; i++) - free(options->extra_env_vars[i]); - free(options->extra_env_vars); - } - if (options->extra_keep_env) { - for (i = 0; options->extra_keep_env[i]; i++) - free(options->extra_keep_env[i]); - free(options->extra_keep_env); - } - free(options); -} - -/* Module functions */ -static PyObject * -LXC_arch_to_personality(PyObject *self, PyObject *arg) -{ - long rv = -1; - PyObject *pystr = NULL; - char *str; - - if (!PyUnicode_Check(arg)) { - PyErr_SetString(PyExc_ValueError, "Expected a string"); - return NULL; - } - - pystr = PyUnicode_AsUTF8String(arg); - if (!pystr) - return NULL; - - str = PyBytes_AsString(pystr); - if (!str) - goto out; - - rv = lxc_config_parse_arch(str); - if (rv == -1) - PyErr_SetString(PyExc_KeyError, "Failed to lookup architecture."); - -out: - Py_DECREF(pystr); - return rv == -1 ? NULL : PyLong_FromLong(rv); -} - -static PyObject * -LXC_attach_run_command(PyObject *self, PyObject *arg) -{ - PyObject *args_obj = NULL; - int i, rv; - lxc_attach_command_t cmd = { - NULL, /* program */ - NULL /* argv[] */ - }; - - if (!PyArg_ParseTuple(arg, "sO", (const char**)&cmd.program, &args_obj)) - return NULL; - if (args_obj && PyList_Check(args_obj)) { - cmd.argv = convert_tuple_to_char_pointer_array(args_obj); - } else { - PyErr_Format(PyExc_TypeError, "Second part of tuple passed to " - "attach_run_command must be a list."); - return NULL; - } - - if (!cmd.argv) - return NULL; - - rv = lxc_attach_run_command(&cmd); - - for (i = 0; cmd.argv[i]; i++) - free(cmd.argv[i]); - free(cmd.argv); - - return PyLong_FromLong(rv); -} - -static PyObject * -LXC_attach_run_shell(PyObject *self, PyObject *arg) -{ - int rv; - - rv = lxc_attach_run_shell(NULL); - - return PyLong_FromLong(rv); -} - -static PyObject * -LXC_get_global_config_item(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", NULL}; - char* key = NULL; - const char* value = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, - &key)) - return NULL; - - value = lxc_get_global_config_item(key); - - if (!value) { - PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); - return NULL; - } - - return PyUnicode_FromString(value); -} - -static PyObject * -LXC_get_version(PyObject *self, PyObject *args) -{ - const char *rv = NULL; - - rv = lxc_get_version(); - if (!rv) { - return PyUnicode_FromString(""); - } - - return PyUnicode_FromString(rv); -} - -static PyObject * -LXC_list_containers(PyObject *self, PyObject *args, PyObject *kwds) -{ - char **names = NULL; - PyObject *list = NULL; - int list_count = 0; - - int list_active = 1; - int list_defined = 1; - - PyObject *py_list_active = NULL; - PyObject *py_list_defined = NULL; - - char* config_path = NULL; - - int i = 0; - PyObject *vargs = NULL; - static char *kwlist[] = {"active", "defined", "config_path", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOs", kwlist, - &py_list_active, - &py_list_defined, - &config_path, &vargs)) - return NULL; - - /* We default to listing everything */ - if (py_list_active && py_list_active != Py_True) { - list_active = 0; - } - - if (py_list_defined && py_list_defined != Py_True) { - list_defined = 0; - } - - /* Call the right API function based on filters */ - if (list_active == 1 && list_defined == 1) - list_count = list_all_containers(config_path, &names, NULL); - else if (list_active == 1) - list_count = list_active_containers(config_path, &names, NULL); - else if (list_defined == 1) - list_count = list_defined_containers(config_path, &names, NULL); - - /* Handle failure */ - if (list_count < 0) { - PyErr_SetString(PyExc_ValueError, "failure to list containers"); - return NULL; - } - - /* Generate the tuple */ - list = PyTuple_New(list_count); - for (i = 0; i < list_count; i++) { - if (!names[i]) { - continue; - } - - PyTuple_SET_ITEM(list, i, PyUnicode_FromString(names[i])); - free(names[i]); - } - free(names); - - return list; -} - -/* Base type and functions for Container */ -typedef struct { - PyObject_HEAD - struct lxc_container *container; -} Container; - -static void -Container_dealloc(Container* self) -{ - lxc_container_put(self->container); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static int -Container_init(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"name", "config_path", NULL}; - char *name = NULL; - PyObject *fs_config_path = NULL; - char *config_path = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O&", kwlist, - &name, - PyUnicode_FSConverter, &fs_config_path)) - return -1; - - if (fs_config_path != NULL) { - config_path = PyBytes_AS_STRING(fs_config_path); - assert(config_path != NULL); - } - - self->container = lxc_container_new(name, config_path); - if (!self->container) { - Py_XDECREF(fs_config_path); - - PyErr_Format(PyExc_RuntimeError, "%s:%s:%d: error during init for container '%s'.", - __FUNCTION__, __FILE__, __LINE__, name); - return -1; - } - - Py_XDECREF(fs_config_path); - return 0; -} - -static PyObject * -Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - Container *self; - - self = (Container *)type->tp_alloc(type, 0); - - return (PyObject *)self; -} - -/* Container properties */ -static PyObject * -Container_config_file_name(Container *self, void *closure) -{ - char *rv = NULL; - - rv = self->container->config_file_name(self->container); - if (!rv) { - return PyUnicode_FromString(""); - } - - return PyUnicode_FromString(rv); -} - -static PyObject * -Container_controllable(Container *self, void *closure) -{ - if (self->container->may_control(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_defined(Container *self, void *closure) -{ - if (self->container->is_defined(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_init_pid(Container *self, void *closure) -{ - return PyLong_FromLong(self->container->init_pid(self->container)); -} - -static PyObject * -Container_name(Container *self, void *closure) -{ - if (!self->container->name) { - return PyUnicode_FromString(""); - } - - return PyUnicode_FromString(self->container->name); -} - -static PyObject * -Container_running(Container *self, void *closure) -{ - if (self->container->is_running(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_state(Container *self, void *closure) -{ - const char *rv = NULL; - - rv = self->container->state(self->container); - - if (!rv) { - return PyUnicode_FromString(""); - } - - return PyUnicode_FromString(rv); -} - -/* Container Functions */ -static PyObject * -Container_attach_interface(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"src_ifname", "dst_ifname", NULL}; - char *src_name = NULL; - char *dst_name = NULL; - PyObject *py_src_name = NULL; - PyObject *py_dst_name = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, - PyUnicode_FSConverter, &py_src_name, - PyUnicode_FSConverter, &py_dst_name)) - return NULL; - - if (py_src_name != NULL) { - src_name = PyBytes_AS_STRING(py_src_name); - assert(src_name != NULL); - } - - if (py_dst_name != NULL) { - dst_name = PyBytes_AS_STRING(py_dst_name); - assert(dst_name != NULL); - } - - if (self->container->attach_interface(self->container, src_name, dst_name)) { - Py_XDECREF(py_src_name); - Py_XDECREF(py_dst_name); - Py_RETURN_TRUE; - } - - Py_XDECREF(py_src_name); - Py_XDECREF(py_dst_name); - Py_RETURN_FALSE; -} - -static PyObject * -Container_detach_interface(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"ifname", NULL}; - char *ifname = NULL; - PyObject *py_ifname = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&", kwlist, - PyUnicode_FSConverter, &py_ifname)) - return NULL; - - if (py_ifname != NULL) { - ifname = PyBytes_AS_STRING(py_ifname); - assert(ifname != NULL); - } - - if (self->container->detach_interface(self->container, ifname, NULL)) { - Py_XDECREF(py_ifname); - Py_RETURN_TRUE; - } - - Py_XDECREF(py_ifname); - Py_RETURN_FALSE; -} - -static PyObject * -Container_add_device_node(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"src_path", "dest_path", NULL}; - char *src_path = NULL; - char *dst_path = NULL; - PyObject *py_src_path = NULL; - PyObject *py_dst_path = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, - PyUnicode_FSConverter, &py_src_path, - PyUnicode_FSConverter, &py_dst_path)) - return NULL; - - if (py_src_path != NULL) { - src_path = PyBytes_AS_STRING(py_src_path); - assert(src_path != NULL); - } - - if (py_dst_path != NULL) { - dst_path = PyBytes_AS_STRING(py_dst_path); - assert(dst_path != NULL); - } - - if (self->container->add_device_node(self->container, src_path, - dst_path)) { - Py_XDECREF(py_src_path); - Py_XDECREF(py_dst_path); - Py_RETURN_TRUE; - } - - Py_XDECREF(py_src_path); - Py_XDECREF(py_dst_path); - Py_RETURN_FALSE; -} - -static PyObject * -Container_attach_and_possibly_wait(Container *self, PyObject *args, - PyObject *kwds, int wait) -{ - struct lxc_attach_python_payload payload = { NULL, NULL }; - lxc_attach_options_t *options = NULL; - long ret; - pid_t pid; - - if (!PyArg_ParseTuple(args, "O|O", &payload.fn, &payload.arg)) - return NULL; - if (!PyCallable_Check(payload.fn)) { - PyErr_Format(PyExc_TypeError, "attach: object not callable"); - return NULL; - } - - options = lxc_attach_parse_options(kwds); - if (!options) - return NULL; - - ret = self->container->attach(self->container, lxc_attach_python_exec, - &payload, options, &pid); - if (ret < 0) - goto out; - - if (wait) { - Py_BEGIN_ALLOW_THREADS - ret = lxc_wait_for_pid_status(pid); - Py_END_ALLOW_THREADS - /* handle case where attach fails */ - if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255) - ret = -1; - } else { - ret = (long)pid; - } - -out: - lxc_attach_free_options(options); - return PyLong_FromLong(ret); -} - -static PyObject * -Container_attach(Container *self, PyObject *args, PyObject *kwds) -{ - return Container_attach_and_possibly_wait(self, args, kwds, 0); -} - -static PyObject * -Container_attach_wait(Container *self, PyObject *args, PyObject *kwds) -{ - return Container_attach_and_possibly_wait(self, args, kwds, 1); -} - -static PyObject * -Container_clear_config(Container *self, PyObject *args, PyObject *kwds) -{ - self->container->clear_config(self->container); - - Py_RETURN_NONE; -} - -static PyObject * -Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", NULL}; - char *key = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, - &key)) - return NULL; - - if (self->container->clear_config_item(self->container, key)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_clone(Container *self, PyObject *args, PyObject *kwds) -{ - char *newname = NULL; - char *config_path = NULL; - int flags = 0; - char *bdevtype = NULL; - char *bdevdata = NULL; - unsigned long newsize = 0; - char **hookargs = NULL; - - PyObject *py_hookargs = NULL; - PyObject *py_config_path = NULL; - struct lxc_container *new_container = NULL; - int i = 0; - - static char *kwlist[] = {"newname", "config_path", "flags", "bdevtype", - "bdevdata", "newsize", "hookargs", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O&isskO", kwlist, - &newname, - PyUnicode_FSConverter, &py_config_path, - &flags, &bdevtype, &bdevdata, &newsize, - &py_hookargs)) - return NULL; - - if (py_hookargs) { - if (PyTuple_Check(py_hookargs)) { - hookargs = convert_tuple_to_char_pointer_array(py_hookargs); - if (!hookargs) { - return NULL; - } - } - else { - PyErr_SetString(PyExc_ValueError, "hookargs needs to be a tuple"); - return NULL; - } - } - - if (py_config_path != NULL) { - config_path = PyBytes_AS_STRING(py_config_path); - assert(config_path != NULL); - } - - new_container = self->container->clone(self->container, newname, - config_path, flags, bdevtype, - bdevdata, newsize, hookargs); - - Py_XDECREF(py_config_path); - - if (hookargs) { - for (i = 0; i < PyTuple_GET_SIZE(py_hookargs); i++) - free(hookargs[i]); - free(hookargs); - } - - if (new_container == NULL) { - Py_RETURN_FALSE; - } - - lxc_container_put(new_container); - - Py_RETURN_TRUE; -} - -static PyObject * -Container_console(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"ttynum", "stdinfd", "stdoutfd", "stderrfd", - "escape", NULL}; - int ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, escape = 1; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iiiii", kwlist, - &ttynum, &stdinfd, &stdoutfd, &stderrfd, - &escape)) - return NULL; - - if (self->container->console(self->container, ttynum, - stdinfd, stdoutfd, stderrfd, escape) == 0) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; -} - -static PyObject * -Container_console_getfd(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"ttynum", NULL}; - int ttynum = -1, masterfd; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &ttynum)) - return NULL; - - if (self->container->console_getfd(self->container, &ttynum, - &masterfd) < 0) { - PyErr_SetString(PyExc_ValueError, "Unable to allocate tty"); - return NULL; - } - return PyLong_FromLong(masterfd); -} - -static PyObject * -Container_create(Container *self, PyObject *args, PyObject *kwds) -{ - char* template_name = NULL; - int flags = 0; - char** create_args = {NULL}; - PyObject *retval = NULL; - PyObject *vargs = NULL; - char *bdevtype = NULL; - int i = 0; - static char *kwlist[] = {"template", "flags", "bdevtype", "args", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|sisO", kwlist, - &template_name, &flags, &bdevtype, &vargs)) - return NULL; - - if (vargs) { - if (PyTuple_Check(vargs)) { - create_args = convert_tuple_to_char_pointer_array(vargs); - if (!create_args) { - return NULL; - } - } - else { - PyErr_SetString(PyExc_ValueError, "args needs to be a tuple"); - return NULL; - } - } - - if (self->container->create(self->container, template_name, bdevtype, NULL, - flags, create_args)) - retval = Py_True; - else - retval = Py_False; - - if (vargs) { - /* We cannot have gotten here unless vargs was given and create_args - * was successfully allocated. - */ - for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) - free(create_args[i]); - free(create_args); - } - - Py_INCREF(retval); - return retval; -} - -static PyObject * -Container_destroy(Container *self, PyObject *args, PyObject *kwds) -{ - if (self->container->destroy(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_freeze(Container *self, PyObject *args, PyObject *kwds) -{ - if (self->container->freeze(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", NULL}; - char* key = NULL; - int len = 0; - char* value; - PyObject *ret = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, - &key)) - return NULL; - - len = self->container->get_cgroup_item(self->container, key, NULL, 0); - - if (len < 0) { - PyErr_SetString(PyExc_KeyError, "Invalid cgroup entry"); - return NULL; - } - - value = (char*) malloc(sizeof(char)*len + 1); - if (value == NULL) - return PyErr_NoMemory(); - - if (self->container->get_cgroup_item(self->container, - key, value, len + 1) != len) { - PyErr_SetString(PyExc_ValueError, "Unable to read config value"); - free(value); - return NULL; - } - - ret = PyUnicode_FromString(value); - free(value); - return ret; -} - -static PyObject * -Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", NULL}; - char* key = NULL; - int len = 0; - char* value; - PyObject *ret = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, - &key)) - return NULL; - - len = self->container->get_config_item(self->container, key, NULL, 0); - - if (len < 0) { - PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); - return NULL; - } - - if (len == 0) { - return PyUnicode_FromString(""); - } - - value = (char*) malloc(sizeof(char)*len + 1); - if (value == NULL) - return PyErr_NoMemory(); - - if (self->container->get_config_item(self->container, - key, value, len + 1) != len) { - PyErr_SetString(PyExc_ValueError, "Unable to read config value"); - free(value); - return NULL; - } - - ret = PyUnicode_FromString(value); - free(value); - return ret; -} - -static PyObject * -Container_get_config_path(Container *self, PyObject *args, PyObject *kwds) -{ - const char *rv = NULL; - - rv = self->container->get_config_path(self->container); - - if (!rv) { - return PyUnicode_FromString(""); - } - - return PyUnicode_FromString(rv); -} - -static PyObject * -Container_get_keys(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", NULL}; - char* key = NULL; - int len = 0; - char* value; - PyObject *ret = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, - &key)) - return NULL; - - len = self->container->get_keys(self->container, key, NULL, 0); - - if (len < 0) { - PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); - return NULL; - } - - value = (char*) malloc(sizeof(char)*len + 1); - if (value == NULL) - return PyErr_NoMemory(); - - if (self->container->get_keys(self->container, - key, value, len + 1) != len) { - PyErr_SetString(PyExc_ValueError, "Unable to read config keys"); - free(value); - return NULL; - } - - ret = PyUnicode_FromString(value); - free(value); - return ret; -} - -static PyObject * -Container_get_interfaces(Container *self) -{ - int i = 0; - char** interfaces = NULL; - - PyObject* ret; - - /* Get the interfaces */ - interfaces = self->container->get_interfaces(self->container); - if (!interfaces) - return PyTuple_New(0); - - /* Count the entries */ - while (interfaces[i]) - i++; - - /* Create the new tuple */ - ret = PyTuple_New(i); - if (!ret) - return NULL; - - /* Add the entries to the tuple and free the memory */ - i = 0; - while (interfaces[i]) { - if (!interfaces[i]) { - i++; - continue; - } - - PyObject *unicode = PyUnicode_FromString(interfaces[i]); - if (!unicode) { - Py_DECREF(ret); - ret = NULL; - break; - } - PyTuple_SET_ITEM(ret, i, unicode); - i++; - } - - /* Free the list of IPs */ - i = 0; - while (interfaces[i]) { - free(interfaces[i]); - i++; - } - free(interfaces); - - return ret; -} - -static PyObject * -Container_get_ips(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"interface", "family", "scope", NULL}; - char* interface = NULL; - char* family = NULL; - int scope = 0; - - int i = 0; - char** ips = NULL; - - PyObject* ret; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ssi", kwlist, - &interface, &family, &scope)) - return NULL; - - /* Get the IPs */ - ips = self->container->get_ips(self->container, interface, family, scope); - if (!ips) - return PyTuple_New(0); - - /* Count the entries */ - while (ips[i]) - i++; - - /* Create the new tuple */ - ret = PyTuple_New(i); - if (!ret) - return NULL; - - /* Add the entries to the tuple and free the memory */ - i = 0; - while (ips[i]) { - if (!ips[i]) { - i++; - continue; - } - - PyObject *unicode = PyUnicode_FromString(ips[i]); - if (!unicode) { - Py_DECREF(ret); - ret = NULL; - break; - } - PyTuple_SET_ITEM(ret, i, unicode); - i++; - } - - /* Free the list of IPs */ - i = 0; - while (ips[i]) { - free(ips[i]); - i++; - } - free(ips); - - return ret; -} - -static PyObject * -Container_get_running_config_item(Container *self, PyObject *args, - PyObject *kwds) -{ - static char *kwlist[] = {"key", NULL}; - char* key = NULL; - char* value = NULL; - PyObject *ret = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, - &key)) - return NULL; - - value = self->container->get_running_config_item(self->container, key); - - if (!value) - Py_RETURN_NONE; - - ret = PyUnicode_FromString(value); - free(value); - return ret; -} - - -static PyObject * -Container_load_config(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"path", NULL}; - PyObject *fs_path = NULL; - char* path = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, - PyUnicode_FSConverter, &fs_path)) - return NULL; - - if (fs_path != NULL) { - path = PyBytes_AS_STRING(fs_path); - assert(path != NULL); - } - - if (self->container->load_config(self->container, path)) { - Py_XDECREF(fs_path); - Py_RETURN_TRUE; - } - - Py_XDECREF(fs_path); - Py_RETURN_FALSE; -} - -static PyObject * -Container_reboot(Container *self, PyObject *args, PyObject *kwds) -{ - if (self->container->reboot(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_rename(Container *self, PyObject *args, PyObject *kwds) -{ - char *new_name = NULL; - static char *kwlist[] = {"new_name", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, - &new_name)) - return NULL; - - if (self->container->rename(self->container, new_name)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_remove_device_node(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"src_path", "dest_path", NULL}; - char *src_path = NULL; - char *dst_path = NULL; - PyObject *py_src_path = NULL; - PyObject *py_dst_path = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, - PyUnicode_FSConverter, &py_src_path, - PyUnicode_FSConverter, &py_dst_path)) - return NULL; - - if (py_src_path != NULL) { - src_path = PyBytes_AS_STRING(py_src_path); - assert(src_path != NULL); - } - - if (py_dst_path != NULL) { - dst_path = PyBytes_AS_STRING(py_dst_path); - assert(dst_path != NULL); - } - - if (self->container->remove_device_node(self->container, src_path, - dst_path)) { - Py_XDECREF(py_src_path); - Py_XDECREF(py_dst_path); - Py_RETURN_TRUE; - } - - Py_XDECREF(py_src_path); - Py_XDECREF(py_dst_path); - Py_RETURN_FALSE; -} - -static PyObject * -Container_save_config(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"path", NULL}; - PyObject *fs_path = NULL; - char* path = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, - PyUnicode_FSConverter, &fs_path)) - return NULL; - - if (fs_path != NULL) { - path = PyBytes_AS_STRING(fs_path); - assert(path != NULL); - } - - if (self->container->save_config(self->container, path)) { - Py_XDECREF(fs_path); - Py_RETURN_TRUE; - } - - Py_XDECREF(fs_path); - Py_RETURN_FALSE; -} - -static PyObject * -Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", "value", NULL}; - char *key = NULL; - char *value = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, - &key, &value)) - return NULL; - - if (self->container->set_cgroup_item(self->container, key, value)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"key", "value", NULL}; - char *key = NULL; - char *value = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, - &key, &value)) - return NULL; - - if (self->container->set_config_item(self->container, key, value)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_set_config_path(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"path", NULL}; - char *path = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, - &path)) - return NULL; - - if (self->container->set_config_path(self->container, path)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_shutdown(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"timeout", NULL}; - int timeout = -1; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, - &timeout)) - return NULL; - - if (self->container->shutdown(self->container, timeout)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_snapshot(Container *self, PyObject *args, PyObject *kwds) -{ - char *comment_path = NULL; - static char *kwlist[] = {"comment_path", NULL}; - int retval = 0; - int ret = 0; - char newname[20]; - PyObject *py_comment_path = NULL; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, - PyUnicode_FSConverter, &py_comment_path)) - return NULL; - - if (py_comment_path != NULL) { - comment_path = PyBytes_AS_STRING(py_comment_path); - assert(comment_path != NULL); - } - - retval = self->container->snapshot(self->container, comment_path); - - Py_XDECREF(py_comment_path); - - if (retval < 0) { - Py_RETURN_FALSE; - } - - ret = snprintf(newname, 20, "snap%d", retval); - if (ret < 0 || ret >= 20) - return NULL; - - - return PyUnicode_FromString(newname); -} - -static PyObject * -Container_snapshot_destroy(Container *self, PyObject *args, PyObject *kwds) -{ - char *name = NULL; - static char *kwlist[] = {"name", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, - &name)) - return NULL; - - if (self->container->snapshot_destroy(self->container, name)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_snapshot_list(Container *self, PyObject *args, PyObject *kwds) -{ - struct lxc_snapshot *snap; - int snap_count = 0; - PyObject *list = NULL; - int i = 0; - - snap_count = self->container->snapshot_list(self->container, &snap); - - if (snap_count < 0) { - PyErr_SetString(PyExc_KeyError, "Unable to list snapshots"); - return NULL; - } - - list = PyTuple_New(snap_count); - for (i = 0; i < snap_count; i++) { - PyObject *list_entry = NULL; - - list_entry = PyTuple_New(4); - PyTuple_SET_ITEM(list_entry, 0, - PyUnicode_FromString(snap[i].name)); - PyTuple_SET_ITEM(list_entry, 1, - PyUnicode_FromString(snap[i].comment_pathname)); - PyTuple_SET_ITEM(list_entry, 2, - PyUnicode_FromString(snap[i].timestamp)); - PyTuple_SET_ITEM(list_entry, 3, - PyUnicode_FromString(snap[i].lxcpath)); - - snap[i].free(&snap[i]); - - PyTuple_SET_ITEM(list, i, list_entry); - } - - return list; -} - - -static PyObject * -Container_snapshot_restore(Container *self, PyObject *args, PyObject *kwds) -{ - char *name = NULL; - char *newname = NULL; - static char *kwlist[] = {"name", "newname", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, - &name, &newname)) - return NULL; - - if (self->container->snapshot_restore(self->container, name, newname)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_start(Container *self, PyObject *args, PyObject *kwds) -{ - PyObject *useinit = NULL; - PyObject *daemonize = NULL; - PyObject *close_fds = NULL; - - PyObject *vargs = NULL; - char** init_args = {NULL}; - - PyObject *retval = NULL; - int init_useinit = 0, i = 0; - static char *kwlist[] = {"useinit", "daemonize", "close_fds", - "cmd", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, - &useinit, &daemonize, &close_fds, - &vargs)) - return NULL; - - if (useinit && useinit == Py_True) { - init_useinit = 1; - } - - if (vargs && PyTuple_Check(vargs)) { - init_args = convert_tuple_to_char_pointer_array(vargs); - if (!init_args) { - return NULL; - } - } - - if (close_fds && close_fds == Py_True) { - self->container->want_close_all_fds(self->container, true); - } - else { - self->container->want_close_all_fds(self->container, false); - } - - if (!daemonize || daemonize == Py_True) { - self->container->want_daemonize(self->container, true); - } - else { - self->container->want_daemonize(self->container, false); - } - - if (self->container->start(self->container, init_useinit, init_args)) - retval = Py_True; - else - retval = Py_False; - - if (vargs) { - /* We cannot have gotten here unless vargs was given and create_args - * was successfully allocated. - */ - for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) - free(init_args[i]); - free(init_args); - } - - Py_INCREF(retval); - return retval; -} - -static PyObject * -Container_stop(Container *self, PyObject *args, PyObject *kwds) -{ - if (self->container->stop(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_unfreeze(Container *self, PyObject *args, PyObject *kwds) -{ - if (self->container->unfreeze(self->container)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static PyObject * -Container_wait(Container *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"state", "timeout", NULL}; - char *state = NULL; - int timeout = -1; - - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, - &state, &timeout)) - return NULL; - - if (self->container->wait(self->container, state, timeout)) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -/* Function/Properties list */ -static PyGetSetDef Container_getseters[] = { - {"config_file_name", - (getter)Container_config_file_name, NULL, - "Path to the container configuration", - NULL}, - {"controllable", - (getter)Container_controllable, NULL, - "Boolean indicating whether the container may be controlled", - NULL}, - {"defined", - (getter)Container_defined, NULL, - "Boolean indicating whether the container configuration exists", - NULL}, - {"init_pid", - (getter)Container_init_pid, NULL, - "PID of the container's init process in the host's PID namespace", - NULL}, - {"name", - (getter)Container_name, NULL, - "Container name", - NULL}, - {"running", - (getter)Container_running, NULL, - "Boolean indicating whether the container is running or not", - NULL}, - {"state", - (getter)Container_state, NULL, - "Container state", - NULL}, - {NULL, NULL, NULL, NULL, NULL} -}; - -static PyMethodDef Container_methods[] = { - {"attach_interface", (PyCFunction)Container_attach_interface, - METH_VARARGS|METH_KEYWORDS, - "attach_interface(src_ifname, dest_ifname) -> boolean\n" - "\n" - "Pass a new network device to the container." - }, - {"detach_interface", (PyCFunction)Container_detach_interface, - METH_VARARGS|METH_KEYWORDS, - "detach_interface(ifname) -> boolean\n" - "\n" - "detach a network device from the container." - }, - {"add_device_node", (PyCFunction)Container_add_device_node, - METH_VARARGS|METH_KEYWORDS, - "add_device_node(src_path, dest_path) -> boolean\n" - "\n" - "Pass a new device to the container." - }, - {"attach", (PyCFunction)Container_attach, - METH_VARARGS|METH_KEYWORDS, - "attach(run, payload) -> int\n" - "\n" - "Attach to the container. Returns the pid of the attached process." - }, - {"attach_wait", (PyCFunction)Container_attach_wait, - METH_VARARGS|METH_KEYWORDS, - "attach(run, payload) -> int\n" - "\n" - "Attach to the container. Returns the exit code of the process." - }, - {"clear_config", (PyCFunction)Container_clear_config, - METH_NOARGS, - "clear_config()\n" - "\n" - "Clear any container configuration." - }, - {"clear_config_item", (PyCFunction)Container_clear_config_item, - METH_VARARGS|METH_KEYWORDS, - "clear_config_item(key) -> boolean\n" - "\n" - "Clear the current value of a config key." - }, - {"console", (PyCFunction)Container_console, - METH_VARARGS|METH_KEYWORDS, - "console(ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, " - "escape = 0) -> boolean\n" - "\n" - "Attach to container's console." - }, - {"console_getfd", (PyCFunction)Container_console_getfd, - METH_VARARGS|METH_KEYWORDS, - "console(ttynum = -1) -> boolean\n" - "\n" - "Attach to container's console." - }, - {"clone", (PyCFunction)Container_clone, - METH_VARARGS|METH_KEYWORDS, - "clone(newname, config_path, flags, bdevtype, bdevdata, newsize, " - "hookargs) -> boolean\n" - "\n" - "Create a new container based on the current one." - }, - {"create", (PyCFunction)Container_create, - METH_VARARGS|METH_KEYWORDS, - "create(template, args = (,)) -> boolean\n" - "\n" - "Create a new rootfs for the container, using the given template " - "and passing some optional arguments to it." - }, - {"destroy", (PyCFunction)Container_destroy, - METH_NOARGS, - "destroy() -> boolean\n" - "\n" - "Destroys the container." - }, - {"freeze", (PyCFunction)Container_freeze, - METH_NOARGS, - "freeze() -> boolean\n" - "\n" - "Freezes the container and returns its return code." - }, - {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, - METH_VARARGS|METH_KEYWORDS, - "get_cgroup_item(key) -> string\n" - "\n" - "Get the current value of a cgroup entry." - }, - {"get_config_item", (PyCFunction)Container_get_config_item, - METH_VARARGS|METH_KEYWORDS, - "get_config_item(key) -> string\n" - "\n" - "Get the current value of a config key." - }, - {"get_config_path", (PyCFunction)Container_get_config_path, - METH_NOARGS, - "get_config_path() -> string\n" - "\n" - "Return the LXC config path (where the containers are stored)." - }, - {"get_keys", (PyCFunction)Container_get_keys, - METH_VARARGS|METH_KEYWORDS, - "get_keys(key) -> string\n" - "\n" - "Get a list of valid sub-keys for a key." - }, - {"get_interfaces", (PyCFunction)Container_get_interfaces, - METH_NOARGS, - "get_interface() -> tuple\n" - "\n" - "Get a tuple of interfaces for the container." - }, - {"get_ips", (PyCFunction)Container_get_ips, - METH_VARARGS|METH_KEYWORDS, - "get_ips(interface, family, scope) -> tuple\n" - "\n" - "Get a tuple of IPs for the container." - }, - {"get_running_config_item", (PyCFunction)Container_get_running_config_item, - METH_VARARGS|METH_KEYWORDS, - "get_running_config_item(key) -> string\n" - "\n" - "Get the runtime value of a config key." - }, - {"load_config", (PyCFunction)Container_load_config, - METH_VARARGS|METH_KEYWORDS, - "load_config(path = DEFAULT) -> boolean\n" - "\n" - "Read the container configuration from its default " - "location or from an alternative location if provided." - }, - {"reboot", (PyCFunction)Container_reboot, - METH_NOARGS, - "reboot() -> boolean\n" - "\n" - "Ask the container to reboot." - }, - {"rename", (PyCFunction)Container_rename, - METH_VARARGS|METH_KEYWORDS, - "rename(new_name) -> boolean\n" - "\n" - "Rename the container." - }, - {"remove_device_node", (PyCFunction)Container_remove_device_node, - METH_VARARGS|METH_KEYWORDS, - "remove_device_node(src_path, dest_path) -> boolean\n" - "\n" - "Remove a device from the container." - }, - {"save_config", (PyCFunction)Container_save_config, - METH_VARARGS|METH_KEYWORDS, - "save_config(path = DEFAULT) -> boolean\n" - "\n" - "Save the container configuration to its default " - "location or to an alternative location if provided." - }, - {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, - METH_VARARGS|METH_KEYWORDS, - "set_cgroup_item(key, value) -> boolean\n" - "\n" - "Set a cgroup entry to the provided value." - }, - {"set_config_item", (PyCFunction)Container_set_config_item, - METH_VARARGS|METH_KEYWORDS, - "set_config_item(key, value) -> boolean\n" - "\n" - "Set a config key to the provided value." - }, - {"set_config_path", (PyCFunction)Container_set_config_path, - METH_VARARGS|METH_KEYWORDS, - "set_config_path(path) -> boolean\n" - "\n" - "Set the LXC config path (where the containers are stored)." - }, - {"shutdown", (PyCFunction)Container_shutdown, - METH_VARARGS|METH_KEYWORDS, - "shutdown(timeout = -1) -> boolean\n" - "\n" - "Sends SIGPWR to the container and wait for it to shutdown." - "-1 means wait forever, 0 means skip waiting." - }, - {"snapshot", (PyCFunction)Container_snapshot, - METH_VARARGS|METH_KEYWORDS, - "snapshot(comment_path = None) -> string\n" - "\n" - "Snapshot the container and return the snapshot name " - "(or False on error)." - }, - {"snapshot_destroy", (PyCFunction)Container_snapshot_destroy, - METH_VARARGS|METH_KEYWORDS, - "snapshot_destroy(name) -> boolean\n" - "\n" - "Destroy a snapshot." - }, - {"snapshot_list", (PyCFunction)Container_snapshot_list, - METH_NOARGS, - "snapshot_list() -> tuple of snapshot tuples\n" - "\n" - "List all snapshots for a container." - }, - {"snapshot_restore", (PyCFunction)Container_snapshot_restore, - METH_VARARGS|METH_KEYWORDS, - "snapshot_restore(name, newname = None) -> boolean\n" - "\n" - "Restore a container snapshot. If newname is provided a new " - "container will be created from the snapshot, otherwise an in-place " - "restore will be attempted." - }, - {"start", (PyCFunction)Container_start, - METH_VARARGS|METH_KEYWORDS, - "start(useinit = False, daemonize=True, close_fds=False, " - "cmd = (,)) -> boolean\n" - "\n" - "Start the container, return True on success.\n" - "When set useinit will make LXC use lxc-init to start the container.\n" - "The container can be started in the foreground with daemonize=False.\n" - "All fds may also be closed by passing close_fds=True." - }, - {"stop", (PyCFunction)Container_stop, - METH_NOARGS, - "stop() -> boolean\n" - "\n" - "Stop the container and returns its return code." - }, - {"unfreeze", (PyCFunction)Container_unfreeze, - METH_NOARGS, - "unfreeze() -> boolean\n" - "\n" - "Unfreezes the container and returns its return code." - }, - {"wait", (PyCFunction)Container_wait, - METH_VARARGS|METH_KEYWORDS, - "wait(state, timeout = -1) -> boolean\n" - "\n" - "Wait for the container to reach a given state or timeout." - }, - {NULL, NULL, 0, NULL} -}; - -static PyTypeObject _lxc_ContainerType = { -PyVarObject_HEAD_INIT(NULL, 0) - "lxc.Container", /* tp_name */ - sizeof(Container), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Container_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Container objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Container_methods, /* tp_methods */ - 0, /* tp_members */ - Container_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Container_init, /* tp_init */ - 0, /* tp_alloc */ - Container_new, /* tp_new */ -}; - -static PyMethodDef LXC_methods[] = { - {"arch_to_personality", (PyCFunction)LXC_arch_to_personality, METH_O, - "Returns the process personality of the corresponding architecture"}, - {"attach_run_command", (PyCFunction)LXC_attach_run_command, METH_O, - "Runs a command when attaching, to use as the run parameter for attach " - "or attach_wait"}, - {"attach_run_shell", (PyCFunction)LXC_attach_run_shell, METH_O, - "Starts up a shell when attaching, to use as the run parameter for " - "attach or attach_wait"}, - {"get_global_config_item", (PyCFunction)LXC_get_global_config_item, - METH_VARARGS|METH_KEYWORDS, - "Returns the current LXC config path"}, - {"get_version", (PyCFunction)LXC_get_version, METH_NOARGS, - "Returns the current LXC library version"}, - {"list_containers", (PyCFunction)LXC_list_containers, - METH_VARARGS|METH_KEYWORDS, - "Returns a list of container names or objects"}, - {NULL, NULL, 0, NULL} -}; - -static PyModuleDef _lxcmodule = { - PyModuleDef_HEAD_INIT, - "_lxc", - "Binding for liblxc in python", - -1, - LXC_methods -}; - -PyMODINIT_FUNC -PyInit__lxc(void) -{ - PyObject* m; - PyObject* d; - - if (PyType_Ready(&_lxc_ContainerType) < 0) - return NULL; - - m = PyModule_Create(&_lxcmodule); - if (m == NULL) - return NULL; - - Py_INCREF(&_lxc_ContainerType); - PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType); - - /* add constants */ - d = PyModule_GetDict(m); - - #define PYLXC_EXPORT_CONST(c) \ - PyDict_SetItemString(d, #c, PyLong_FromLong(c)) - - /* namespace flags (no other python lib exports this) */ - PYLXC_EXPORT_CONST(CLONE_NEWUTS); - PYLXC_EXPORT_CONST(CLONE_NEWIPC); - PYLXC_EXPORT_CONST(CLONE_NEWUSER); - PYLXC_EXPORT_CONST(CLONE_NEWPID); - PYLXC_EXPORT_CONST(CLONE_NEWNET); - PYLXC_EXPORT_CONST(CLONE_NEWNS); - - /* attach: environment variable handling */ - PYLXC_EXPORT_CONST(LXC_ATTACH_CLEAR_ENV); - PYLXC_EXPORT_CONST(LXC_ATTACH_KEEP_ENV); - - /* attach: attach options */ - PYLXC_EXPORT_CONST(LXC_ATTACH_DEFAULT); - PYLXC_EXPORT_CONST(LXC_ATTACH_DROP_CAPABILITIES); - PYLXC_EXPORT_CONST(LXC_ATTACH_LSM_EXEC); - PYLXC_EXPORT_CONST(LXC_ATTACH_LSM_NOW); - PYLXC_EXPORT_CONST(LXC_ATTACH_MOVE_TO_CGROUP); - PYLXC_EXPORT_CONST(LXC_ATTACH_REMOUNT_PROC_SYS); - PYLXC_EXPORT_CONST(LXC_ATTACH_SET_PERSONALITY); - - /* clone: clone flags */ - PYLXC_EXPORT_CONST(LXC_CLONE_KEEPBDEVTYPE); - PYLXC_EXPORT_CONST(LXC_CLONE_KEEPMACADDR); - PYLXC_EXPORT_CONST(LXC_CLONE_KEEPNAME); - PYLXC_EXPORT_CONST(LXC_CLONE_MAYBE_SNAPSHOT); - PYLXC_EXPORT_CONST(LXC_CLONE_SNAPSHOT); - - /* create: create flags */ - PYLXC_EXPORT_CONST(LXC_CREATE_QUIET); - - #undef PYLXC_EXPORT_CONST - - return m; -} - -/* - * kate: space-indent on; indent-width 4; mixedindent off; indent-mode cstyle; - */ diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py deleted file mode 100644 index ccc4d18bb..000000000 --- a/src/python-lxc/lxc/__init__.py +++ /dev/null @@ -1,510 +0,0 @@ -# -# -*- coding: utf-8 -*- -# python-lxc: Python bindings for LXC -# -# (C) Copyright Canonical Ltd. 2012 -# -# Authors: -# Stéphane Graber -# -# This library 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. -# -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -# USA -# - -import _lxc -import os -import subprocess -import time - -default_config_path = _lxc.get_global_config_item("lxc.lxcpath") -get_global_config_item = _lxc.get_global_config_item -version = _lxc.get_version() - - -class ContainerNetwork(object): - props = {} - - def __init__(self, container, index): - self.container = container - self.index = index - - for key in self.container.get_keys("lxc.net.%s" % self.index): - if "." in key: - self.props[key.replace(".", "_")] = key - else: - self.props[key] = key - - if not self.props: - return False - - def __delattr__(self, key): - if key in ["container", "index", "props"]: - return object.__delattr__(self, key) - - if key not in self.props: - raise AttributeError("'%s' network has no attribute '%s'" % ( - self.__get_network_item("type"), key)) - - return self.__clear_network_item(self.props[key]) - - def __dir__(self): - return sorted(self.props.keys()) - - def __getattr__(self, key): - if key in ["container", "index", "props"]: - return object.__getattribute__(self, key) - - if key not in self.props: - raise AttributeError("'%s' network has no attribute '%s'" % ( - self.__get_network_item("type"), key)) - - return self.__get_network_item(self.props[key]) - - def __hasattr__(self, key): - if key in ["container", "index", "props"]: - return object.__hasattr__(self, key) - - if key not in self.props: - raise AttributeError("'%s' network has no attribute '%s'" % ( - self.__get_network_item("type"), key)) - - return True - - def __repr__(self): - return "'%s' network at index '%s'" % ( - self.__get_network_item("type"), self.index) - - def __setattr__(self, key, value): - if key in ["container", "index", "props"]: - return object.__setattr__(self, key, value) - - if key not in self.props: - raise AttributeError("'%s' network has no attribute '%s'" % ( - self.__get_network_item("type"), key)) - - return self.__set_network_item(self.props[key], value) - - def __clear_network_item(self, key): - if key in ("ipv4", "ipv6"): - return self.container.clear_config_item("lxc.net.%s.%s" % ( - self.index, key)) - else: - return self.container.set_config_item("lxc.net.%s.%s" % ( - self.index, key), "") - - def __get_network_item(self, key): - return self.container.get_config_item("lxc.net.%s.%s" % ( - self.index, key)) - - def __set_network_item(self, key, value): - return self.container.set_config_item("lxc.net.%s.%s" % ( - self.index, key), value) - - -class ContainerNetworkList(): - def __init__(self, container): - self.container = container - - def __getitem__(self, index): - if index >= len(self): - raise IndexError("list index out of range") - - return ContainerNetwork(self.container, index) - - def __len__(self): - values = self.container.get_config_item("lxc.net") - - if values: - return len(values) - else: - return 0 - - def add(self, network_type): - index = len(self) - - return self.container.set_config_item("lxc.net.%s.type" % index, - network_type) - - def remove(self, index): - count = len(self) - if index >= count: - raise IndexError("list index out of range") - - return self.container.clear_config_item("lxc.net.%s" % index) - - -class Container(_lxc.Container): - def __init__(self, name, config_path=None): - """ - Creates a new Container instance. - """ - - if config_path: - _lxc.Container.__init__(self, name, config_path) - else: - _lxc.Container.__init__(self, name) - - self.network = ContainerNetworkList(self) - - def add_device_net(self, name, destname=None): - """ - Add network device to running container. - """ - - if not self.running: - return False - - if os.path.exists("/sys/class/net/%s/phy80211/name" % name): - with open("/sys/class/net/%s/phy80211/name" % name) as fd: - phy = fd.read().strip() - - if subprocess.call(['iw', 'phy', phy, 'set', 'netns', - str(self.init_pid)]) != 0: - return False - - if destname: - def rename_interface(args): - old, new = args - - return subprocess.call(['ip', 'link', 'set', - 'dev', old, 'name', new]) - - return self.attach_wait(rename_interface, (name, destname), - namespaces=(CLONE_NEWNET)) == 0 - - return True - - if not destname: - destname = name - - if not os.path.exists("/sys/class/net/%s/" % name): - return False - - return subprocess.call(['ip', 'link', 'set', - 'dev', name, - 'netns', str(self.init_pid), - 'name', destname]) == 0 - - def append_config_item(self, key, value): - """ - Append 'value' to 'key', assuming 'key' is a list. - If 'key' isn't a list, 'value' will be set as the value of 'key'. - """ - - return _lxc.Container.set_config_item(self, key, value) - - def create(self, template=None, flags=0, args=(), bdevtype=None): - """ - Create a new rootfs for the container. - - "template" if passed must be a valid template name. - - "flags" (optional) is an integer representing the optional - create flags to be passed. - - "args" (optional) is a tuple of arguments to pass to the - template. It can also be provided as a dict. - """ - if isinstance(args, dict): - tmp_args = [] - for item in args.items(): - tmp_args.append("--%s" % item[0]) - tmp_args.append("%s" % item[1]) - args = tmp_args - template_args = {} - if template: - template_args['template'] = template - template_args['flags'] = flags - template_args['args'] = tuple(args) - if bdevtype: - template_args['bdevtype'] = bdevtype - return _lxc.Container.create(self, **template_args) - - def clone(self, newname, config_path=None, flags=0, bdevtype=None, - bdevdata=None, newsize=0, hookargs=()): - """ - Clone the current container. - """ - - args = {} - args['newname'] = newname - args['flags'] = flags - args['newsize'] = newsize - args['hookargs'] = hookargs - if config_path: - args['config_path'] = config_path - if bdevtype: - args['bdevtype'] = bdevtype - if bdevdata: - args['bdevdata'] = bdevdata - - if _lxc.Container.clone(self, **args): - return Container(newname, config_path=config_path) - else: - return False - - def console(self, ttynum=-1, stdinfd=0, stdoutfd=1, stderrfd=2, escape=1): - """ - Attach to console of running container. - """ - - if not self.running: - return False - - return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd, - stderrfd, escape) - - def console_getfd(self, ttynum=-1): - """ - Attach to console of running container. - """ - - if not self.running: - return False - - return _lxc.Container.console_getfd(self, ttynum) - - def get_cgroup_item(self, key): - """ - Returns the value for a given cgroup entry. - A list is returned when multiple values are set. - """ - value = _lxc.Container.get_cgroup_item(self, key) - - if value is False: - return False - else: - return value.rstrip("\n") - - def get_config_item(self, key): - """ - Returns the value for a given config key. - A list is returned when multiple values are set. - """ - value = _lxc.Container.get_config_item(self, key) - - if value is False: - return False - elif value.endswith("\n"): - return value.rstrip("\n").split("\n") - else: - return value - - def get_keys(self, key=None): - """ - Returns a list of valid sub-keys. - """ - if key: - value = _lxc.Container.get_keys(self, key) - else: - value = _lxc.Container.get_keys(self) - - if value is False: - return False - elif value.endswith("\n"): - return value.rstrip("\n").split("\n") - else: - return value - - def get_interfaces(self): - """ - Get a tuple of interfaces for the container. - """ - - return _lxc.Container.get_interfaces(self) - - def get_ips(self, interface=None, family=None, scope=None, timeout=0): - """ - Get a tuple of IPs for the container. - """ - - kwargs = {} - if interface: - kwargs['interface'] = interface - if family: - kwargs['family'] = family - if scope: - kwargs['scope'] = scope - - ips = None - timeout = int(os.environ.get('LXC_GETIP_TIMEOUT', timeout)) - - while not ips: - ips = _lxc.Container.get_ips(self, **kwargs) - if timeout == 0: - break - - timeout -= 1 - time.sleep(1) - - return ips - - def rename(self, new_name): - """ - Rename the container. - On success, returns the new Container object. - On failure, returns False. - """ - - if _lxc.Container.rename(self, new_name): - return Container(new_name) - - return False - - def set_config_item(self, key, value): - """ - Set a config key to a provided value. - The value can be a list for the keys supporting multiple values. - """ - try: - old_value = self.get_config_item(key) - except KeyError: - old_value = None - - # Check if it's a list - def set_key(key, value): - self.clear_config_item(key) - if isinstance(value, list): - for entry in value: - if not _lxc.Container.set_config_item(self, key, entry): - return False - else: - _lxc.Container.set_config_item(self, key, value) - - set_key(key, value) - new_value = self.get_config_item(key) - - # loglevel is special and won't match the string we set - if key == "lxc.log.level": - new_value = value - - if (isinstance(value, str) and isinstance(new_value, str) and - value == new_value): - return True - elif (isinstance(value, list) and isinstance(new_value, list) and - set(value) == set(new_value)): - return True - elif (isinstance(value, str) and isinstance(new_value, list) and - set([value]) == set(new_value)): - return True - elif old_value: - set_key(key, old_value) - return False - else: - self.clear_config_item(key) - return False - - def wait(self, state, timeout=-1): - """ - Wait for the container to reach a given state or timeout. - """ - - if isinstance(state, str): - state = state.upper() - - return _lxc.Container.wait(self, state, timeout) - - -def list_containers(active=True, defined=True, - as_object=False, config_path=None): - """ - List the containers on the system. - """ - - if config_path: - if not os.path.exists(config_path): - return tuple() - try: - entries = _lxc.list_containers(active=active, defined=defined, - config_path=config_path) - except ValueError: - return tuple() - else: - try: - entries = _lxc.list_containers(active=active, defined=defined) - except ValueError: - return tuple() - - if as_object: - return tuple([Container(name, config_path) for name in entries]) - else: - return entries - - -def attach_run_command(cmd): - """ - Run a command when attaching - - Please do not call directly, this will execvp the command. - This is to be used in conjunction with the attach method - of a container. - """ - if isinstance(cmd, tuple): - return _lxc.attach_run_command(cmd) - elif isinstance(cmd, list): - return _lxc.attach_run_command((cmd[0], cmd)) - else: - return _lxc.attach_run_command((cmd, [cmd])) - - -def attach_run_shell(): - """ - Run a shell when attaching - - Please do not call directly, this will execvp the shell. - This is to be used in conjunction with the attach method - of a container. - """ - return _lxc.attach_run_shell(None) - - -def arch_to_personality(arch): - """ - Determine the process personality corresponding to the architecture - """ - if isinstance(arch, bytes): - arch = str(arch, 'utf-8') - return _lxc.arch_to_personality(arch) - -# namespace flags (no other python lib exports this) -CLONE_NEWIPC = _lxc.CLONE_NEWIPC -CLONE_NEWNET = _lxc.CLONE_NEWNET -CLONE_NEWNS = _lxc.CLONE_NEWNS -CLONE_NEWPID = _lxc.CLONE_NEWPID -CLONE_NEWUSER = _lxc.CLONE_NEWUSER -CLONE_NEWUTS = _lxc.CLONE_NEWUTS - -# attach: environment variable handling -LXC_ATTACH_CLEAR_ENV = _lxc.LXC_ATTACH_CLEAR_ENV -LXC_ATTACH_KEEP_ENV = _lxc.LXC_ATTACH_KEEP_ENV - -# attach: attach options -LXC_ATTACH_DEFAULT = _lxc.LXC_ATTACH_DEFAULT -LXC_ATTACH_DROP_CAPABILITIES = _lxc.LXC_ATTACH_DROP_CAPABILITIES -LXC_ATTACH_LSM_EXEC = _lxc.LXC_ATTACH_LSM_EXEC -LXC_ATTACH_LSM_NOW = _lxc.LXC_ATTACH_LSM_NOW -LXC_ATTACH_MOVE_TO_CGROUP = _lxc.LXC_ATTACH_MOVE_TO_CGROUP -LXC_ATTACH_REMOUNT_PROC_SYS = _lxc.LXC_ATTACH_REMOUNT_PROC_SYS -LXC_ATTACH_SET_PERSONALITY = _lxc.LXC_ATTACH_SET_PERSONALITY - -# clone: clone flags -LXC_CLONE_KEEPBDEVTYPE = _lxc.LXC_CLONE_KEEPBDEVTYPE -LXC_CLONE_KEEPMACADDR = _lxc.LXC_CLONE_KEEPMACADDR -LXC_CLONE_KEEPNAME = _lxc.LXC_CLONE_KEEPNAME -LXC_CLONE_MAYBE_SNAPSHOT = _lxc.LXC_CLONE_MAYBE_SNAPSHOT -LXC_CLONE_SNAPSHOT = _lxc.LXC_CLONE_SNAPSHOT - -# create: create flags -LXC_CREATE_QUIET = _lxc.LXC_CREATE_QUIET diff --git a/src/python-lxc/setup.py b/src/python-lxc/setup.py deleted file mode 100644 index bf0d74a0c..000000000 --- a/src/python-lxc/setup.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python3 -# -# python-lxc: Python bindings for LXC -# -# (C) Copyright Canonical Ltd. 2012 -# -# Authors: -# Stéphane Graber -# -# This library 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. -# -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -# USA - -import os -import subprocess - -# Fix build when PIE is enabled (must run before setuptools import) -for var in ("LDFLAGS", "CFLAGS"): - current = os.environ.get(var, None) - if not current: - continue - - new = [] - for flag in current.split(" "): - if flag.lower() in ("-pie", "-fpie"): - if "-fPIC" not in new: - new.append("-fPIC") - continue - new.append(flag) - - os.environ[var] = " ".join(new) - -from setuptools import setup, Extension -from setuptools.command.build_ext import build_ext as BuildExtCommand - - -class LxcBuildExtCommand(BuildExtCommand): - user_options = BuildExtCommand.user_options + [ - ('no-pkg-config', None, - "don't use pkg-config to detect include/library paths") - ] - - def initialize_options(self): - super(LxcBuildExtCommand, self).initialize_options() - self.no_pkg_config = False - - def build_extensions(self): - if not self.no_pkg_config: - pkg_config_executable = os.environ.get('PKG_CONFIG_EXECUTABLE', - 'pkg-config') - - def get_pkg_config_var(name): - args = [pkg_config_executable, '--variable', name, 'lxc'] - output = subprocess.check_output(args, - universal_newlines=True) - return output.rstrip('\n') - - try: - includedir = get_pkg_config_var('includedir') - libdir = get_pkg_config_var('libdir') - - self.compiler.add_include_dir(includedir) - self.compiler.add_library_dir(libdir) - - except subprocess.CalledProcessError: - pass - - super(LxcBuildExtCommand, self).build_extensions() - - -setup(name='lxc', - version='0.1', - description='LXC', - packages=['lxc'], - package_dir={'lxc': 'lxc'}, - ext_modules=[Extension('_lxc', sources=['lxc.c'], libraries=['lxc'])], - cmdclass={'build_ext': LxcBuildExtCommand}, - )