├── MANIFEST.in ├── .gitignore ├── setup.cfg ├── README.md ├── .github └── workflows │ ├── commits.yml │ └── basic-tests.yml ├── examples ├── pyconsole.py ├── pyconsole-vte.py └── api_test.py ├── setup.py ├── lxc └── __init__.py ├── COPYING └── lxc.c /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | recursive-include examples * 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | build/* 3 | dist/* 4 | *.pyc 5 | python3_lxc.egg-info/ 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_rpm] 2 | group=System Environment/Libraries 3 | requires=lxc-libs 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python3-lxc 2 | 3 | This repository provides python3 bindings for the [LXC](https://github.com/lxc/lxc) 4 | container API. 5 | 6 | # Build 7 | 8 | - Build: 9 | ``` 10 | python3 setup.py build 11 | ``` 12 | - Install: 13 | ``` 14 | python3 setup.py install 15 | ``` 16 | - Build rpm: 17 | ``` 18 | python3 setup.py bdist_rpm 19 | ``` 20 | -------------------------------------------------------------------------------- /.github/workflows/commits.yml: -------------------------------------------------------------------------------- 1 | name: Commits 2 | on: 3 | - pull_request 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | dco-check: 10 | permissions: 11 | pull-requests: read # for tim-actions/get-pr-commits to get list of commits from the PR 12 | name: Signed-off-by (DCO) 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Get PR Commits 16 | id: 'get-pr-commits' 17 | uses: tim-actions/get-pr-commits@master 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | 21 | - name: Check that all commits are signed-off 22 | uses: tim-actions/dco@master 23 | with: 24 | commits: ${{ steps.get-pr-commits.outputs.commits }} 25 | 26 | target-branch: 27 | permissions: 28 | contents: none 29 | name: Branch target 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Check branch target 33 | env: 34 | TARGET: ${{ github.event.pull_request.base.ref }} 35 | run: | 36 | set -x 37 | [ "${TARGET}" = "main" ] && exit 0 38 | 39 | echo "Invalid branch target: ${TARGET}" 40 | exit 1 41 | -------------------------------------------------------------------------------- /examples/pyconsole.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # pyconsole: Example program showing use of console functions 5 | # in the lxc python binding 6 | # 7 | # (C) Copyright Oracle. 2013 8 | # 9 | # Authors: 10 | # Dwight Engen 11 | # 12 | # This library is free software; you can redistribute it and/or 13 | # modify it under the terms of the GNU Lesser General Public 14 | # License as published by the Free Software Foundation; either 15 | # version 2.1 of the License, or (at your option) any later version. 16 | # 17 | # This library is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | # Lesser General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Lesser General Public 23 | # License along with this library; if not, write to the Free Software 24 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 25 | # USA 26 | # 27 | 28 | import lxc 29 | import sys 30 | import time 31 | 32 | if __name__ == '__main__': 33 | ttynum = -1 34 | escape = 1 35 | if len(sys.argv) < 2: 36 | sys.exit("Usage: %s container-name [ttynum [escape]]" % sys.argv[0]) 37 | if len(sys.argv) > 2: 38 | ttynum = int(sys.argv[2]) 39 | if len(sys.argv) > 3: 40 | escape = ord(sys.argv[3]) - ord('a') + 1 41 | 42 | ct = lxc.Container(sys.argv[1]) 43 | 44 | print("Container:%s tty:%d Ctrl-%c q to quit" % 45 | (ct.name, ttynum, ord('a') + escape-1)) 46 | time.sleep(1) 47 | if not ct.defined: 48 | sys.exit("Container %s not defined" % ct.name) 49 | if not ct.running: 50 | sys.exit("Container %s not running" % ct.name) 51 | 52 | ct.console(ttynum, 0, 1, 2, escape) 53 | print("Console done") 54 | -------------------------------------------------------------------------------- /.github/workflows/basic-tests.yml: -------------------------------------------------------------------------------- 1 | name: Basic Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags-ignore: 8 | - '*' 9 | pull_request: 10 | 11 | jobs: 12 | basic-tests: 13 | runs-on: ubuntu-24.04 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: ["3.8", "3.13"] 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install setuptools 27 | run: python -m pip install setuptools 28 | 29 | - name: Install LXC 30 | run: sudo apt-get install -y lxc lxc-dev 31 | 32 | - name: Configure LXC 33 | run: | 34 | mkdir -p ~/.config/lxc /var/tmp/lxc 35 | cat >~/.config/lxc/lxc.conf <~/.config/lxc/default.conf < 57 | python3 -c 'import lxc;exit(0 if lxc.Container("mycontainer").create("download", 0, {"dist": "alpine", "release": "edge", "arch": "amd64"}) else 1)' 58 | 59 | - name: Start container 60 | run: python3 -c 'import lxc;exit(0 if lxc.Container("mycontainer").start() else 1)' 61 | 62 | - name: Run command inside container 63 | run: python3 -c 'import lxc;exit(lxc.Container("mycontainer").attach_wait(lxc.attach_run_command, ["uname", "-a"]))' 64 | 65 | - name: Stop container 66 | if: success() || failure() 67 | run: python3 -c 'import lxc;exit(0 if lxc.Container("mycontainer").stop() else 1)' 68 | 69 | - name: Destroy container 70 | if: success() || failure() 71 | run: python3 -c 'import lxc;exit(0 if lxc.Container("mycontainer").destroy() else 1)' 72 | -------------------------------------------------------------------------------- /examples/pyconsole-vte.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # pyconsole-vte: Example program showing use of console functions 5 | # in the lxc python binding 6 | # 7 | # (C) Copyright Oracle. 2013 8 | # 9 | # Authors: 10 | # Dwight Engen 11 | # 12 | # This library is free software; you can redistribute it and/or 13 | # modify it under the terms of the GNU Lesser General Public 14 | # License as published by the Free Software Foundation; either 15 | # version 2.1 of the License, or (at your option) any later version. 16 | # 17 | # This library is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | # Lesser General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Lesser General Public 23 | # License along with this library; if not, write to the Free Software 24 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 25 | # USA 26 | # 27 | 28 | import gtk 29 | import vte 30 | import lxc 31 | import sys 32 | 33 | 34 | def gtk_exit_cb(terminal): 35 | gtk.main_quit() 36 | 37 | 38 | def vte_con(ct, ttynum): 39 | print("Doing console in a VTE widget...") 40 | masterfd = ct.console_getfd(ttynum) 41 | term = vte.Terminal() 42 | term.set_cursor_blinks(True) 43 | term.set_scrollback_lines(1000) 44 | term.connect('eof', gtk_exit_cb) 45 | 46 | term.set_pty(masterfd) 47 | term.feed_child('\n') 48 | #term.feed_child('ps aux\n') 49 | 50 | vscrollbar = gtk.VScrollbar() 51 | vscrollbar.set_adjustment(term.get_adjustment()) 52 | 53 | hbox = gtk.HBox() 54 | hbox.pack_start(term) 55 | hbox.pack_start(vscrollbar) 56 | 57 | window = gtk.Window() 58 | window.add(hbox) 59 | window.connect('delete-event', lambda window, event: gtk.main_quit()) 60 | window.show_all() 61 | gtk.main() 62 | print("Console done") 63 | 64 | if __name__ == '__main__': 65 | ttynum = -1 66 | if len(sys.argv) < 2: 67 | sys.exit("Usage: %s container-name [ttynum]" % sys.argv[0]) 68 | if len(sys.argv) > 2: 69 | ttynum = int(sys.argv[2]) 70 | 71 | ct = lxc.Container(sys.argv[1]) 72 | 73 | print("Container:%s tty:%d" % (ct.name, ttynum)) 74 | if not ct.defined: 75 | sys.exit("Container %s not defined" % ct.name) 76 | if not ct.running: 77 | sys.exit("Container %s not running" % ct.name) 78 | 79 | vte_con(ct, ttynum) 80 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # python-lxc: Python bindings for LXC 6 | # 7 | # (C) Copyright Canonical Ltd. 2012 8 | # 9 | # Authors: 10 | # Stéphane Graber 11 | # 12 | # This library is free software; you can redistribute it and/or 13 | # modify it under the terms of the GNU Lesser General Public 14 | # License as published by the Free Software Foundation; either 15 | # version 2.1 of the License, or (at your option) any later version. 16 | # 17 | # This library is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | # Lesser General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Lesser General Public 23 | # License along with this library; if not, write to the Free Software 24 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 25 | # USA 26 | 27 | import glob 28 | import os 29 | import subprocess 30 | 31 | # Fix build when PIE is enabled (must run before setuptools import) 32 | for var in ("LDFLAGS", "CFLAGS"): 33 | current = os.environ.get(var, None) 34 | if not current: 35 | continue 36 | 37 | new = [] 38 | for flag in current.split(" "): 39 | if flag.lower() in ("-pie", "-fpie"): 40 | if "-fPIC" not in new: 41 | new.append("-fPIC") 42 | continue 43 | new.append(flag) 44 | 45 | os.environ[var] = " ".join(new) 46 | 47 | from setuptools import setup, Extension 48 | from setuptools.command.build_ext import build_ext as BuildExtCommand 49 | 50 | 51 | class LxcBuildExtCommand(BuildExtCommand): 52 | user_options = BuildExtCommand.user_options + [ 53 | ('no-pkg-config', None, 54 | "don't use pkg-config to detect include/library paths") 55 | ] 56 | 57 | def initialize_options(self): 58 | super(LxcBuildExtCommand, self).initialize_options() 59 | self.no_pkg_config = False 60 | 61 | def build_extensions(self): 62 | if not self.no_pkg_config: 63 | pkg_config_executable = os.environ.get('PKG_CONFIG_EXECUTABLE', 64 | 'pkg-config') 65 | 66 | def get_pkg_config_var(name): 67 | args = [pkg_config_executable, '--variable', name, 'lxc'] 68 | output = subprocess.check_output(args, 69 | universal_newlines=True) 70 | return output.rstrip('\n') 71 | 72 | try: 73 | includedir = get_pkg_config_var('includedir') 74 | libdir = get_pkg_config_var('libdir') 75 | 76 | self.compiler.add_include_dir(includedir) 77 | self.compiler.add_library_dir(libdir) 78 | 79 | except subprocess.CalledProcessError: 80 | pass 81 | 82 | super(LxcBuildExtCommand, self).build_extensions() 83 | 84 | 85 | setup(name='python3-lxc', 86 | version='5.0.0', 87 | description='Python3 bindings for LXC', 88 | long_description='The lxc-python3 package contains LXC bindings for python3', 89 | license='LGPLv2+', 90 | maintainer='lxc', 91 | maintainer_email='lxc-devel@lists.linuxcontainers.org', 92 | url='git://github.com/lxc/python3-lxc', 93 | packages=['lxc'], 94 | package_dir={'lxc': 'lxc'}, 95 | ext_modules=[Extension('_lxc', sources=['lxc.c'], libraries=['lxc'])], 96 | cmdclass={'build_ext': LxcBuildExtCommand}, 97 | ) 98 | -------------------------------------------------------------------------------- /examples/api_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # api_test.py: Test/demo of the python3-lxc API 5 | # 6 | # (C) Copyright Canonical Ltd. 2012 7 | # 8 | # Authors: 9 | # Stéphane Graber 10 | # 11 | # This library is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU Lesser General Public 13 | # License as published by the Free Software Foundation; either 14 | # version 2.1 of the License, or (at your option) any later version. 15 | # 16 | # This library is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | # Lesser General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU Lesser General Public 22 | # License along with this library; if not, write to the Free Software 23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 24 | # USA 25 | # 26 | 27 | import lxc 28 | import uuid 29 | import os 30 | import subprocess 31 | import sys 32 | import time 33 | 34 | # Let's pick a random name, avoiding clashes 35 | CONTAINER_NAME = str(uuid.uuid1()) 36 | CLONE_NAME = str(uuid.uuid1()) 37 | RENAME_NAME = str(uuid.uuid1()) 38 | 39 | ## Instantiate the container instance 40 | print("Getting instance for '%s'" % CONTAINER_NAME) 41 | container = lxc.Container(CONTAINER_NAME) 42 | 43 | # A few basic checks of the current state 44 | assert(container.config_file_name == "%s/%s/config" % 45 | (lxc.default_config_path, CONTAINER_NAME)) 46 | assert(not container.defined) 47 | assert(container.init_pid == -1) 48 | assert(container.name == CONTAINER_NAME) 49 | assert(not container.running) 50 | assert(container.state == "STOPPED") 51 | 52 | # Try to get the host architecture for dpkg systems 53 | arch = "i386" 54 | try: 55 | with open(os.path.devnull, "w") as devnull: 56 | dpkg = subprocess.Popen(['dpkg', '--print-architecture'], 57 | stderr=devnull, stdout=subprocess.PIPE, 58 | universal_newlines=True) 59 | 60 | if dpkg.wait() == 0: 61 | arch = dpkg.stdout.read().strip() 62 | except: 63 | pass 64 | 65 | ## Create a rootfs 66 | print("Creating rootfs using 'download', arch=%s" % arch) 67 | container.create("download", 0, 68 | {"dist": "ubuntu", 69 | "release": "noble", 70 | "arch": arch}) 71 | 72 | assert(container.defined) 73 | assert(container.name == CONTAINER_NAME 74 | == container.get_config_item("lxc.uts.name")) 75 | assert(container.name in lxc.list_containers()) 76 | 77 | ## Test the config 78 | print("Testing the configuration") 79 | capdrop = container.get_config_item("lxc.cap.drop") 80 | container.clear_config_item("lxc.cap.drop") 81 | container.set_config_item("lxc.cap.drop", capdrop[:-1]) 82 | container.append_config_item("lxc.cap.drop", capdrop[-1]) 83 | container.save_config() 84 | 85 | # A few basic checks of the current state 86 | assert(isinstance(capdrop, list)) 87 | assert(capdrop == container.get_config_item("lxc.cap.drop")) 88 | 89 | ## Test the networking 90 | print("Testing the networking") 91 | 92 | # A few basic checks of the current state 93 | assert("name" in container.get_keys("lxc.net.0")) 94 | assert(len(container.network) == 1) 95 | 96 | ## Starting the container 97 | print("Starting the container") 98 | container.start() 99 | container.wait("RUNNING", 3) 100 | 101 | # A few basic checks of the current state 102 | assert(container.init_pid > 1) 103 | assert(container.running) 104 | assert(container.state == "RUNNING") 105 | 106 | 107 | ## Checking IP address 108 | print("Getting the interface names") 109 | expected_ifs = set(('lo', 'eth0')) 110 | if os.path.isdir('/sys/module/sit'): 111 | expected_ifs.add('sit0') 112 | if os.path.isdir('/sys/module/ipip'): 113 | expected_ifs.add('tunl0') 114 | assert(set(container.get_interfaces()) == expected_ifs) 115 | 116 | ## Checking IP address 117 | print("Getting the IP addresses") 118 | 119 | count = 0 120 | ips = [] 121 | while not ips and count != 30: 122 | ips = container.get_ips() 123 | time.sleep(1) 124 | count += 1 125 | 126 | if os.geteuid(): 127 | container.attach_wait(lxc.attach_run_command, ["ifconfig", "eth0"], 128 | namespaces=(lxc.CLONE_NEWUSER + lxc.CLONE_NEWNET 129 | + lxc.CLONE_NEWUTS)) 130 | else: 131 | container.attach_wait(lxc.attach_run_command, ["ifconfig", "eth0"], 132 | namespaces=(lxc.CLONE_NEWNET + lxc.CLONE_NEWUTS)) 133 | 134 | # A few basic checks of the current state 135 | assert(len(ips) > 0) 136 | 137 | ## Test running config 138 | assert(container.name == CONTAINER_NAME 139 | == container.get_config_item("lxc.uts.name") 140 | == container.get_running_config_item("lxc.uts.name")) 141 | 142 | ## Testing cgroups a bit 143 | print("Testing cgroup API") 144 | max_mem = container.get_cgroup_item("memory.peak") 145 | current_limit = container.get_cgroup_item("memory.max") 146 | assert(container.set_cgroup_item("memory.max", max_mem)) 147 | assert(container.get_cgroup_item("memory.max") != current_limit) 148 | 149 | ## Freezing the container 150 | print("Freezing the container") 151 | container.freeze() 152 | container.wait("FROZEN", 3) 153 | 154 | # A few basic checks of the current state 155 | assert(container.init_pid > 1) 156 | assert(container.running) 157 | assert(container.state == "FROZEN") 158 | 159 | ## Unfreezing the container 160 | print("Unfreezing the container") 161 | container.unfreeze() 162 | container.wait("RUNNING", 3) 163 | 164 | # A few basic checks of the current state 165 | assert(container.init_pid > 1) 166 | assert(container.running) 167 | assert(container.state == "RUNNING") 168 | 169 | if len(sys.argv) > 1 and sys.argv[1] == "--with-console": 170 | ## Attaching to tty1 171 | print("Attaching to tty1") 172 | container.console(tty=1) 173 | 174 | ## Shutting down the container 175 | print("Shutting down the container") 176 | if not container.shutdown(3): 177 | container.stop() 178 | 179 | if container.running: 180 | print("Stopping the container") 181 | container.stop() 182 | container.wait("STOPPED", 3) 183 | 184 | # A few basic checks of the current state 185 | assert(container.init_pid == -1) 186 | assert(not container.running) 187 | assert(container.state == "STOPPED") 188 | 189 | ## Snapshotting the container 190 | print("Snapshotting the container") 191 | assert(not container.snapshot_list()) 192 | assert(container.snapshot() == "snap0") 193 | assert(len(container.snapshot_list()) == 1) 194 | assert(container.snapshot_restore("snap0") is True) 195 | assert(container.snapshot_destroy("snap0") is True) 196 | 197 | ## Cloning the container 198 | print("Cloning the container as '%s'" % CLONE_NAME) 199 | clone = container.clone(CLONE_NAME) 200 | assert(clone is not False) 201 | 202 | print ("Renaming the clone to '%s'" % RENAME_NAME) 203 | rename = clone.rename(RENAME_NAME) 204 | rename.start() 205 | rename.stop() 206 | rename.destroy() 207 | 208 | ## Destroy the container 209 | print("Destroying the container") 210 | container.destroy() 211 | 212 | assert(not container.defined) 213 | -------------------------------------------------------------------------------- /lxc/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # -*- coding: utf-8 -*- 3 | # python-lxc: Python bindings for LXC 4 | # 5 | # (C) Copyright Canonical Ltd. 2012 6 | # 7 | # Authors: 8 | # Stéphane Graber 9 | # 10 | # This library is free software; you can redistribute it and/or 11 | # modify it under the terms of the GNU Lesser General Public 12 | # License as published by the Free Software Foundation; either 13 | # version 2.1 of the License, or (at your option) any later version. 14 | # 15 | # This library is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | # Lesser General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU Lesser General Public 21 | # License along with this library; if not, write to the Free Software 22 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 23 | # USA 24 | # 25 | 26 | import _lxc 27 | import os 28 | import subprocess 29 | import time 30 | 31 | default_config_path = _lxc.get_global_config_item("lxc.lxcpath") 32 | get_global_config_item = _lxc.get_global_config_item 33 | version = _lxc.get_version() 34 | 35 | 36 | class ContainerNetwork(object): 37 | props = {} 38 | 39 | def __init__(self, container, index): 40 | self.container = container 41 | self.index = index 42 | 43 | for key in self.container.get_keys("lxc.net.%s" % self.index): 44 | if "." in key: 45 | self.props[key.replace(".", "_")] = key 46 | else: 47 | self.props[key] = key 48 | 49 | if not self.props: 50 | return False 51 | 52 | def __delattr__(self, key): 53 | if key in ["container", "index", "props"]: 54 | return object.__delattr__(self, key) 55 | 56 | if key not in self.props: 57 | raise AttributeError("'%s' network has no attribute '%s'" % ( 58 | self.__get_network_item("type"), key)) 59 | 60 | return self.__clear_network_item(self.props[key]) 61 | 62 | def __dir__(self): 63 | return sorted(self.props.keys()) 64 | 65 | def __getattr__(self, key): 66 | if key in ["container", "index", "props"]: 67 | return object.__getattribute__(self, key) 68 | 69 | if key not in self.props: 70 | raise AttributeError("'%s' network has no attribute '%s'" % ( 71 | self.__get_network_item("type"), key)) 72 | 73 | return self.__get_network_item(self.props[key]) 74 | 75 | def __hasattr__(self, key): 76 | if key in ["container", "index", "props"]: 77 | return object.__hasattr__(self, key) 78 | 79 | if key not in self.props: 80 | raise AttributeError("'%s' network has no attribute '%s'" % ( 81 | self.__get_network_item("type"), key)) 82 | 83 | return True 84 | 85 | def __repr__(self): 86 | return "'%s' network at index '%s'" % ( 87 | self.__get_network_item("type"), self.index) 88 | 89 | def __setattr__(self, key, value): 90 | if key in ["container", "index", "props"]: 91 | return object.__setattr__(self, key, value) 92 | 93 | if key not in self.props: 94 | raise AttributeError("'%s' network has no attribute '%s'" % ( 95 | self.__get_network_item("type"), key)) 96 | 97 | return self.__set_network_item(self.props[key], value) 98 | 99 | def __clear_network_item(self, key): 100 | if key in ("ipv4", "ipv6"): 101 | return self.container.clear_config_item("lxc.net.%s.%s" % ( 102 | self.index, key)) 103 | else: 104 | return self.container.set_config_item("lxc.net.%s.%s" % ( 105 | self.index, key), "") 106 | 107 | def __get_network_item(self, key): 108 | return self.container.get_config_item("lxc.net.%s.%s" % ( 109 | self.index, key)) 110 | 111 | def __set_network_item(self, key, value): 112 | return self.container.set_config_item("lxc.net.%s.%s" % ( 113 | self.index, key), value) 114 | 115 | 116 | class ContainerNetworkList(): 117 | def __init__(self, container): 118 | self.container = container 119 | 120 | def __getitem__(self, index): 121 | if index >= len(self): 122 | # If index of network is out of bounds, create a new network. 123 | self.add("temp_type") 124 | 125 | return ContainerNetwork(self.container, index) 126 | 127 | def __len__(self): 128 | try: 129 | values = self.container.get_config_item("lxc.net") 130 | except KeyError: 131 | values = None 132 | 133 | if values: 134 | return len(values) 135 | else: 136 | return 0 137 | 138 | def add(self, network_type): 139 | index = len(self) 140 | 141 | return self.container.set_config_item("lxc.net.%s.type" % index, 142 | network_type) 143 | 144 | def remove(self, index): 145 | count = len(self) 146 | if index >= count: 147 | raise IndexError("list index out of range") 148 | 149 | return self.container.clear_config_item("lxc.net.%s" % index) 150 | 151 | 152 | class Container(_lxc.Container): 153 | def __init__(self, name, config_path=None): 154 | """ 155 | Creates a new Container instance. 156 | """ 157 | 158 | if config_path: 159 | _lxc.Container.__init__(self, name, config_path) 160 | else: 161 | _lxc.Container.__init__(self, name) 162 | 163 | self.network = ContainerNetworkList(self) 164 | 165 | def add_device_net(self, name, destname=None): 166 | """ 167 | Add network device to running container. 168 | """ 169 | 170 | if not self.running: 171 | return False 172 | 173 | if os.path.exists("/sys/class/net/%s/phy80211/name" % name): 174 | with open("/sys/class/net/%s/phy80211/name" % name) as fd: 175 | phy = fd.read().strip() 176 | 177 | if subprocess.call(['iw', 'phy', phy, 'set', 'netns', 178 | str(self.init_pid)]) != 0: 179 | return False 180 | 181 | if destname: 182 | def rename_interface(args): 183 | old, new = args 184 | 185 | return subprocess.call(['ip', 'link', 'set', 186 | 'dev', old, 'name', new]) 187 | 188 | return self.attach_wait(rename_interface, (name, destname), 189 | namespaces=(CLONE_NEWNET)) == 0 190 | 191 | return True 192 | 193 | if not destname: 194 | destname = name 195 | 196 | if not os.path.exists("/sys/class/net/%s/" % name): 197 | return False 198 | 199 | return subprocess.call(['ip', 'link', 'set', 200 | 'dev', name, 201 | 'netns', str(self.init_pid), 202 | 'name', destname]) == 0 203 | 204 | def append_config_item(self, key, value): 205 | """ 206 | Append 'value' to 'key', assuming 'key' is a list. 207 | If 'key' isn't a list, 'value' will be set as the value of 'key'. 208 | """ 209 | 210 | return _lxc.Container.set_config_item(self, key, value) 211 | 212 | def create(self, template=None, flags=0, args=(), bdevtype=None, fssize=0): 213 | """ 214 | Create a new rootfs for the container. 215 | 216 | "template" if passed must be a valid template name. 217 | 218 | "flags" (optional) is an integer representing the optional 219 | create flags to be passed. 220 | 221 | "args" (optional) is a tuple of arguments to pass to the 222 | template. It can also be provided as a dict. 223 | """ 224 | 225 | if isinstance(args, dict): 226 | tmp_args = [] 227 | for item in args.items(): 228 | tmp_args.append("--%s" % item[0]) 229 | tmp_args.append("%s" % item[1]) 230 | args = tmp_args 231 | template_args = {} 232 | if template: 233 | template_args['template'] = template 234 | template_args['flags'] = flags 235 | template_args['args'] = tuple(args) 236 | if bdevtype: 237 | template_args['bdevtype'] = bdevtype 238 | template_args['fssize'] = fssize 239 | if not self.defined: 240 | self.save_config() 241 | return _lxc.Container.create(self, **template_args) 242 | 243 | def destroy(self): 244 | """ 245 | Deletes the container and clears its config 246 | """ 247 | 248 | if not _lxc.Container.destroy(self): 249 | return False 250 | 251 | # Clear the configuration to match destroyed container state. 252 | self.clear_config() 253 | 254 | return True 255 | 256 | def clone(self, newname, config_path=None, flags=0, bdevtype=None, 257 | bdevdata=None, newsize=0, hookargs=()): 258 | """ 259 | Clone the current container. 260 | """ 261 | 262 | args = {} 263 | args['newname'] = newname 264 | args['flags'] = flags 265 | args['newsize'] = newsize 266 | args['hookargs'] = hookargs 267 | if config_path: 268 | args['config_path'] = config_path 269 | if bdevtype: 270 | args['bdevtype'] = bdevtype 271 | if bdevdata: 272 | args['bdevdata'] = bdevdata 273 | 274 | if _lxc.Container.clone(self, **args): 275 | return Container(newname, config_path=config_path) 276 | else: 277 | return False 278 | 279 | def console(self, ttynum=-1, stdinfd=0, stdoutfd=1, stderrfd=2, escape=1): 280 | """ 281 | Attach to console of running container. 282 | """ 283 | 284 | if not self.running: 285 | return False 286 | 287 | return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd, 288 | stderrfd, escape) 289 | 290 | def console_getfd(self, ttynum=-1): 291 | """ 292 | Attach to console of running container. 293 | """ 294 | 295 | if not self.running: 296 | return False 297 | 298 | return _lxc.Container.console_getfd(self, ttynum) 299 | 300 | def get_cgroup_item(self, key): 301 | """ 302 | Returns the value for a given cgroup entry. 303 | A list is returned when multiple values are set. 304 | """ 305 | value = _lxc.Container.get_cgroup_item(self, key) 306 | 307 | if value is False: 308 | return False 309 | else: 310 | return value.rstrip("\n") 311 | 312 | def get_config_item(self, key): 313 | """ 314 | Returns the value for a given config key. 315 | A list is returned when multiple values are set. 316 | """ 317 | value = _lxc.Container.get_config_item(self, key) 318 | 319 | if value is False: 320 | return False 321 | elif value.endswith("\n"): 322 | return value.rstrip("\n").split("\n") 323 | else: 324 | return value 325 | 326 | def get_keys(self, key=None): 327 | """ 328 | Returns a list of valid sub-keys. 329 | """ 330 | if key: 331 | value = _lxc.Container.get_keys(self, key) 332 | else: 333 | value = _lxc.Container.get_keys(self) 334 | 335 | if value is False: 336 | return False 337 | elif value.endswith("\n"): 338 | return value.rstrip("\n").split("\n") 339 | else: 340 | return value 341 | 342 | def get_interfaces(self): 343 | """ 344 | Get a tuple of interfaces for the container. 345 | """ 346 | 347 | return _lxc.Container.get_interfaces(self) 348 | 349 | def get_ips(self, interface=None, family=None, scope=None, timeout=0): 350 | """ 351 | Get a tuple of IPs for the container. 352 | """ 353 | 354 | kwargs = {} 355 | if interface: 356 | kwargs['interface'] = interface 357 | if family: 358 | kwargs['family'] = family 359 | if scope: 360 | kwargs['scope'] = scope 361 | 362 | ips = None 363 | timeout = int(os.environ.get('LXC_GETIP_TIMEOUT', timeout)) 364 | 365 | while not ips: 366 | ips = _lxc.Container.get_ips(self, **kwargs) 367 | if timeout == 0: 368 | break 369 | 370 | timeout -= 1 371 | time.sleep(1) 372 | 373 | return ips 374 | 375 | def rename(self, new_name): 376 | """ 377 | Rename the container. 378 | On success, returns the new Container object. 379 | On failure, returns False. 380 | """ 381 | 382 | if _lxc.Container.rename(self, new_name): 383 | return Container(new_name) 384 | 385 | return False 386 | 387 | def set_config_item(self, key, value): 388 | """ 389 | Set a config key to a provided value. 390 | The value can be a list for the keys supporting multiple values. 391 | """ 392 | try: 393 | old_value = self.get_config_item(key) 394 | except KeyError: 395 | old_value = None 396 | 397 | # Check if it's a list 398 | def set_key(key, value): 399 | self.clear_config_item(key) 400 | if isinstance(value, list): 401 | for entry in value: 402 | if not _lxc.Container.set_config_item(self, key, entry): 403 | return False 404 | else: 405 | _lxc.Container.set_config_item(self, key, value) 406 | 407 | set_key(key, value) 408 | new_value = self.get_config_item(key) 409 | 410 | # loglevel is special and won't match the string we set 411 | if key == "lxc.log.level": 412 | new_value = value 413 | 414 | if (isinstance(value, str) and isinstance(new_value, str) and 415 | value == new_value): 416 | return True 417 | elif (isinstance(value, list) and isinstance(new_value, list) and 418 | set(value) == set(new_value)): 419 | return True 420 | elif (isinstance(value, str) and isinstance(new_value, list) and 421 | set([value]) == set(new_value)): 422 | return True 423 | elif old_value: 424 | set_key(key, old_value) 425 | return False 426 | else: 427 | self.clear_config_item(key) 428 | return False 429 | 430 | def wait(self, state, timeout=-1): 431 | """ 432 | Wait for the container to reach a given state or timeout. 433 | """ 434 | 435 | if isinstance(state, str): 436 | state = state.upper() 437 | 438 | return _lxc.Container.wait(self, state, timeout) 439 | 440 | 441 | def list_containers(active=True, defined=True, 442 | as_object=False, config_path=None): 443 | """ 444 | List the containers on the system. 445 | """ 446 | 447 | if config_path: 448 | if not os.path.exists(config_path): 449 | return tuple() 450 | try: 451 | entries = _lxc.list_containers(active=active, defined=defined, 452 | config_path=config_path) 453 | except ValueError: 454 | return tuple() 455 | else: 456 | try: 457 | entries = _lxc.list_containers(active=active, defined=defined) 458 | except ValueError: 459 | return tuple() 460 | 461 | if as_object: 462 | return tuple([Container(name, config_path) for name in entries]) 463 | else: 464 | return entries 465 | 466 | 467 | def attach_run_command(cmd): 468 | """ 469 | Run a command when attaching 470 | 471 | Please do not call directly, this will execvp the command. 472 | This is to be used in conjunction with the attach method 473 | of a container. 474 | """ 475 | if isinstance(cmd, tuple): 476 | return _lxc.attach_run_command(cmd) 477 | elif isinstance(cmd, list): 478 | return _lxc.attach_run_command((cmd[0], cmd)) 479 | else: 480 | return _lxc.attach_run_command((cmd, [cmd])) 481 | 482 | 483 | def attach_run_shell(): 484 | """ 485 | Run a shell when attaching 486 | 487 | Please do not call directly, this will execvp the shell. 488 | This is to be used in conjunction with the attach method 489 | of a container. 490 | """ 491 | return _lxc.attach_run_shell(None) 492 | 493 | 494 | def arch_to_personality(arch): 495 | """ 496 | Determine the process personality corresponding to the architecture 497 | """ 498 | if isinstance(arch, bytes): 499 | arch = str(arch, 'utf-8') 500 | return _lxc.arch_to_personality(arch) 501 | 502 | # namespace flags (no other python lib exports this) 503 | CLONE_NEWIPC = _lxc.CLONE_NEWIPC 504 | CLONE_NEWNET = _lxc.CLONE_NEWNET 505 | CLONE_NEWNS = _lxc.CLONE_NEWNS 506 | CLONE_NEWPID = _lxc.CLONE_NEWPID 507 | CLONE_NEWUSER = _lxc.CLONE_NEWUSER 508 | CLONE_NEWUTS = _lxc.CLONE_NEWUTS 509 | 510 | # attach: environment variable handling 511 | LXC_ATTACH_CLEAR_ENV = _lxc.LXC_ATTACH_CLEAR_ENV 512 | LXC_ATTACH_KEEP_ENV = _lxc.LXC_ATTACH_KEEP_ENV 513 | 514 | # attach: attach options 515 | LXC_ATTACH_DEFAULT = _lxc.LXC_ATTACH_DEFAULT 516 | LXC_ATTACH_DROP_CAPABILITIES = _lxc.LXC_ATTACH_DROP_CAPABILITIES 517 | LXC_ATTACH_LSM_EXEC = _lxc.LXC_ATTACH_LSM_EXEC 518 | LXC_ATTACH_LSM_NOW = _lxc.LXC_ATTACH_LSM_NOW 519 | LXC_ATTACH_MOVE_TO_CGROUP = _lxc.LXC_ATTACH_MOVE_TO_CGROUP 520 | LXC_ATTACH_REMOUNT_PROC_SYS = _lxc.LXC_ATTACH_REMOUNT_PROC_SYS 521 | LXC_ATTACH_SET_PERSONALITY = _lxc.LXC_ATTACH_SET_PERSONALITY 522 | 523 | # clone: clone flags 524 | LXC_CLONE_KEEPBDEVTYPE = _lxc.LXC_CLONE_KEEPBDEVTYPE 525 | LXC_CLONE_KEEPMACADDR = _lxc.LXC_CLONE_KEEPMACADDR 526 | LXC_CLONE_KEEPNAME = _lxc.LXC_CLONE_KEEPNAME 527 | LXC_CLONE_MAYBE_SNAPSHOT = _lxc.LXC_CLONE_MAYBE_SNAPSHOT 528 | LXC_CLONE_SNAPSHOT = _lxc.LXC_CLONE_SNAPSHOT 529 | 530 | # create: create flags 531 | LXC_CREATE_QUIET = _lxc.LXC_CREATE_QUIET 532 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /lxc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * python-lxc: Python bindings for LXC 3 | * 4 | * (C) Copyright Canonical Ltd. 2012-2013 5 | * 6 | * Authors: 7 | * Stéphane Graber 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA 23 | */ 24 | 25 | #include 26 | #include "structmember.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /* 33 | * CLONE_* definitions copied from lxc/namespace.h 34 | */ 35 | #ifndef CLONE_FS 36 | # define CLONE_FS 0x00000200 37 | #endif 38 | #ifndef CLONE_NEWNS 39 | # define CLONE_NEWNS 0x00020000 40 | #endif 41 | #ifndef CLONE_NEWCGROUP 42 | # define CLONE_NEWCGROUP 0x02000000 43 | #endif 44 | #ifndef CLONE_NEWUTS 45 | # define CLONE_NEWUTS 0x04000000 46 | #endif 47 | #ifndef CLONE_NEWIPC 48 | # define CLONE_NEWIPC 0x08000000 49 | #endif 50 | #ifndef CLONE_NEWUSER 51 | # define CLONE_NEWUSER 0x10000000 52 | #endif 53 | #ifndef CLONE_NEWPID 54 | # define CLONE_NEWPID 0x20000000 55 | #endif 56 | #ifndef CLONE_NEWNET 57 | # define CLONE_NEWNET 0x40000000 58 | #endif 59 | 60 | /* From sys/personality.h */ 61 | #define PER_LINUX 0x0000 62 | #define PER_LINUX32 0x0008 63 | 64 | /* Helper functions */ 65 | 66 | /* Copied from lxc/utils.c */ 67 | static int lxc_wait_for_pid_status(pid_t pid) 68 | { 69 | int status, ret; 70 | 71 | again: 72 | ret = waitpid(pid, &status, 0); 73 | if (ret == -1) { 74 | if (errno == EINTR) 75 | goto again; 76 | return -1; 77 | } 78 | if (ret != pid) 79 | goto again; 80 | return status; 81 | } 82 | 83 | /* Copied from lxc/confile.c, with HAVE_SYS_PERSONALITY_H check removed */ 84 | static signed long lxc_config_parse_arch(const char *arch) 85 | { 86 | struct per_name { 87 | char *name; 88 | unsigned long per; 89 | } pername[] = { 90 | { "x86", PER_LINUX32 }, 91 | { "linux32", PER_LINUX32 }, 92 | { "i386", PER_LINUX32 }, 93 | { "i486", PER_LINUX32 }, 94 | { "i586", PER_LINUX32 }, 95 | { "i686", PER_LINUX32 }, 96 | { "athlon", PER_LINUX32 }, 97 | { "linux64", PER_LINUX }, 98 | { "x86_64", PER_LINUX }, 99 | { "amd64", PER_LINUX }, 100 | }; 101 | size_t len = sizeof(pername) / sizeof(pername[0]); 102 | 103 | size_t i; 104 | 105 | for (i = 0; i < len; i++) { 106 | if (!strcmp(pername[i].name, arch)) 107 | return pername[i].per; 108 | } 109 | 110 | return -1; 111 | } 112 | 113 | char** 114 | convert_tuple_to_char_pointer_array(PyObject *argv) { 115 | int argc; 116 | int i, j; 117 | char **result; 118 | 119 | /* not a list or tuple */ 120 | if (!PyList_Check(argv) && !PyTuple_Check(argv)) { 121 | PyErr_SetString(PyExc_TypeError, "Expected list or tuple."); 122 | return NULL; 123 | } 124 | 125 | argc = PySequence_Fast_GET_SIZE(argv); 126 | 127 | result = (char**) calloc(argc + 1, sizeof(char*)); 128 | 129 | if (result == NULL) { 130 | PyErr_SetNone(PyExc_MemoryError); 131 | return NULL; 132 | } 133 | 134 | for (i = 0; i < argc; i++) { 135 | char *str = NULL; 136 | PyObject *pystr = NULL; 137 | PyObject *pyobj = PySequence_Fast_GET_ITEM(argv, i); 138 | assert(pyobj != NULL); 139 | 140 | if (!PyUnicode_Check(pyobj)) { 141 | PyErr_SetString(PyExc_ValueError, "Expected a string"); 142 | goto error; 143 | } 144 | 145 | pystr = PyUnicode_AsUTF8String(pyobj); 146 | if (!pystr) { 147 | /* Maybe it wasn't UTF-8 encoded. An exception is already set. */ 148 | goto error; 149 | } 150 | 151 | str = PyBytes_AsString(pystr); 152 | if (!str) { 153 | /* Maybe pystr wasn't a valid object. An exception is already set. 154 | */ 155 | Py_DECREF(pystr); 156 | goto error; 157 | } 158 | 159 | /* We must make a copy of str, because it points into internal memory 160 | * which we do not own. Assume it's NULL terminated, otherwise we'd 161 | * have to use PyUnicode_AsUTF8AndSize() and be explicit about copying 162 | * the memory. 163 | */ 164 | result[i] = strdup(str); 165 | 166 | /* Do not decref pyobj since we stole a reference by using 167 | * PyTuple_GET_ITEM(). 168 | */ 169 | Py_DECREF(pystr); 170 | if (result[i] == NULL) { 171 | PyErr_SetNone(PyExc_MemoryError); 172 | goto error; 173 | } 174 | } 175 | 176 | result[argc] = NULL; 177 | return result; 178 | 179 | error: 180 | /* We can only iterate up to but not including i because malloc() does not 181 | * initialize its memory. Thus if we got here, i points to the index 182 | * after the last strdup'd entry in result. 183 | */ 184 | for (j = 0; j < i; j++) 185 | free(result[j]); 186 | free(result); 187 | return NULL; 188 | } 189 | 190 | struct lxc_attach_python_payload { 191 | PyObject *fn; 192 | PyObject *arg; 193 | }; 194 | 195 | static int lxc_attach_python_exec(void* _payload) 196 | { 197 | /* This function is the first one to be called after attaching to a 198 | * container. As lxc_attach() calls fork() PyOS_AfterFork should be called 199 | * in the new process if the Python interpreter will continue to be used. 200 | */ 201 | #if PY_VERSION_HEX >= 0x030700F0 202 | PyOS_AfterFork_Child(); 203 | #else 204 | PyOS_AfterFork(); 205 | #endif 206 | 207 | struct lxc_attach_python_payload *payload = 208 | (struct lxc_attach_python_payload *)_payload; 209 | PyObject *result = PyObject_CallFunctionObjArgs(payload->fn, 210 | payload->arg, NULL); 211 | 212 | if (!result) { 213 | PyErr_Print(); 214 | return -1; 215 | } 216 | if (PyLong_Check(result)) 217 | return (int)PyLong_AsLong(result); 218 | else 219 | return -1; 220 | } 221 | 222 | static void lxc_attach_free_options(lxc_attach_options_t *options); 223 | 224 | static lxc_attach_options_t *lxc_attach_parse_options(PyObject *kwds) 225 | { 226 | static char *kwlist[] = {"attach_flags", "namespaces", "personality", 227 | "initial_cwd", "uid", "gid", "env_policy", 228 | "extra_env_vars", "extra_keep_env", "stdin", 229 | "stdout", "stderr", NULL}; 230 | long temp_uid, temp_gid; 231 | int temp_env_policy; 232 | PyObject *extra_env_vars_obj = NULL; 233 | PyObject *extra_keep_env_obj = NULL; 234 | PyObject *stdin_obj = NULL; 235 | PyObject *stdout_obj = NULL; 236 | PyObject *stderr_obj = NULL; 237 | PyObject *initial_cwd_obj = NULL; 238 | PyObject *dummy = NULL; 239 | bool parse_result; 240 | 241 | lxc_attach_options_t default_options = LXC_ATTACH_OPTIONS_DEFAULT; 242 | lxc_attach_options_t *options = malloc(sizeof(*options)); 243 | 244 | if (!options) { 245 | PyErr_SetNone(PyExc_MemoryError); 246 | return NULL; 247 | } 248 | memcpy(options, &default_options, sizeof(*options)); 249 | 250 | /* we need some dummy variables because we can't be sure 251 | * the data types match completely */ 252 | temp_uid = -1; 253 | temp_gid = -1; 254 | temp_env_policy = options->env_policy; 255 | 256 | /* we need a dummy tuple */ 257 | dummy = PyTuple_New(0); 258 | 259 | parse_result = PyArg_ParseTupleAndKeywords(dummy, kwds, "|iilO&lliOOOOO", 260 | kwlist, &options->attach_flags, 261 | &options->namespaces, 262 | &options->personality, 263 | PyUnicode_FSConverter, 264 | &initial_cwd_obj, &temp_uid, 265 | &temp_gid, &temp_env_policy, 266 | &extra_env_vars_obj, 267 | &extra_keep_env_obj, 268 | &stdin_obj, &stdout_obj, 269 | &stderr_obj); 270 | 271 | /* immediately get rid of the dummy tuple */ 272 | Py_DECREF(dummy); 273 | 274 | if (!parse_result) { 275 | lxc_attach_free_options(options); 276 | return NULL; 277 | } 278 | 279 | /* duplicate the string, so we don't depend on some random Python object */ 280 | if (initial_cwd_obj != NULL) { 281 | options->initial_cwd = strndup(PyBytes_AsString(initial_cwd_obj), 282 | PyBytes_Size(initial_cwd_obj)); 283 | Py_DECREF(initial_cwd_obj); 284 | } 285 | 286 | /* do the type conversion from the types that match the parse string */ 287 | if (temp_uid != -1) options->uid = (uid_t)temp_uid; 288 | if (temp_gid != -1) options->gid = (gid_t)temp_gid; 289 | options->env_policy = (lxc_attach_env_policy_t)temp_env_policy; 290 | 291 | if (extra_env_vars_obj) 292 | options->extra_env_vars = 293 | convert_tuple_to_char_pointer_array(extra_env_vars_obj); 294 | if (extra_keep_env_obj) 295 | options->extra_keep_env = 296 | convert_tuple_to_char_pointer_array(extra_keep_env_obj); 297 | if (stdin_obj) { 298 | options->stdin_fd = PyObject_AsFileDescriptor(stdin_obj); 299 | if (options->stdin_fd < 0) { 300 | lxc_attach_free_options(options); 301 | return NULL; 302 | } 303 | } 304 | if (stdout_obj) { 305 | options->stdout_fd = PyObject_AsFileDescriptor(stdout_obj); 306 | if (options->stdout_fd < 0) { 307 | lxc_attach_free_options(options); 308 | return NULL; 309 | } 310 | } 311 | if (stderr_obj) { 312 | options->stderr_fd = PyObject_AsFileDescriptor(stderr_obj); 313 | if (options->stderr_fd < 0) { 314 | lxc_attach_free_options(options); 315 | return NULL; 316 | } 317 | } 318 | 319 | return options; 320 | } 321 | 322 | void lxc_attach_free_options(lxc_attach_options_t *options) 323 | { 324 | int i; 325 | if (!options) 326 | return; 327 | free(options->initial_cwd); 328 | if (options->extra_env_vars) { 329 | for (i = 0; options->extra_env_vars[i]; i++) 330 | free(options->extra_env_vars[i]); 331 | free(options->extra_env_vars); 332 | } 333 | if (options->extra_keep_env) { 334 | for (i = 0; options->extra_keep_env[i]; i++) 335 | free(options->extra_keep_env[i]); 336 | free(options->extra_keep_env); 337 | } 338 | free(options); 339 | } 340 | 341 | /* Module functions */ 342 | static PyObject * 343 | LXC_arch_to_personality(PyObject *self, PyObject *arg) 344 | { 345 | long rv = -1; 346 | PyObject *pystr = NULL; 347 | char *str; 348 | 349 | if (!PyUnicode_Check(arg)) { 350 | PyErr_SetString(PyExc_ValueError, "Expected a string"); 351 | return NULL; 352 | } 353 | 354 | pystr = PyUnicode_AsUTF8String(arg); 355 | if (!pystr) 356 | return NULL; 357 | 358 | str = PyBytes_AsString(pystr); 359 | if (!str) 360 | goto out; 361 | 362 | rv = lxc_config_parse_arch(str); 363 | if (rv == -1) 364 | PyErr_SetString(PyExc_KeyError, "Failed to lookup architecture."); 365 | 366 | out: 367 | Py_DECREF(pystr); 368 | return rv == -1 ? NULL : PyLong_FromLong(rv); 369 | } 370 | 371 | static PyObject * 372 | LXC_attach_run_command(PyObject *self, PyObject *arg) 373 | { 374 | PyObject *args_obj = NULL; 375 | int i, rv; 376 | lxc_attach_command_t cmd = { 377 | NULL, /* program */ 378 | NULL /* argv[] */ 379 | }; 380 | 381 | if (!PyArg_ParseTuple(arg, "sO", (const char**)&cmd.program, &args_obj)) 382 | return NULL; 383 | if (args_obj && PyList_Check(args_obj)) { 384 | cmd.argv = convert_tuple_to_char_pointer_array(args_obj); 385 | } else { 386 | PyErr_Format(PyExc_TypeError, "Second part of tuple passed to " 387 | "attach_run_command must be a list."); 388 | return NULL; 389 | } 390 | 391 | if (!cmd.argv) 392 | return NULL; 393 | 394 | rv = lxc_attach_run_command(&cmd); 395 | 396 | for (i = 0; cmd.argv[i]; i++) 397 | free(cmd.argv[i]); 398 | free(cmd.argv); 399 | 400 | return PyLong_FromLong(rv); 401 | } 402 | 403 | static PyObject * 404 | LXC_attach_run_shell(PyObject *self, PyObject *arg) 405 | { 406 | int rv; 407 | 408 | rv = lxc_attach_run_shell(NULL); 409 | 410 | return PyLong_FromLong(rv); 411 | } 412 | 413 | static PyObject * 414 | LXC_get_global_config_item(PyObject *self, PyObject *args, PyObject *kwds) 415 | { 416 | static char *kwlist[] = {"key", NULL}; 417 | char* key = NULL; 418 | const char* value = NULL; 419 | 420 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, 421 | &key)) 422 | return NULL; 423 | 424 | value = lxc_get_global_config_item(key); 425 | 426 | if (!value) { 427 | PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); 428 | return NULL; 429 | } 430 | 431 | return PyUnicode_FromString(value); 432 | } 433 | 434 | static PyObject * 435 | LXC_get_version(PyObject *self, PyObject *args) 436 | { 437 | const char *rv = NULL; 438 | 439 | rv = lxc_get_version(); 440 | if (!rv) { 441 | return PyUnicode_FromString(""); 442 | } 443 | 444 | return PyUnicode_FromString(rv); 445 | } 446 | 447 | static PyObject * 448 | LXC_list_containers(PyObject *self, PyObject *args, PyObject *kwds) 449 | { 450 | char **names = NULL; 451 | PyObject *list = NULL; 452 | int list_count = 0; 453 | 454 | int list_active = 1; 455 | int list_defined = 1; 456 | 457 | PyObject *py_list_active = NULL; 458 | PyObject *py_list_defined = NULL; 459 | 460 | char* config_path = NULL; 461 | 462 | int i = 0; 463 | PyObject *vargs = NULL; 464 | static char *kwlist[] = {"active", "defined", "config_path", NULL}; 465 | 466 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOs", kwlist, 467 | &py_list_active, 468 | &py_list_defined, 469 | &config_path, &vargs)) 470 | return NULL; 471 | 472 | /* We default to listing everything */ 473 | if (py_list_active && py_list_active != Py_True) { 474 | list_active = 0; 475 | } 476 | 477 | if (py_list_defined && py_list_defined != Py_True) { 478 | list_defined = 0; 479 | } 480 | 481 | /* Call the right API function based on filters */ 482 | if (list_active == 1 && list_defined == 1) 483 | list_count = list_all_containers(config_path, &names, NULL); 484 | else if (list_active == 1) 485 | list_count = list_active_containers(config_path, &names, NULL); 486 | else if (list_defined == 1) 487 | list_count = list_defined_containers(config_path, &names, NULL); 488 | 489 | /* Handle failure */ 490 | if (list_count < 0) { 491 | PyErr_SetString(PyExc_ValueError, "failure to list containers"); 492 | return NULL; 493 | } 494 | 495 | /* Generate the tuple */ 496 | list = PyTuple_New(list_count); 497 | for (i = 0; i < list_count; i++) { 498 | if (!names[i]) { 499 | continue; 500 | } 501 | 502 | PyTuple_SET_ITEM(list, i, PyUnicode_FromString(names[i])); 503 | free(names[i]); 504 | } 505 | free(names); 506 | 507 | return list; 508 | } 509 | 510 | /* Base type and functions for Container */ 511 | typedef struct { 512 | PyObject_HEAD 513 | struct lxc_container *container; 514 | } Container; 515 | 516 | static void 517 | Container_dealloc(Container* self) 518 | { 519 | lxc_container_put(self->container); 520 | Py_TYPE(self)->tp_free((PyObject*)self); 521 | } 522 | 523 | static int 524 | Container_init(Container *self, PyObject *args, PyObject *kwds) 525 | { 526 | static char *kwlist[] = {"name", "config_path", NULL}; 527 | char *name = NULL; 528 | PyObject *fs_config_path = NULL; 529 | char *config_path = NULL; 530 | 531 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O&", kwlist, 532 | &name, 533 | PyUnicode_FSConverter, &fs_config_path)) 534 | return -1; 535 | 536 | if (fs_config_path != NULL) { 537 | config_path = PyBytes_AS_STRING(fs_config_path); 538 | assert(config_path != NULL); 539 | } 540 | 541 | self->container = lxc_container_new(name, config_path); 542 | if (!self->container) { 543 | Py_XDECREF(fs_config_path); 544 | 545 | PyErr_Format(PyExc_RuntimeError, "%s:%s:%d: error during init for container '%s'.", 546 | __FUNCTION__, __FILE__, __LINE__, name); 547 | return -1; 548 | } 549 | 550 | Py_XDECREF(fs_config_path); 551 | return 0; 552 | } 553 | 554 | static PyObject * 555 | Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 556 | { 557 | Container *self; 558 | 559 | self = (Container *)type->tp_alloc(type, 0); 560 | 561 | return (PyObject *)self; 562 | } 563 | 564 | /* Container properties */ 565 | static PyObject * 566 | Container_config_file_name(Container *self, void *closure) 567 | { 568 | char *rv = NULL; 569 | 570 | rv = self->container->config_file_name(self->container); 571 | if (!rv) { 572 | return PyUnicode_FromString(""); 573 | } 574 | 575 | return PyUnicode_FromString(rv); 576 | } 577 | 578 | static PyObject * 579 | Container_controllable(Container *self, void *closure) 580 | { 581 | if (self->container->may_control(self->container)) { 582 | Py_RETURN_TRUE; 583 | } 584 | 585 | Py_RETURN_FALSE; 586 | } 587 | 588 | static PyObject * 589 | Container_defined(Container *self, void *closure) 590 | { 591 | if (self->container->is_defined(self->container)) { 592 | Py_RETURN_TRUE; 593 | } 594 | 595 | Py_RETURN_FALSE; 596 | } 597 | 598 | static PyObject * 599 | Container_init_pid(Container *self, void *closure) 600 | { 601 | return PyLong_FromLong(self->container->init_pid(self->container)); 602 | } 603 | 604 | static PyObject * 605 | Container_name(Container *self, void *closure) 606 | { 607 | if (!self->container->name) { 608 | return PyUnicode_FromString(""); 609 | } 610 | 611 | return PyUnicode_FromString(self->container->name); 612 | } 613 | 614 | static PyObject * 615 | Container_running(Container *self, void *closure) 616 | { 617 | if (self->container->is_running(self->container)) { 618 | Py_RETURN_TRUE; 619 | } 620 | 621 | Py_RETURN_FALSE; 622 | } 623 | 624 | static PyObject * 625 | Container_state(Container *self, void *closure) 626 | { 627 | const char *rv = NULL; 628 | 629 | rv = self->container->state(self->container); 630 | 631 | if (!rv) { 632 | return PyUnicode_FromString(""); 633 | } 634 | 635 | return PyUnicode_FromString(rv); 636 | } 637 | 638 | /* Container Functions */ 639 | static PyObject * 640 | Container_attach_interface(Container *self, PyObject *args, PyObject *kwds) 641 | { 642 | static char *kwlist[] = {"src_ifname", "dst_ifname", NULL}; 643 | char *src_name = NULL; 644 | char *dst_name = NULL; 645 | PyObject *py_src_name = NULL; 646 | PyObject *py_dst_name = NULL; 647 | 648 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, 649 | PyUnicode_FSConverter, &py_src_name, 650 | PyUnicode_FSConverter, &py_dst_name)) 651 | return NULL; 652 | 653 | if (py_src_name != NULL) { 654 | src_name = PyBytes_AS_STRING(py_src_name); 655 | assert(src_name != NULL); 656 | } 657 | 658 | if (py_dst_name != NULL) { 659 | dst_name = PyBytes_AS_STRING(py_dst_name); 660 | assert(dst_name != NULL); 661 | } 662 | 663 | if (self->container->attach_interface(self->container, src_name, dst_name)) { 664 | Py_XDECREF(py_src_name); 665 | Py_XDECREF(py_dst_name); 666 | Py_RETURN_TRUE; 667 | } 668 | 669 | Py_XDECREF(py_src_name); 670 | Py_XDECREF(py_dst_name); 671 | Py_RETURN_FALSE; 672 | } 673 | 674 | static PyObject * 675 | Container_detach_interface(Container *self, PyObject *args, PyObject *kwds) 676 | { 677 | static char *kwlist[] = {"ifname", NULL}; 678 | char *ifname = NULL; 679 | PyObject *py_ifname = NULL; 680 | 681 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&", kwlist, 682 | PyUnicode_FSConverter, &py_ifname)) 683 | return NULL; 684 | 685 | if (py_ifname != NULL) { 686 | ifname = PyBytes_AS_STRING(py_ifname); 687 | assert(ifname != NULL); 688 | } 689 | 690 | if (self->container->detach_interface(self->container, ifname, NULL)) { 691 | Py_XDECREF(py_ifname); 692 | Py_RETURN_TRUE; 693 | } 694 | 695 | Py_XDECREF(py_ifname); 696 | Py_RETURN_FALSE; 697 | } 698 | 699 | static PyObject * 700 | Container_add_device_node(Container *self, PyObject *args, PyObject *kwds) 701 | { 702 | static char *kwlist[] = {"src_path", "dest_path", NULL}; 703 | char *src_path = NULL; 704 | char *dst_path = NULL; 705 | PyObject *py_src_path = NULL; 706 | PyObject *py_dst_path = NULL; 707 | 708 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, 709 | PyUnicode_FSConverter, &py_src_path, 710 | PyUnicode_FSConverter, &py_dst_path)) 711 | return NULL; 712 | 713 | if (py_src_path != NULL) { 714 | src_path = PyBytes_AS_STRING(py_src_path); 715 | assert(src_path != NULL); 716 | } 717 | 718 | if (py_dst_path != NULL) { 719 | dst_path = PyBytes_AS_STRING(py_dst_path); 720 | assert(dst_path != NULL); 721 | } 722 | 723 | if (self->container->add_device_node(self->container, src_path, 724 | dst_path)) { 725 | Py_XDECREF(py_src_path); 726 | Py_XDECREF(py_dst_path); 727 | Py_RETURN_TRUE; 728 | } 729 | 730 | Py_XDECREF(py_src_path); 731 | Py_XDECREF(py_dst_path); 732 | Py_RETURN_FALSE; 733 | } 734 | 735 | static PyObject * 736 | Container_attach_and_possibly_wait(Container *self, PyObject *args, 737 | PyObject *kwds, int wait) 738 | { 739 | struct lxc_attach_python_payload payload = { NULL, NULL }; 740 | lxc_attach_options_t *options = NULL; 741 | long ret; 742 | pid_t pid; 743 | 744 | if (!PyArg_ParseTuple(args, "O|O", &payload.fn, &payload.arg)) 745 | return NULL; 746 | if (!PyCallable_Check(payload.fn)) { 747 | PyErr_Format(PyExc_TypeError, "attach: object not callable"); 748 | return NULL; 749 | } 750 | 751 | options = lxc_attach_parse_options(kwds); 752 | if (!options) 753 | return NULL; 754 | 755 | #if PY_VERSION_HEX >= 0x030700F0 756 | PyOS_BeforeFork(); 757 | #endif 758 | ret = self->container->attach(self->container, lxc_attach_python_exec, 759 | &payload, options, &pid); 760 | #if PY_VERSION_HEX >= 0x030700F0 761 | PyOS_AfterFork_Parent(); 762 | #endif 763 | if (ret < 0) 764 | goto out; 765 | 766 | if (wait) { 767 | Py_BEGIN_ALLOW_THREADS 768 | ret = lxc_wait_for_pid_status(pid); 769 | Py_END_ALLOW_THREADS 770 | /* handle case where attach fails */ 771 | if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255) 772 | ret = -1; 773 | } else { 774 | ret = (long)pid; 775 | } 776 | 777 | out: 778 | lxc_attach_free_options(options); 779 | return PyLong_FromLong(ret); 780 | } 781 | 782 | static PyObject * 783 | Container_attach(Container *self, PyObject *args, PyObject *kwds) 784 | { 785 | return Container_attach_and_possibly_wait(self, args, kwds, 0); 786 | } 787 | 788 | static PyObject * 789 | Container_attach_wait(Container *self, PyObject *args, PyObject *kwds) 790 | { 791 | return Container_attach_and_possibly_wait(self, args, kwds, 1); 792 | } 793 | 794 | static PyObject * 795 | Container_clear_config(Container *self, PyObject *args, PyObject *kwds) 796 | { 797 | self->container->clear_config(self->container); 798 | 799 | Py_RETURN_NONE; 800 | } 801 | 802 | static PyObject * 803 | Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds) 804 | { 805 | static char *kwlist[] = {"key", NULL}; 806 | char *key = NULL; 807 | 808 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, 809 | &key)) 810 | return NULL; 811 | 812 | if (self->container->clear_config_item(self->container, key)) { 813 | Py_RETURN_TRUE; 814 | } 815 | 816 | Py_RETURN_FALSE; 817 | } 818 | 819 | static PyObject * 820 | Container_clone(Container *self, PyObject *args, PyObject *kwds) 821 | { 822 | char *newname = NULL; 823 | char *config_path = NULL; 824 | int flags = 0; 825 | char *bdevtype = NULL; 826 | char *bdevdata = NULL; 827 | unsigned long newsize = 0; 828 | char **hookargs = NULL; 829 | 830 | PyObject *py_hookargs = NULL; 831 | PyObject *py_config_path = NULL; 832 | struct lxc_container *new_container = NULL; 833 | int i = 0; 834 | 835 | static char *kwlist[] = {"newname", "config_path", "flags", "bdevtype", 836 | "bdevdata", "newsize", "hookargs", NULL}; 837 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O&isskO", kwlist, 838 | &newname, 839 | PyUnicode_FSConverter, &py_config_path, 840 | &flags, &bdevtype, &bdevdata, &newsize, 841 | &py_hookargs)) 842 | return NULL; 843 | 844 | if (py_hookargs) { 845 | if (PyTuple_Check(py_hookargs)) { 846 | hookargs = convert_tuple_to_char_pointer_array(py_hookargs); 847 | if (!hookargs) { 848 | return NULL; 849 | } 850 | } 851 | else { 852 | PyErr_SetString(PyExc_ValueError, "hookargs needs to be a tuple"); 853 | return NULL; 854 | } 855 | } 856 | 857 | if (py_config_path != NULL) { 858 | config_path = PyBytes_AS_STRING(py_config_path); 859 | assert(config_path != NULL); 860 | } 861 | 862 | Py_BEGIN_ALLOW_THREADS 863 | new_container = self->container->clone(self->container, newname, 864 | config_path, flags, bdevtype, 865 | bdevdata, newsize, hookargs); 866 | Py_END_ALLOW_THREADS 867 | 868 | Py_XDECREF(py_config_path); 869 | 870 | if (hookargs) { 871 | for (i = 0; i < PyTuple_GET_SIZE(py_hookargs); i++) 872 | free(hookargs[i]); 873 | free(hookargs); 874 | } 875 | 876 | if (new_container == NULL) { 877 | Py_RETURN_FALSE; 878 | } 879 | 880 | lxc_container_put(new_container); 881 | 882 | Py_RETURN_TRUE; 883 | } 884 | 885 | static PyObject * 886 | Container_console(Container *self, PyObject *args, PyObject *kwds) 887 | { 888 | static char *kwlist[] = {"ttynum", "stdinfd", "stdoutfd", "stderrfd", 889 | "escape", NULL}; 890 | int ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, escape = 1; 891 | 892 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iiiii", kwlist, 893 | &ttynum, &stdinfd, &stdoutfd, &stderrfd, 894 | &escape)) 895 | return NULL; 896 | 897 | if (self->container->console(self->container, ttynum, 898 | stdinfd, stdoutfd, stderrfd, escape) == 0) { 899 | Py_RETURN_TRUE; 900 | } 901 | Py_RETURN_FALSE; 902 | } 903 | 904 | static PyObject * 905 | Container_console_getfd(Container *self, PyObject *args, PyObject *kwds) 906 | { 907 | static char *kwlist[] = {"ttynum", NULL}; 908 | int ttynum = -1, masterfd; 909 | 910 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &ttynum)) 911 | return NULL; 912 | 913 | if (self->container->console_getfd(self->container, &ttynum, 914 | &masterfd) < 0) { 915 | PyErr_SetString(PyExc_ValueError, "Unable to allocate tty"); 916 | return NULL; 917 | } 918 | return PyLong_FromLong(masterfd); 919 | } 920 | 921 | static PyObject * 922 | Container_create(Container *self, PyObject *args, PyObject *kwds) 923 | { 924 | char* template_name = NULL; 925 | int flags = 0; 926 | char** create_args = {NULL}; 927 | PyObject *retval = NULL; 928 | PyObject *vargs = NULL; 929 | char *bdevtype = NULL; 930 | int i = 0; 931 | struct bdev_specs fs_specs; 932 | memset(&fs_specs, 0, sizeof(fs_specs)); 933 | static char *kwlist[] = {"template", "flags", "bdevtype", "fssize", "args", NULL}; 934 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|siskO", kwlist, 935 | &template_name, &flags, &bdevtype, 936 | &fs_specs.fssize, &vargs)) 937 | return NULL; 938 | 939 | if (vargs) { 940 | if (PyTuple_Check(vargs)) { 941 | create_args = convert_tuple_to_char_pointer_array(vargs); 942 | if (!create_args) { 943 | return NULL; 944 | } 945 | } 946 | else { 947 | PyErr_SetString(PyExc_ValueError, "args needs to be a tuple"); 948 | return NULL; 949 | } 950 | } 951 | 952 | Py_BEGIN_ALLOW_THREADS 953 | if (self->container->create(self->container, template_name, bdevtype, &fs_specs, 954 | flags, create_args)) 955 | retval = Py_True; 956 | else 957 | retval = Py_False; 958 | Py_END_ALLOW_THREADS 959 | 960 | if (vargs) { 961 | /* We cannot have gotten here unless vargs was given and create_args 962 | * was successfully allocated. 963 | */ 964 | for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) 965 | free(create_args[i]); 966 | free(create_args); 967 | } 968 | 969 | Py_INCREF(retval); 970 | return retval; 971 | } 972 | 973 | static PyObject * 974 | Container_destroy(Container *self, PyObject *args, PyObject *kwds) 975 | { 976 | if (self->container->destroy(self->container)) { 977 | Py_RETURN_TRUE; 978 | } 979 | 980 | Py_RETURN_FALSE; 981 | } 982 | 983 | static PyObject * 984 | Container_freeze(Container *self, PyObject *args, PyObject *kwds) 985 | { 986 | if (self->container->freeze(self->container)) { 987 | Py_RETURN_TRUE; 988 | } 989 | 990 | Py_RETURN_FALSE; 991 | } 992 | 993 | static PyObject * 994 | Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) 995 | { 996 | static char *kwlist[] = {"key", NULL}; 997 | char* key = NULL; 998 | int len = 0; 999 | char* value; 1000 | PyObject *ret = NULL; 1001 | 1002 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, 1003 | &key)) 1004 | return NULL; 1005 | 1006 | len = self->container->get_cgroup_item(self->container, key, NULL, 0); 1007 | 1008 | if (len < 0) { 1009 | PyErr_SetString(PyExc_KeyError, "Invalid cgroup entry"); 1010 | return NULL; 1011 | } 1012 | 1013 | value = (char*) malloc(sizeof(char)*len + 1); 1014 | if (value == NULL) 1015 | return PyErr_NoMemory(); 1016 | 1017 | if (self->container->get_cgroup_item(self->container, 1018 | key, value, len + 1) != len) { 1019 | PyErr_SetString(PyExc_ValueError, "Unable to read config value"); 1020 | free(value); 1021 | return NULL; 1022 | } 1023 | 1024 | ret = PyUnicode_FromString(value); 1025 | free(value); 1026 | return ret; 1027 | } 1028 | 1029 | static PyObject * 1030 | Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) 1031 | { 1032 | static char *kwlist[] = {"key", NULL}; 1033 | char* key = NULL; 1034 | int len = 0; 1035 | char* value; 1036 | PyObject *ret = NULL; 1037 | 1038 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, 1039 | &key)) 1040 | return NULL; 1041 | 1042 | len = self->container->get_config_item(self->container, key, NULL, 0); 1043 | 1044 | if (len < 0) { 1045 | PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); 1046 | return NULL; 1047 | } 1048 | 1049 | if (len == 0) { 1050 | return PyUnicode_FromString(""); 1051 | } 1052 | 1053 | value = (char*) malloc(sizeof(char)*len + 1); 1054 | if (value == NULL) 1055 | return PyErr_NoMemory(); 1056 | 1057 | if (self->container->get_config_item(self->container, 1058 | key, value, len + 1) != len) { 1059 | PyErr_SetString(PyExc_ValueError, "Unable to read config value"); 1060 | free(value); 1061 | return NULL; 1062 | } 1063 | 1064 | ret = PyUnicode_FromString(value); 1065 | free(value); 1066 | return ret; 1067 | } 1068 | 1069 | static PyObject * 1070 | Container_get_config_path(Container *self, PyObject *args, PyObject *kwds) 1071 | { 1072 | const char *rv = NULL; 1073 | 1074 | rv = self->container->get_config_path(self->container); 1075 | 1076 | if (!rv) { 1077 | return PyUnicode_FromString(""); 1078 | } 1079 | 1080 | return PyUnicode_FromString(rv); 1081 | } 1082 | 1083 | static PyObject * 1084 | Container_get_keys(Container *self, PyObject *args, PyObject *kwds) 1085 | { 1086 | static char *kwlist[] = {"key", NULL}; 1087 | char* key = NULL; 1088 | int len = 0; 1089 | char* value; 1090 | PyObject *ret = NULL; 1091 | 1092 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, 1093 | &key)) 1094 | return NULL; 1095 | 1096 | len = self->container->get_keys(self->container, key, NULL, 0); 1097 | 1098 | if (len < 0) { 1099 | PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); 1100 | return NULL; 1101 | } 1102 | 1103 | value = (char*) malloc(sizeof(char)*len + 1); 1104 | if (value == NULL) 1105 | return PyErr_NoMemory(); 1106 | 1107 | if (self->container->get_keys(self->container, 1108 | key, value, len + 1) != len) { 1109 | PyErr_SetString(PyExc_ValueError, "Unable to read config keys"); 1110 | free(value); 1111 | return NULL; 1112 | } 1113 | 1114 | ret = PyUnicode_FromString(value); 1115 | free(value); 1116 | return ret; 1117 | } 1118 | 1119 | static PyObject * 1120 | Container_get_interfaces(Container *self) 1121 | { 1122 | int i = 0; 1123 | char** interfaces = NULL; 1124 | 1125 | PyObject* ret; 1126 | 1127 | /* Get the interfaces */ 1128 | interfaces = self->container->get_interfaces(self->container); 1129 | if (!interfaces) 1130 | return PyTuple_New(0); 1131 | 1132 | /* Count the entries */ 1133 | while (interfaces[i]) 1134 | i++; 1135 | 1136 | /* Create the new tuple */ 1137 | ret = PyTuple_New(i); 1138 | if (!ret) 1139 | return NULL; 1140 | 1141 | /* Add the entries to the tuple and free the memory */ 1142 | i = 0; 1143 | while (interfaces[i]) { 1144 | if (!interfaces[i]) { 1145 | i++; 1146 | continue; 1147 | } 1148 | 1149 | PyObject *unicode = PyUnicode_FromString(interfaces[i]); 1150 | if (!unicode) { 1151 | Py_DECREF(ret); 1152 | ret = NULL; 1153 | break; 1154 | } 1155 | PyTuple_SET_ITEM(ret, i, unicode); 1156 | i++; 1157 | } 1158 | 1159 | /* Free the list of IPs */ 1160 | i = 0; 1161 | while (interfaces[i]) { 1162 | free(interfaces[i]); 1163 | i++; 1164 | } 1165 | free(interfaces); 1166 | 1167 | return ret; 1168 | } 1169 | 1170 | static PyObject * 1171 | Container_get_ips(Container *self, PyObject *args, PyObject *kwds) 1172 | { 1173 | static char *kwlist[] = {"interface", "family", "scope", NULL}; 1174 | char* interface = NULL; 1175 | char* family = NULL; 1176 | int scope = 0; 1177 | 1178 | int i = 0; 1179 | char** ips = NULL; 1180 | 1181 | PyObject* ret; 1182 | 1183 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ssi", kwlist, 1184 | &interface, &family, &scope)) 1185 | return NULL; 1186 | 1187 | /* Get the IPs */ 1188 | ips = self->container->get_ips(self->container, interface, family, scope); 1189 | if (!ips) 1190 | return PyTuple_New(0); 1191 | 1192 | /* Count the entries */ 1193 | while (ips[i]) 1194 | i++; 1195 | 1196 | /* Create the new tuple */ 1197 | ret = PyTuple_New(i); 1198 | if (!ret) 1199 | return NULL; 1200 | 1201 | /* Add the entries to the tuple and free the memory */ 1202 | i = 0; 1203 | while (ips[i]) { 1204 | if (!ips[i]) { 1205 | i++; 1206 | continue; 1207 | } 1208 | 1209 | PyObject *unicode = PyUnicode_FromString(ips[i]); 1210 | if (!unicode) { 1211 | Py_DECREF(ret); 1212 | ret = NULL; 1213 | break; 1214 | } 1215 | PyTuple_SET_ITEM(ret, i, unicode); 1216 | i++; 1217 | } 1218 | 1219 | /* Free the list of IPs */ 1220 | i = 0; 1221 | while (ips[i]) { 1222 | free(ips[i]); 1223 | i++; 1224 | } 1225 | free(ips); 1226 | 1227 | return ret; 1228 | } 1229 | 1230 | static PyObject * 1231 | Container_get_running_config_item(Container *self, PyObject *args, 1232 | PyObject *kwds) 1233 | { 1234 | static char *kwlist[] = {"key", NULL}; 1235 | char* key = NULL; 1236 | char* value = NULL; 1237 | PyObject *ret = NULL; 1238 | 1239 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, 1240 | &key)) 1241 | return NULL; 1242 | 1243 | value = self->container->get_running_config_item(self->container, key); 1244 | 1245 | if (!value) 1246 | Py_RETURN_NONE; 1247 | 1248 | ret = PyUnicode_FromString(value); 1249 | free(value); 1250 | return ret; 1251 | } 1252 | 1253 | 1254 | static PyObject * 1255 | Container_load_config(Container *self, PyObject *args, PyObject *kwds) 1256 | { 1257 | static char *kwlist[] = {"path", NULL}; 1258 | PyObject *fs_path = NULL; 1259 | char* path = NULL; 1260 | 1261 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, 1262 | PyUnicode_FSConverter, &fs_path)) 1263 | return NULL; 1264 | 1265 | if (fs_path != NULL) { 1266 | path = PyBytes_AS_STRING(fs_path); 1267 | assert(path != NULL); 1268 | } 1269 | 1270 | if (self->container->load_config(self->container, path)) { 1271 | Py_XDECREF(fs_path); 1272 | Py_RETURN_TRUE; 1273 | } 1274 | 1275 | Py_XDECREF(fs_path); 1276 | Py_RETURN_FALSE; 1277 | } 1278 | 1279 | static PyObject * 1280 | Container_reboot(Container *self, PyObject *args, PyObject *kwds) 1281 | { 1282 | if (self->container->reboot(self->container)) { 1283 | Py_RETURN_TRUE; 1284 | } 1285 | 1286 | Py_RETURN_FALSE; 1287 | } 1288 | 1289 | static PyObject * 1290 | Container_rename(Container *self, PyObject *args, PyObject *kwds) 1291 | { 1292 | char *new_name = NULL; 1293 | static char *kwlist[] = {"new_name", NULL}; 1294 | 1295 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, 1296 | &new_name)) 1297 | return NULL; 1298 | 1299 | if (self->container->rename(self->container, new_name)) { 1300 | Py_RETURN_TRUE; 1301 | } 1302 | 1303 | Py_RETURN_FALSE; 1304 | } 1305 | 1306 | static PyObject * 1307 | Container_remove_device_node(Container *self, PyObject *args, PyObject *kwds) 1308 | { 1309 | static char *kwlist[] = {"src_path", "dest_path", NULL}; 1310 | char *src_path = NULL; 1311 | char *dst_path = NULL; 1312 | PyObject *py_src_path = NULL; 1313 | PyObject *py_dst_path = NULL; 1314 | 1315 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&", kwlist, 1316 | PyUnicode_FSConverter, &py_src_path, 1317 | PyUnicode_FSConverter, &py_dst_path)) 1318 | return NULL; 1319 | 1320 | if (py_src_path != NULL) { 1321 | src_path = PyBytes_AS_STRING(py_src_path); 1322 | assert(src_path != NULL); 1323 | } 1324 | 1325 | if (py_dst_path != NULL) { 1326 | dst_path = PyBytes_AS_STRING(py_dst_path); 1327 | assert(dst_path != NULL); 1328 | } 1329 | 1330 | if (self->container->remove_device_node(self->container, src_path, 1331 | dst_path)) { 1332 | Py_XDECREF(py_src_path); 1333 | Py_XDECREF(py_dst_path); 1334 | Py_RETURN_TRUE; 1335 | } 1336 | 1337 | Py_XDECREF(py_src_path); 1338 | Py_XDECREF(py_dst_path); 1339 | Py_RETURN_FALSE; 1340 | } 1341 | 1342 | static PyObject * 1343 | Container_save_config(Container *self, PyObject *args, PyObject *kwds) 1344 | { 1345 | static char *kwlist[] = {"path", NULL}; 1346 | PyObject *fs_path = NULL; 1347 | char* path = NULL; 1348 | 1349 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, 1350 | PyUnicode_FSConverter, &fs_path)) 1351 | return NULL; 1352 | 1353 | if (fs_path != NULL) { 1354 | path = PyBytes_AS_STRING(fs_path); 1355 | assert(path != NULL); 1356 | } 1357 | 1358 | if (self->container->save_config(self->container, path)) { 1359 | Py_XDECREF(fs_path); 1360 | Py_RETURN_TRUE; 1361 | } 1362 | 1363 | Py_XDECREF(fs_path); 1364 | Py_RETURN_FALSE; 1365 | } 1366 | 1367 | static PyObject * 1368 | Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds) 1369 | { 1370 | static char *kwlist[] = {"key", "value", NULL}; 1371 | char *key = NULL; 1372 | char *value = NULL; 1373 | 1374 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, 1375 | &key, &value)) 1376 | return NULL; 1377 | 1378 | if (self->container->set_cgroup_item(self->container, key, value)) { 1379 | Py_RETURN_TRUE; 1380 | } 1381 | 1382 | Py_RETURN_FALSE; 1383 | } 1384 | 1385 | static PyObject * 1386 | Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) 1387 | { 1388 | static char *kwlist[] = {"key", "value", NULL}; 1389 | char *key = NULL; 1390 | char *value = NULL; 1391 | 1392 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, 1393 | &key, &value)) 1394 | return NULL; 1395 | 1396 | if (self->container->set_config_item(self->container, key, value)) { 1397 | Py_RETURN_TRUE; 1398 | } 1399 | 1400 | Py_RETURN_FALSE; 1401 | } 1402 | 1403 | static PyObject * 1404 | Container_set_config_path(Container *self, PyObject *args, PyObject *kwds) 1405 | { 1406 | static char *kwlist[] = {"path", NULL}; 1407 | char *path = NULL; 1408 | 1409 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, 1410 | &path)) 1411 | return NULL; 1412 | 1413 | if (self->container->set_config_path(self->container, path)) { 1414 | Py_RETURN_TRUE; 1415 | } 1416 | 1417 | Py_RETURN_FALSE; 1418 | } 1419 | 1420 | static PyObject * 1421 | Container_shutdown(Container *self, PyObject *args, PyObject *kwds) 1422 | { 1423 | static char *kwlist[] = {"timeout", NULL}; 1424 | int timeout = -1; 1425 | 1426 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, 1427 | &timeout)) 1428 | return NULL; 1429 | 1430 | if (self->container->shutdown(self->container, timeout)) { 1431 | Py_RETURN_TRUE; 1432 | } 1433 | 1434 | Py_RETURN_FALSE; 1435 | } 1436 | 1437 | static PyObject * 1438 | Container_snapshot(Container *self, PyObject *args, PyObject *kwds) 1439 | { 1440 | char *comment_path = NULL; 1441 | static char *kwlist[] = {"comment_path", NULL}; 1442 | int retval = 0; 1443 | int ret = 0; 1444 | char newname[20]; 1445 | PyObject *py_comment_path = NULL; 1446 | 1447 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, 1448 | PyUnicode_FSConverter, &py_comment_path)) 1449 | return NULL; 1450 | 1451 | if (py_comment_path != NULL) { 1452 | comment_path = PyBytes_AS_STRING(py_comment_path); 1453 | assert(comment_path != NULL); 1454 | } 1455 | 1456 | retval = self->container->snapshot(self->container, comment_path); 1457 | 1458 | Py_XDECREF(py_comment_path); 1459 | 1460 | if (retval < 0) { 1461 | Py_RETURN_FALSE; 1462 | } 1463 | 1464 | ret = snprintf(newname, 20, "snap%d", retval); 1465 | if (ret < 0 || ret >= 20) 1466 | return NULL; 1467 | 1468 | 1469 | return PyUnicode_FromString(newname); 1470 | } 1471 | 1472 | static PyObject * 1473 | Container_snapshot_destroy(Container *self, PyObject *args, PyObject *kwds) 1474 | { 1475 | char *name = NULL; 1476 | static char *kwlist[] = {"name", NULL}; 1477 | 1478 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, 1479 | &name)) 1480 | return NULL; 1481 | 1482 | if (self->container->snapshot_destroy(self->container, name)) { 1483 | Py_RETURN_TRUE; 1484 | } 1485 | 1486 | Py_RETURN_FALSE; 1487 | } 1488 | 1489 | static PyObject * 1490 | Container_snapshot_list(Container *self, PyObject *args, PyObject *kwds) 1491 | { 1492 | struct lxc_snapshot *snap; 1493 | int snap_count = 0; 1494 | PyObject *list = NULL; 1495 | int i = 0; 1496 | 1497 | snap_count = self->container->snapshot_list(self->container, &snap); 1498 | 1499 | if (snap_count < 0) { 1500 | PyErr_SetString(PyExc_KeyError, "Unable to list snapshots"); 1501 | return NULL; 1502 | } 1503 | 1504 | list = PyTuple_New(snap_count); 1505 | for (i = 0; i < snap_count; i++) { 1506 | PyObject *list_entry = NULL; 1507 | 1508 | list_entry = PyTuple_New(4); 1509 | PyTuple_SET_ITEM(list_entry, 0, 1510 | PyUnicode_FromString(snap[i].name)); 1511 | PyTuple_SET_ITEM(list_entry, 1, 1512 | PyUnicode_FromString(snap[i].comment_pathname)); 1513 | PyTuple_SET_ITEM(list_entry, 2, 1514 | PyUnicode_FromString(snap[i].timestamp)); 1515 | PyTuple_SET_ITEM(list_entry, 3, 1516 | PyUnicode_FromString(snap[i].lxcpath)); 1517 | 1518 | snap[i].free(&snap[i]); 1519 | 1520 | PyTuple_SET_ITEM(list, i, list_entry); 1521 | } 1522 | 1523 | return list; 1524 | } 1525 | 1526 | 1527 | static PyObject * 1528 | Container_snapshot_restore(Container *self, PyObject *args, PyObject *kwds) 1529 | { 1530 | char *name = NULL; 1531 | char *newname = NULL; 1532 | static char *kwlist[] = {"name", "newname", NULL}; 1533 | 1534 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, 1535 | &name, &newname)) 1536 | return NULL; 1537 | 1538 | if (self->container->snapshot_restore(self->container, name, newname)) { 1539 | Py_RETURN_TRUE; 1540 | } 1541 | 1542 | Py_RETURN_FALSE; 1543 | } 1544 | 1545 | static PyObject * 1546 | Container_start(Container *self, PyObject *args, PyObject *kwds) 1547 | { 1548 | PyObject *useinit = NULL; 1549 | PyObject *daemonize = NULL; 1550 | PyObject *close_fds = NULL; 1551 | 1552 | PyObject *vargs = NULL; 1553 | char** init_args = {NULL}; 1554 | 1555 | PyObject *retval = NULL; 1556 | int init_useinit = 0, i = 0; 1557 | static char *kwlist[] = {"useinit", "daemonize", "close_fds", 1558 | "cmd", NULL}; 1559 | 1560 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, 1561 | &useinit, &daemonize, &close_fds, 1562 | &vargs)) 1563 | return NULL; 1564 | 1565 | if (useinit && useinit == Py_True) { 1566 | init_useinit = 1; 1567 | } 1568 | 1569 | if (vargs && PyTuple_Check(vargs)) { 1570 | init_args = convert_tuple_to_char_pointer_array(vargs); 1571 | if (!init_args) { 1572 | return NULL; 1573 | } 1574 | } 1575 | 1576 | if (close_fds && close_fds == Py_True) { 1577 | self->container->want_close_all_fds(self->container, true); 1578 | } 1579 | else { 1580 | self->container->want_close_all_fds(self->container, false); 1581 | } 1582 | 1583 | if (!daemonize || daemonize == Py_True) { 1584 | self->container->want_daemonize(self->container, true); 1585 | } 1586 | else { 1587 | self->container->want_daemonize(self->container, false); 1588 | } 1589 | 1590 | if (self->container->start(self->container, init_useinit, init_args)) 1591 | retval = Py_True; 1592 | else 1593 | retval = Py_False; 1594 | 1595 | if (vargs) { 1596 | /* We cannot have gotten here unless vargs was given and create_args 1597 | * was successfully allocated. 1598 | */ 1599 | for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) 1600 | free(init_args[i]); 1601 | free(init_args); 1602 | } 1603 | 1604 | Py_INCREF(retval); 1605 | return retval; 1606 | } 1607 | 1608 | static PyObject * 1609 | Container_stop(Container *self, PyObject *args, PyObject *kwds) 1610 | { 1611 | if (self->container->stop(self->container)) { 1612 | Py_RETURN_TRUE; 1613 | } 1614 | 1615 | Py_RETURN_FALSE; 1616 | } 1617 | 1618 | static PyObject * 1619 | Container_unfreeze(Container *self, PyObject *args, PyObject *kwds) 1620 | { 1621 | if (self->container->unfreeze(self->container)) { 1622 | Py_RETURN_TRUE; 1623 | } 1624 | 1625 | Py_RETURN_FALSE; 1626 | } 1627 | 1628 | static PyObject * 1629 | Container_wait(Container *self, PyObject *args, PyObject *kwds) 1630 | { 1631 | static char *kwlist[] = {"state", "timeout", NULL}; 1632 | char *state = NULL; 1633 | int timeout = -1; 1634 | 1635 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, 1636 | &state, &timeout)) 1637 | return NULL; 1638 | 1639 | if (self->container->wait(self->container, state, timeout)) { 1640 | Py_RETURN_TRUE; 1641 | } 1642 | 1643 | Py_RETURN_FALSE; 1644 | } 1645 | 1646 | /* Function/Properties list */ 1647 | static PyGetSetDef Container_getseters[] = { 1648 | {"config_file_name", 1649 | (getter)Container_config_file_name, NULL, 1650 | "Path to the container configuration", 1651 | NULL}, 1652 | {"controllable", 1653 | (getter)Container_controllable, NULL, 1654 | "Boolean indicating whether the container may be controlled", 1655 | NULL}, 1656 | {"defined", 1657 | (getter)Container_defined, NULL, 1658 | "Boolean indicating whether the container configuration exists", 1659 | NULL}, 1660 | {"init_pid", 1661 | (getter)Container_init_pid, NULL, 1662 | "PID of the container's init process in the host's PID namespace", 1663 | NULL}, 1664 | {"name", 1665 | (getter)Container_name, NULL, 1666 | "Container name", 1667 | NULL}, 1668 | {"running", 1669 | (getter)Container_running, NULL, 1670 | "Boolean indicating whether the container is running or not", 1671 | NULL}, 1672 | {"state", 1673 | (getter)Container_state, NULL, 1674 | "Container state", 1675 | NULL}, 1676 | {NULL, NULL, NULL, NULL, NULL} 1677 | }; 1678 | 1679 | static PyMethodDef Container_methods[] = { 1680 | {"attach_interface", (PyCFunction)Container_attach_interface, 1681 | METH_VARARGS|METH_KEYWORDS, 1682 | "attach_interface(src_ifname, dest_ifname) -> boolean\n" 1683 | "\n" 1684 | "Pass a new network device to the container." 1685 | }, 1686 | {"detach_interface", (PyCFunction)Container_detach_interface, 1687 | METH_VARARGS|METH_KEYWORDS, 1688 | "detach_interface(ifname) -> boolean\n" 1689 | "\n" 1690 | "detach a network device from the container." 1691 | }, 1692 | {"add_device_node", (PyCFunction)Container_add_device_node, 1693 | METH_VARARGS|METH_KEYWORDS, 1694 | "add_device_node(src_path, dest_path) -> boolean\n" 1695 | "\n" 1696 | "Pass a new device to the container." 1697 | }, 1698 | {"attach", (PyCFunction)Container_attach, 1699 | METH_VARARGS|METH_KEYWORDS, 1700 | "attach(run, payload) -> int\n" 1701 | "\n" 1702 | "Attach to the container. Returns the pid of the attached process." 1703 | }, 1704 | {"attach_wait", (PyCFunction)Container_attach_wait, 1705 | METH_VARARGS|METH_KEYWORDS, 1706 | "attach(run, payload) -> int\n" 1707 | "\n" 1708 | "Attach to the container. Returns the exit code of the process." 1709 | }, 1710 | {"clear_config", (PyCFunction)Container_clear_config, 1711 | METH_NOARGS, 1712 | "clear_config()\n" 1713 | "\n" 1714 | "Clear any container configuration." 1715 | }, 1716 | {"clear_config_item", (PyCFunction)Container_clear_config_item, 1717 | METH_VARARGS|METH_KEYWORDS, 1718 | "clear_config_item(key) -> boolean\n" 1719 | "\n" 1720 | "Clear the current value of a config key." 1721 | }, 1722 | {"console", (PyCFunction)Container_console, 1723 | METH_VARARGS|METH_KEYWORDS, 1724 | "console(ttynum = -1, stdinfd = 0, stdoutfd = 1, stderrfd = 2, " 1725 | "escape = 0) -> boolean\n" 1726 | "\n" 1727 | "Attach to container's console." 1728 | }, 1729 | {"console_getfd", (PyCFunction)Container_console_getfd, 1730 | METH_VARARGS|METH_KEYWORDS, 1731 | "console(ttynum = -1) -> boolean\n" 1732 | "\n" 1733 | "Attach to container's console." 1734 | }, 1735 | {"clone", (PyCFunction)Container_clone, 1736 | METH_VARARGS|METH_KEYWORDS, 1737 | "clone(newname, config_path, flags, bdevtype, bdevdata, newsize, " 1738 | "hookargs) -> boolean\n" 1739 | "\n" 1740 | "Create a new container based on the current one." 1741 | }, 1742 | {"create", (PyCFunction)Container_create, 1743 | METH_VARARGS|METH_KEYWORDS, 1744 | "create(template, flags, bdevtype, fssize, args = (,)) -> boolean\n" 1745 | "\n" 1746 | "Create a new rootfs for the container, using the given template " 1747 | "and passing some optional arguments to it." 1748 | }, 1749 | {"destroy", (PyCFunction)Container_destroy, 1750 | METH_NOARGS, 1751 | "destroy() -> boolean\n" 1752 | "\n" 1753 | "Destroys the container." 1754 | }, 1755 | {"freeze", (PyCFunction)Container_freeze, 1756 | METH_NOARGS, 1757 | "freeze() -> boolean\n" 1758 | "\n" 1759 | "Freezes the container and returns its return code." 1760 | }, 1761 | {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, 1762 | METH_VARARGS|METH_KEYWORDS, 1763 | "get_cgroup_item(key) -> string\n" 1764 | "\n" 1765 | "Get the current value of a cgroup entry." 1766 | }, 1767 | {"get_config_item", (PyCFunction)Container_get_config_item, 1768 | METH_VARARGS|METH_KEYWORDS, 1769 | "get_config_item(key) -> string\n" 1770 | "\n" 1771 | "Get the current value of a config key." 1772 | }, 1773 | {"get_config_path", (PyCFunction)Container_get_config_path, 1774 | METH_NOARGS, 1775 | "get_config_path() -> string\n" 1776 | "\n" 1777 | "Return the LXC config path (where the containers are stored)." 1778 | }, 1779 | {"get_keys", (PyCFunction)Container_get_keys, 1780 | METH_VARARGS|METH_KEYWORDS, 1781 | "get_keys(key) -> string\n" 1782 | "\n" 1783 | "Get a list of valid sub-keys for a key." 1784 | }, 1785 | {"get_interfaces", (PyCFunction)Container_get_interfaces, 1786 | METH_NOARGS, 1787 | "get_interface() -> tuple\n" 1788 | "\n" 1789 | "Get a tuple of interfaces for the container." 1790 | }, 1791 | {"get_ips", (PyCFunction)Container_get_ips, 1792 | METH_VARARGS|METH_KEYWORDS, 1793 | "get_ips(interface, family, scope) -> tuple\n" 1794 | "\n" 1795 | "Get a tuple of IPs for the container." 1796 | }, 1797 | {"get_running_config_item", (PyCFunction)Container_get_running_config_item, 1798 | METH_VARARGS|METH_KEYWORDS, 1799 | "get_running_config_item(key) -> string\n" 1800 | "\n" 1801 | "Get the runtime value of a config key." 1802 | }, 1803 | {"load_config", (PyCFunction)Container_load_config, 1804 | METH_VARARGS|METH_KEYWORDS, 1805 | "load_config(path = DEFAULT) -> boolean\n" 1806 | "\n" 1807 | "Read the container configuration from its default " 1808 | "location or from an alternative location if provided." 1809 | }, 1810 | {"reboot", (PyCFunction)Container_reboot, 1811 | METH_NOARGS, 1812 | "reboot() -> boolean\n" 1813 | "\n" 1814 | "Ask the container to reboot." 1815 | }, 1816 | {"rename", (PyCFunction)Container_rename, 1817 | METH_VARARGS|METH_KEYWORDS, 1818 | "rename(new_name) -> boolean\n" 1819 | "\n" 1820 | "Rename the container." 1821 | }, 1822 | {"remove_device_node", (PyCFunction)Container_remove_device_node, 1823 | METH_VARARGS|METH_KEYWORDS, 1824 | "remove_device_node(src_path, dest_path) -> boolean\n" 1825 | "\n" 1826 | "Remove a device from the container." 1827 | }, 1828 | {"save_config", (PyCFunction)Container_save_config, 1829 | METH_VARARGS|METH_KEYWORDS, 1830 | "save_config(path = DEFAULT) -> boolean\n" 1831 | "\n" 1832 | "Save the container configuration to its default " 1833 | "location or to an alternative location if provided." 1834 | }, 1835 | {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, 1836 | METH_VARARGS|METH_KEYWORDS, 1837 | "set_cgroup_item(key, value) -> boolean\n" 1838 | "\n" 1839 | "Set a cgroup entry to the provided value." 1840 | }, 1841 | {"set_config_item", (PyCFunction)Container_set_config_item, 1842 | METH_VARARGS|METH_KEYWORDS, 1843 | "set_config_item(key, value) -> boolean\n" 1844 | "\n" 1845 | "Set a config key to the provided value." 1846 | }, 1847 | {"set_config_path", (PyCFunction)Container_set_config_path, 1848 | METH_VARARGS|METH_KEYWORDS, 1849 | "set_config_path(path) -> boolean\n" 1850 | "\n" 1851 | "Set the LXC config path (where the containers are stored)." 1852 | }, 1853 | {"shutdown", (PyCFunction)Container_shutdown, 1854 | METH_VARARGS|METH_KEYWORDS, 1855 | "shutdown(timeout = -1) -> boolean\n" 1856 | "\n" 1857 | "Sends SIGPWR to the container and wait for it to shutdown." 1858 | "-1 means wait forever, 0 means skip waiting." 1859 | }, 1860 | {"snapshot", (PyCFunction)Container_snapshot, 1861 | METH_VARARGS|METH_KEYWORDS, 1862 | "snapshot(comment_path = None) -> string\n" 1863 | "\n" 1864 | "Snapshot the container and return the snapshot name " 1865 | "(or False on error)." 1866 | }, 1867 | {"snapshot_destroy", (PyCFunction)Container_snapshot_destroy, 1868 | METH_VARARGS|METH_KEYWORDS, 1869 | "snapshot_destroy(name) -> boolean\n" 1870 | "\n" 1871 | "Destroy a snapshot." 1872 | }, 1873 | {"snapshot_list", (PyCFunction)Container_snapshot_list, 1874 | METH_NOARGS, 1875 | "snapshot_list() -> tuple of snapshot tuples\n" 1876 | "\n" 1877 | "List all snapshots for a container." 1878 | }, 1879 | {"snapshot_restore", (PyCFunction)Container_snapshot_restore, 1880 | METH_VARARGS|METH_KEYWORDS, 1881 | "snapshot_restore(name, newname = None) -> boolean\n" 1882 | "\n" 1883 | "Restore a container snapshot. If newname is provided a new " 1884 | "container will be created from the snapshot, otherwise an in-place " 1885 | "restore will be attempted." 1886 | }, 1887 | {"start", (PyCFunction)Container_start, 1888 | METH_VARARGS|METH_KEYWORDS, 1889 | "start(useinit = False, daemonize=True, close_fds=False, " 1890 | "cmd = (,)) -> boolean\n" 1891 | "\n" 1892 | "Start the container, return True on success.\n" 1893 | "When set useinit will make LXC use lxc-init to start the container.\n" 1894 | "The container can be started in the foreground with daemonize=False.\n" 1895 | "All fds may also be closed by passing close_fds=True." 1896 | }, 1897 | {"stop", (PyCFunction)Container_stop, 1898 | METH_NOARGS, 1899 | "stop() -> boolean\n" 1900 | "\n" 1901 | "Stop the container and returns its return code." 1902 | }, 1903 | {"unfreeze", (PyCFunction)Container_unfreeze, 1904 | METH_NOARGS, 1905 | "unfreeze() -> boolean\n" 1906 | "\n" 1907 | "Unfreezes the container and returns its return code." 1908 | }, 1909 | {"wait", (PyCFunction)Container_wait, 1910 | METH_VARARGS|METH_KEYWORDS, 1911 | "wait(state, timeout = -1) -> boolean\n" 1912 | "\n" 1913 | "Wait for the container to reach a given state or timeout." 1914 | }, 1915 | {NULL, NULL, 0, NULL} 1916 | }; 1917 | 1918 | static PyTypeObject _lxc_ContainerType = { 1919 | PyVarObject_HEAD_INIT(NULL, 0) 1920 | "lxc.Container", /* tp_name */ 1921 | sizeof(Container), /* tp_basicsize */ 1922 | 0, /* tp_itemsize */ 1923 | (destructor)Container_dealloc, /* tp_dealloc */ 1924 | 0, /* tp_print */ 1925 | 0, /* tp_getattr */ 1926 | 0, /* tp_setattr */ 1927 | 0, /* tp_reserved */ 1928 | 0, /* tp_repr */ 1929 | 0, /* tp_as_number */ 1930 | 0, /* tp_as_sequence */ 1931 | 0, /* tp_as_mapping */ 1932 | 0, /* tp_hash */ 1933 | 0, /* tp_call */ 1934 | 0, /* tp_str */ 1935 | 0, /* tp_getattro */ 1936 | 0, /* tp_setattro */ 1937 | 0, /* tp_as_buffer */ 1938 | Py_TPFLAGS_DEFAULT | 1939 | Py_TPFLAGS_BASETYPE, /* tp_flags */ 1940 | "Container objects", /* tp_doc */ 1941 | 0, /* tp_traverse */ 1942 | 0, /* tp_clear */ 1943 | 0, /* tp_richcompare */ 1944 | 0, /* tp_weaklistoffset */ 1945 | 0, /* tp_iter */ 1946 | 0, /* tp_iternext */ 1947 | Container_methods, /* tp_methods */ 1948 | 0, /* tp_members */ 1949 | Container_getseters, /* tp_getset */ 1950 | 0, /* tp_base */ 1951 | 0, /* tp_dict */ 1952 | 0, /* tp_descr_get */ 1953 | 0, /* tp_descr_set */ 1954 | 0, /* tp_dictoffset */ 1955 | (initproc)Container_init, /* tp_init */ 1956 | 0, /* tp_alloc */ 1957 | Container_new, /* tp_new */ 1958 | }; 1959 | 1960 | static PyMethodDef LXC_methods[] = { 1961 | {"arch_to_personality", (PyCFunction)LXC_arch_to_personality, METH_O, 1962 | "Returns the process personality of the corresponding architecture"}, 1963 | {"attach_run_command", (PyCFunction)LXC_attach_run_command, METH_O, 1964 | "Runs a command when attaching, to use as the run parameter for attach " 1965 | "or attach_wait"}, 1966 | {"attach_run_shell", (PyCFunction)LXC_attach_run_shell, METH_O, 1967 | "Starts up a shell when attaching, to use as the run parameter for " 1968 | "attach or attach_wait"}, 1969 | {"get_global_config_item", (PyCFunction)LXC_get_global_config_item, 1970 | METH_VARARGS|METH_KEYWORDS, 1971 | "Returns the current LXC config path"}, 1972 | {"get_version", (PyCFunction)LXC_get_version, METH_NOARGS, 1973 | "Returns the current LXC library version"}, 1974 | {"list_containers", (PyCFunction)LXC_list_containers, 1975 | METH_VARARGS|METH_KEYWORDS, 1976 | "Returns a list of container names or objects"}, 1977 | {NULL, NULL, 0, NULL} 1978 | }; 1979 | 1980 | static PyModuleDef _lxcmodule = { 1981 | PyModuleDef_HEAD_INIT, 1982 | "_lxc", 1983 | "Binding for liblxc in python", 1984 | -1, 1985 | LXC_methods 1986 | }; 1987 | 1988 | PyMODINIT_FUNC 1989 | PyInit__lxc(void) 1990 | { 1991 | PyObject* m; 1992 | PyObject* d; 1993 | 1994 | if (PyType_Ready(&_lxc_ContainerType) < 0) 1995 | return NULL; 1996 | 1997 | m = PyModule_Create(&_lxcmodule); 1998 | if (m == NULL) 1999 | return NULL; 2000 | 2001 | Py_INCREF(&_lxc_ContainerType); 2002 | PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType); 2003 | 2004 | /* add constants */ 2005 | d = PyModule_GetDict(m); 2006 | 2007 | #define PYLXC_EXPORT_CONST(c) \ 2008 | PyDict_SetItemString(d, #c, PyLong_FromLong(c)) 2009 | 2010 | /* namespace flags (no other python lib exports this) */ 2011 | PYLXC_EXPORT_CONST(CLONE_NEWUTS); 2012 | PYLXC_EXPORT_CONST(CLONE_NEWIPC); 2013 | PYLXC_EXPORT_CONST(CLONE_NEWUSER); 2014 | PYLXC_EXPORT_CONST(CLONE_NEWPID); 2015 | PYLXC_EXPORT_CONST(CLONE_NEWNET); 2016 | PYLXC_EXPORT_CONST(CLONE_NEWNS); 2017 | 2018 | /* attach: environment variable handling */ 2019 | PYLXC_EXPORT_CONST(LXC_ATTACH_CLEAR_ENV); 2020 | PYLXC_EXPORT_CONST(LXC_ATTACH_KEEP_ENV); 2021 | 2022 | /* attach: attach options */ 2023 | PYLXC_EXPORT_CONST(LXC_ATTACH_DEFAULT); 2024 | PYLXC_EXPORT_CONST(LXC_ATTACH_DROP_CAPABILITIES); 2025 | PYLXC_EXPORT_CONST(LXC_ATTACH_LSM_EXEC); 2026 | PYLXC_EXPORT_CONST(LXC_ATTACH_LSM_NOW); 2027 | PYLXC_EXPORT_CONST(LXC_ATTACH_MOVE_TO_CGROUP); 2028 | PYLXC_EXPORT_CONST(LXC_ATTACH_REMOUNT_PROC_SYS); 2029 | PYLXC_EXPORT_CONST(LXC_ATTACH_SET_PERSONALITY); 2030 | 2031 | /* clone: clone flags */ 2032 | PYLXC_EXPORT_CONST(LXC_CLONE_KEEPBDEVTYPE); 2033 | PYLXC_EXPORT_CONST(LXC_CLONE_KEEPMACADDR); 2034 | PYLXC_EXPORT_CONST(LXC_CLONE_KEEPNAME); 2035 | PYLXC_EXPORT_CONST(LXC_CLONE_MAYBE_SNAPSHOT); 2036 | PYLXC_EXPORT_CONST(LXC_CLONE_SNAPSHOT); 2037 | 2038 | /* create: create flags */ 2039 | PYLXC_EXPORT_CONST(LXC_CREATE_QUIET); 2040 | 2041 | #undef PYLXC_EXPORT_CONST 2042 | 2043 | return m; 2044 | } 2045 | 2046 | /* 2047 | * kate: space-indent on; indent-width 4; mixedindent off; indent-mode cstyle; 2048 | */ 2049 | --------------------------------------------------------------------------------