chorut 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,151 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # UV
86
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
87
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
88
+ # commonly ignored for libraries.
89
+ uv.lock
90
+
91
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
92
+ __pypackages__/
93
+
94
+ # Celery stuff
95
+ celerybeat-schedule
96
+ celerybeat.pid
97
+
98
+ # SageMath parsed files
99
+ *.sage.py
100
+
101
+ # Environments
102
+ .env
103
+ .envrc
104
+ .venv
105
+ env/
106
+ venv/
107
+ ENV/
108
+ env.bak/
109
+ venv.bak/
110
+
111
+ # Spyder project settings
112
+ .spyderproject
113
+ .spyproject
114
+
115
+ # Rope project settings
116
+ .ropeproject
117
+
118
+ # mkdocs documentation
119
+ /site
120
+
121
+ # mypy
122
+ .mypy_cache/
123
+ .dmypy.json
124
+ dmypy.json
125
+
126
+ # Pyre type checker
127
+ .pyre/
128
+
129
+ # pytype static type analyzer
130
+ .pytype/
131
+
132
+ # Cython debug symbols
133
+ cython_debug/
134
+
135
+ # Visual Studio Code
136
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
137
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
138
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
139
+ # you could uncomment the following to ignore the entire vscode folder
140
+ .vscode/
141
+
142
+ # Ruff stuff:
143
+ .ruff_cache/
144
+
145
+ # PyPI configuration file
146
+ .pypirc
147
+
148
+ # Marimo
149
+ marimo/_static/
150
+ marimo/_lsp/
151
+ __marimo__/
chorut-0.1.4/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Antal A. Buss
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
chorut-0.1.4/PKG-INFO ADDED
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: chorut
3
+ Version: 0.1.4
4
+ Summary: Python implementation of an enhanced chroot functionality with minimal dependencies
5
+ Project-URL: Homepage, https://github.com/abuss/chorut
6
+ Project-URL: Repository, https://github.com/abuss/chorut.git
7
+ Project-URL: Issues, https://github.com/abuss/chorut/issues
8
+ Project-URL: Documentation, https://github.com/abuss/chorut#readme
9
+ Author: Antal Buss
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: chroot,containers,linux,namespaces
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: System :: Operating System
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: Utilities
26
+ Requires-Python: >=3.12
27
+ Provides-Extra: dev
28
+ Requires-Dist: pre-commit>=4.3.0; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
30
+ Requires-Dist: pytest>=7.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # chorut
35
+
36
+ A Python library that provides chroot functionality inspired by arch-chroot with minimal dependencies, using only Python standard library modules.
37
+
38
+ ## Features
39
+
40
+ - **Complete chroot setup**: Automatically mounts proc, sys, dev, devpts, shm, run, and tmp filesystems
41
+ - **Custom mounts**: Support for user-defined bind mounts and filesystem mounts
42
+ - **Unshare mode**: Support for running as non-root user using Linux namespaces
43
+ - **Context manager**: Clean automatic setup and teardown
44
+ - **resolv.conf handling**: Proper DNS configuration in chroot
45
+ - **String and list commands**: Execute commands using either list format or string format
46
+ - **Output capture**: Capture stdout and stderr from executed commands
47
+ - **Error handling**: Comprehensive error reporting and cleanup
48
+ - **Zero external dependencies**: Uses only Python standard library
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install chorut
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ### As a Library
59
+
60
+ ```python
61
+ from chorut import ChrootManager
62
+
63
+ # Basic usage as root
64
+ with ChrootManager('/path/to/chroot') as chroot:
65
+ result = chroot.execute(['ls', '-la'])
66
+
67
+ # String commands (parsed with shlex.split)
68
+ with ChrootManager('/path/to/chroot') as chroot:
69
+ result = chroot.execute('ls -la /etc')
70
+
71
+ # Output capture
72
+ with ChrootManager('/path/to/chroot') as chroot:
73
+ result = chroot.execute('cat /etc/hostname', capture_output=True)
74
+ if result.returncode == 0:
75
+ hostname = result.stdout.strip()
76
+ print(f"Hostname: {hostname}")
77
+
78
+ # Non-root usage with unshare mode (requires complete chroot environment)
79
+ with ChrootManager('/path/to/complete/chroot', unshare_mode=True) as chroot:
80
+ result = chroot.execute(['whoami'])
81
+
82
+ # Manual setup/teardown
83
+ chroot = ChrootManager('/path/to/chroot')
84
+ chroot.setup()
85
+ try:
86
+ result = chroot.execute(['bash', '-c', 'echo "Hello from chroot"'])
87
+ finally:
88
+ chroot.teardown()
89
+
90
+ # With custom mounts
91
+ custom_mounts = [
92
+ {
93
+ "source": "/home",
94
+ "target": "home",
95
+ "bind": True,
96
+ "options": "ro" # Read-only bind mount
97
+ },
98
+ {
99
+ "source": "tmpfs",
100
+ "target": "workspace",
101
+ "fstype": "tmpfs",
102
+ "options": "size=1G"
103
+ }
104
+ ]
105
+
106
+ with ChrootManager('/path/to/chroot', custom_mounts=custom_mounts) as chroot:
107
+ result = chroot.execute(['df', '-h'])
108
+ ```
109
+
110
+ ### String Commands and Shell Features
111
+
112
+ The `execute` method accepts both list and string commands:
113
+
114
+ ```python
115
+ # List format (recommended for complex commands)
116
+ result = chroot.execute(['ls', '-la', '/etc'])
117
+
118
+ # String format (parsed with shlex.split or auto-wrapped with bash -c)
119
+ result = chroot.execute('ls -la /etc')
120
+
121
+ # Shell features now work automatically (auto_shell=True by default)
122
+ result = chroot.execute('ls | wc -l') # Pipes
123
+ result = chroot.execute('echo hello && echo world') # Logical operators
124
+ result = chroot.execute('echo `date`') # Command substitution
125
+ result = chroot.execute('ls *.txt') # Glob patterns
126
+ result = chroot.execute('echo $HOME') # Variable expansion
127
+
128
+ # Manual shell invocation still works
129
+ result = chroot.execute("bash -c 'ls | wc -l'")
130
+
131
+ # Disable auto-detection by setting auto_shell=False
132
+ chroot_manual = ChrootManager('/path/to/chroot', auto_shell=False)
133
+ result = chroot_manual.execute("bash -c 'ls | wc -l'") # Explicit bash -c needed
134
+ ```
135
+
136
+ **Auto-Detection**: By default (`auto_shell=True`), string commands are automatically analyzed for shell metacharacters (pipes `|`, logical operators `&&`/`||`, redirects `<>`/`>`, command substitution `` `cmd` ``/`$(cmd)`, glob patterns `*`/`?`, variable expansion `$VAR`, etc.). When detected, the command is automatically wrapped with `bash -c`. Simple commands are still parsed with `shlex.split()` for security.
137
+
138
+ ### Output Capture
139
+
140
+ Capture command output using the `capture_output` parameter:
141
+
142
+ ```python
143
+ # Capture both stdout and stderr
144
+ result = chroot.execute('cat /etc/hostname', capture_output=True)
145
+ if result.returncode == 0:
146
+ hostname = result.stdout.strip()
147
+
148
+ # Capture with error handling
149
+ result = chroot.execute('ls /nonexistent', capture_output=True)
150
+ if result.returncode != 0:
151
+ error_msg = result.stderr.strip()
152
+
153
+ # Get raw bytes instead of text
154
+ result = chroot.execute('cat binary_file', capture_output=True, text=False)
155
+ binary_data = result.stdout
156
+ ```
157
+
158
+ ### Custom Mounts
159
+
160
+ You can specify additional mounts to be set up in the chroot environment. Each mount specification is a dictionary with the following keys:
161
+
162
+ - `source` (required): Source path, device, or filesystem type
163
+ - `target` (required): Target path relative to chroot root
164
+ - `fstype` (optional): Filesystem type (e.g., "tmpfs", "ext4")
165
+ - `options` (optional): Mount options (e.g., "ro", "size=1G")
166
+ - `bind` (optional): Whether this is a bind mount (default: False)
167
+ - `mkdir` (optional): Whether to create target directory (default: True)
168
+
169
+ #### Examples:
170
+
171
+ ```python
172
+ # Bind mount home directory as read-only
173
+ {
174
+ "source": "/home",
175
+ "target": "home",
176
+ "bind": True,
177
+ "options": "ro"
178
+ }
179
+
180
+ # Create a tmpfs workspace
181
+ {
182
+ "source": "tmpfs",
183
+ "target": "tmp/workspace",
184
+ "fstype": "tmpfs",
185
+ "options": "size=512M,mode=1777"
186
+ }
187
+
188
+ # Bind mount a specific directory
189
+ {
190
+ "source": "/var/cache/pacman",
191
+ "target": "var/cache/pacman",
192
+ "bind": True
193
+ }
194
+ ```
195
+
196
+ ### Command Line
197
+
198
+ ```bash
199
+ # Basic chroot (requires root)
200
+ sudo chorut /path/to/chroot
201
+
202
+ # Run specific command
203
+ sudo chorut /path/to/chroot ls -la
204
+
205
+ # Non-root mode (requires proper chroot environment)
206
+ chorut -N /path/to/complete/chroot
207
+
208
+ # Specify user
209
+ sudo chorut -u user:group /path/to/chroot
210
+
211
+ # Verbose output
212
+ chorut -v -N /path/to/chroot
213
+
214
+ # Custom mounts
215
+ chorut -m "/home:home:bind,ro" -m "tmpfs:workspace:size=1G" /path/to/chroot
216
+
217
+ # Multiple custom mounts
218
+ chorut -N \
219
+ -m "/var/cache:var/cache:bind" \
220
+ -m "tmpfs:tmp/build:size=2G" \
221
+ /path/to/chroot make -j4
222
+ ```
223
+
224
+ #### Command Line Mount Format
225
+
226
+ The `-m/--mount` option accepts mount specifications in the format:
227
+
228
+ ```
229
+ SOURCE:TARGET[:OPTIONS]
230
+ ```
231
+
232
+ - **SOURCE**: Source path, device, or filesystem type
233
+ - **TARGET**: Target path relative to chroot (without leading slash)
234
+ - **OPTIONS**: Comma-separated mount options (optional)
235
+
236
+ Special options:
237
+ - `bind` - Creates a bind mount
238
+ - Other options are passed to the mount command
239
+
240
+ Examples:
241
+ - `-m "/home:home:bind,ro"` - Read-only bind mount of /home
242
+ - `-m "tmpfs:workspace:size=1G"` - 1GB tmpfs at /workspace
243
+ - `-m "/dev/sdb1:mnt/data:rw"` - Mount device with read-write access
244
+
245
+ ### Command Line Options
246
+
247
+ - `-h, --help`: Show help message
248
+ - `-N, --unshare`: Run in unshare mode as regular user
249
+ - `-u USER[:GROUP], --userspec USER[:GROUP]`: Specify user/group to run as
250
+ - `-v, --verbose`: Enable verbose logging
251
+ - `-m SOURCE:TARGET[:OPTIONS], --mount SOURCE:TARGET[:OPTIONS]`: Add custom mount (can be used multiple times)
252
+
253
+ ## API Reference
254
+
255
+ ### ChrootManager
256
+
257
+ The main class for managing chroot environments.
258
+
259
+ #### Constructor
260
+
261
+ ```python
262
+ ChrootManager(chroot_dir, unshare_mode=False, custom_mounts=None, auto_shell=True)
263
+ ```
264
+
265
+ - `chroot_dir`: Path to the chroot directory
266
+ - `unshare_mode`: Whether to use unshare mode for non-root operation
267
+ - `custom_mounts`: Optional list of custom mount specifications
268
+ - `auto_shell`: Whether to automatically detect shell features in string commands and wrap them with 'bash -c' (default: True)
269
+
270
+ #### Methods
271
+
272
+ - `setup()`: Set up the chroot environment
273
+ - `teardown()`: Clean up the chroot environment
274
+ - `execute(command=None, userspec=None, capture_output=False, text=True)`: Execute a command in the chroot
275
+
276
+ ##### execute() Parameters
277
+
278
+ - `command`: Command to execute. Can be:
279
+ - `list[str]`: List of command and arguments (e.g., `['ls', '-la']`)
280
+ - `str`: String command parsed with `shlex.split()` (e.g., `'ls -la'`)
281
+ - `None`: Start interactive shell
282
+ - `userspec`: User specification in format "user" or "user:group"
283
+ - `capture_output`: If `True`, capture stdout and stderr (default: `False`)
284
+ - `text`: If `True`, decode output as text; if `False`, return bytes (default: `True`)
285
+
286
+ ##### execute() Return Value
287
+
288
+ Returns a `subprocess.CompletedProcess` object with:
289
+ - `returncode`: Exit code of the command
290
+ - `stdout`: Command output (if `capture_output=True`)
291
+ - `stderr`: Command error output (if `capture_output=True`)
292
+
293
+ ##### execute() Examples
294
+
295
+ ```python
296
+ # List command
297
+ result = chroot.execute(['ls', '-la'])
298
+
299
+ # String command
300
+ result = chroot.execute('ls -la')
301
+
302
+ # With output capture
303
+ result = chroot.execute('cat /etc/hostname', capture_output=True)
304
+ hostname = result.stdout.strip()
305
+
306
+ # Shell features require explicit bash
307
+ result = chroot.execute("bash -c 'ls | wc -l'", capture_output=True)
308
+ line_count = int(result.stdout.strip())
309
+
310
+ # Interactive shell (command=None)
311
+ chroot.execute() # Starts bash shell
312
+ ```
313
+
314
+ ### Exceptions
315
+
316
+ - `ChrootError`: Raised for chroot-related errors
317
+ - `MountError`: Raised for mount-related errors
318
+
319
+ ## Requirements
320
+
321
+ - Python 3.12+
322
+ - Linux system with mount/umount utilities
323
+ - Root privileges (unless using unshare mode)
324
+
325
+ ### Unshare Mode Requirements
326
+
327
+ When using unshare mode (`-N` flag), the following additional requirements apply:
328
+
329
+ - `unshare` command must be available
330
+ - The chroot directory must contain a complete filesystem with:
331
+ - Essential binaries in `/bin`, `/usr/bin`, etc.
332
+ - Required libraries in `/lib`, `/lib64`, `/usr/lib`, etc.
333
+ - Proper directory structure (`/etc`, `/proc`, `/sys`, `/dev`, etc.)
334
+
335
+ **Note**: Unshare mode performs all mount operations within an unshared mount namespace, allowing non-root users to create chroot environments. However, the target directory must still contain a complete, functional filesystem for the chroot to work properly.
336
+
337
+ For example, trying to chroot into `/tmp` will fail because it lacks the necessary binaries and libraries. You need a proper root filesystem (like those created by `debootstrap`, `pacstrap`, or similar tools).
338
+
339
+ ## License
340
+
341
+ This project is in the public domain.