1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
import os
import signal
import pytest
from snakeoil import process
from snakeoil.contexts import chdir
from snakeoil.process import spawn
BASH_BINARY = process.find_binary("bash", fallback="")
@pytest.mark.skipif(not BASH_BINARY, reason="missing bash binary")
class TestSpawn:
@pytest.fixture(autouse=True)
def _setup(self, tmp_path):
orig_path = os.environ["PATH"]
os.environ["PATH"] = ":".join([str(tmp_path)] + orig_path.split(":"))
yield
os.environ["PATH"] = orig_path
@pytest.fixture
def dev_null(self):
"""Return an open write file descriptor to `/dev/null`."""
with open("/dev/null", "w") as null_file:
yield null_file.fileno()
def generate_script(self, tmp_path, filename, text):
assert not os.path.isabs(filename)
fp = tmp_path / filename
fp.write_text("#!/usr/bin/env bash\n" + text)
fp.chmod(0o750)
assert fp.stat().st_mode & 0o750 == 0o750
return fp
def test_get_output(self, tmp_path, dev_null):
filename = "spawn-getoutput.sh"
for r, s, text, args in (
[0, ["dar\n"], "echo dar\n", {}],
[0, ["dar"], "echo -n dar", {}],
[1, ["blah\n", "dar\n"], "echo blah\necho dar\nexit 1", {}],
[0, [], "echo dar 1>&2", {"fd_pipes": {1: 1, 2: dev_null}}],
):
fp = self.generate_script(tmp_path, filename, text)
assert (r, s) == spawn.spawn_get_output(
str(fp), spawn_type=spawn.spawn_bash, **args
)
os.unlink(fp)
@pytest.mark.skipif(not spawn.is_sandbox_capable(), reason="missing sandbox binary")
def test_sandbox(self, tmp_path):
fp = self.generate_script(tmp_path, "spawn-sandbox.sh", "echo $LD_PRELOAD")
ret = spawn.spawn_get_output(str(fp), spawn_type=spawn.spawn_sandbox)
assert ret[1], "no output; exit code was %s; script location %s" % (ret[0], fp)
assert "libsandbox.so" in [
os.path.basename(x.strip()) for x in ret[1][0].split()
]
os.unlink(fp)
@pytest.mark.skipif(not spawn.is_sandbox_capable(), reason="missing sandbox binary")
def test_sandbox_empty_dir(self, tmp_path):
"""sandbox gets pissy if it's ran from a nonexistent dir
this verifies our fix works.
"""
fp = self.generate_script(tmp_path, "spawn-sandbox.sh", "echo $LD_PRELOAD")
dpath = tmp_path / "dar"
dpath.mkdir()
with chdir(dpath):
dpath.rmdir()
assert "libsandbox.so" in [
os.path.basename(x.strip())
for x in spawn.spawn_get_output(
str(fp), spawn_type=spawn.spawn_sandbox, cwd="/"
)[1][0].split()
]
fp.unlink()
def test_process_exit_code(self):
assert spawn.process_exit_code(0) == 0
assert spawn.process_exit_code(16 << 8) == 16
def generate_background_pid(self):
try:
return spawn.spawn(["sleep", "5s"], returnpid=True)[0]
except process.CommandNotFound:
pytest.skip("can't complete the test, sleep binary doesn't exist")
def test_spawn_returnpid(self):
pid = self.generate_background_pid()
try:
assert os.kill(pid, 0) is None, "returned pid was invalid, or sleep died"
assert pid in spawn.spawned_pids, "pid wasn't recorded in global pids"
finally:
os.kill(pid, signal.SIGKILL)
def test_cleanup_pids(self):
pid = self.generate_background_pid()
spawn.cleanup_pids([pid])
with pytest.raises(OSError):
os.kill(pid, 0)
assert pid not in spawn.spawned_pids, "pid wasn't removed from global pids"
def test_spawn_bash(self, capfd):
# bash builtin for true without exec'ing true (eg, no path lookup)
assert 0 == spawn.spawn_bash("echo bash")
out, _err = capfd.readouterr()
assert out.strip() == "bash"
def test_umask(self, tmp_path):
fp = self.generate_script(tmp_path, "spawn-umask.sh", f"#!{BASH_BINARY}\numask")
try:
old_umask = os.umask(0)
if old_umask == 0:
# crap.
desired = 0o22
os.umask(desired)
else:
desired = 0
assert str(desired).lstrip("0") == spawn.spawn_get_output(str(fp))[1][
0
].strip().lstrip("0")
finally:
os.umask(old_umask)
|