]>
Commit | Line | Data |
---|---|---|
3e67e5c9 | 1 | #!/usr/bin/env python3 |
35df7443 | 2 | # SPDX-License-Identifier: LGPL-2.1+ |
d001ac2c | 3 | |
3e67e5c9 | 4 | OUTFILE_HEADER = """#!/usr/bin/env python3 |
35df7443 | 5 | # SPDX-License-Identifier: LGPL-2.1+ |
d001ac2c DS |
6 | # |
7 | # create-sys-script.py | |
8 | # | |
9 | # (C) 2017 Canonical Ltd. | |
10 | # Author: Dan Streetman <dan.streetman@canonical.com> | |
11 | # | |
12 | # systemd is free software; you can redistribute it and/or modify it | |
13 | # under the terms of the GNU Lesser General Public License as published by | |
14 | # the Free Software Foundation; either version 2.1 of the License, or | |
15 | # (at your option) any later version. | |
16 | # | |
17 | # systemd is distributed in the hope that it will be useful, but | |
18 | # 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 License | |
23 | # along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
24 | # | |
25 | """ | |
26 | ||
27 | # Use this only to (re-)create the test/sys-script.py script, | |
28 | # after adding or modifying anything in the test/sys/ directory | |
29 | ||
30 | ||
227ef9bc ZJS |
31 | import os, sys |
32 | import stat | |
33 | import tempfile | |
34 | import filecmp | |
35 | import subprocess | |
d001ac2c | 36 | |
d001ac2c DS |
37 | OUTFILE_MODE = 0o775 |
38 | ||
39 | OUTFILE_FUNCS = r""" | |
40 | import os, sys | |
0bca7954 | 41 | import shutil |
d001ac2c DS |
42 | |
43 | def d(path, mode): | |
44 | os.mkdir(path, mode) | |
45 | ||
46 | def l(path, src): | |
47 | os.symlink(src, path) | |
48 | ||
49 | def f(path, mode, contents): | |
50 | with open(path, "wb") as f: | |
51 | f.write(contents) | |
52 | os.chmod(path, mode) | |
d001ac2c DS |
53 | """ |
54 | ||
55 | OUTFILE_MAIN = """ | |
56 | if len(sys.argv) < 2: | |
57 | exit("Usage: {} <target dir>".format(sys.argv[0])) | |
58 | ||
59 | if not os.path.isdir(sys.argv[1]): | |
60 | exit("Target dir {} not found".format(sys.argv[1])) | |
61 | ||
62 | os.chdir(sys.argv[1]) | |
63 | ||
0bca7954 ZJS |
64 | if os.path.exists('sys'): |
65 | shutil.rmtree('sys') | |
d001ac2c DS |
66 | """ |
67 | ||
68 | ||
69 | def handle_dir(outfile, path): | |
70 | m = os.lstat(path).st_mode & 0o777 | |
227ef9bc | 71 | outfile.write(f"d('{path}', {m:#o})\n") |
d001ac2c DS |
72 | |
73 | ||
74 | def handle_link(outfile, path): | |
75 | src = os.readlink(path) | |
227ef9bc | 76 | outfile.write(f"l('{path}', '{src}')\n") |
d001ac2c DS |
77 | |
78 | ||
79 | def escape_single_quotes(b): | |
80 | # remove the b'' wrapping each line repr | |
81 | r = repr(b)[2:-1] | |
82 | # python escapes all ' only if there are ' and " in the string | |
83 | if '"' not in r: | |
84 | r = r.replace("'", r"\'") | |
85 | # return line with all ' escaped | |
86 | return r | |
87 | ||
88 | ||
89 | def handle_file(outfile, path): | |
90 | m = os.lstat(path).st_mode & 0o777 | |
91 | with open(path, "rb") as f: | |
92 | b = f.read() | |
93 | if b.count(b"\n") > 1: | |
227ef9bc ZJS |
94 | r = "\n".join( escape_single_quotes(l) for l in b.split(b"\n") ) |
95 | r = f"b'''{r}'''" | |
d001ac2c DS |
96 | else: |
97 | r = repr(b) | |
227ef9bc | 98 | outfile.write(f"f('{path}', {m:#o}, {r})\n") |
d001ac2c DS |
99 | |
100 | ||
101 | def process_sysdir(outfile): | |
227ef9bc | 102 | for (dirpath, dirnames, filenames) in os.walk('sys'): |
d001ac2c DS |
103 | handle_dir(outfile, dirpath) |
104 | for d in dirnames: | |
105 | path = os.path.join(dirpath, d) | |
106 | if stat.S_ISLNK(os.lstat(path).st_mode): | |
107 | handle_link(outfile, path) | |
108 | for f in filenames: | |
109 | path = os.path.join(dirpath, f) | |
110 | mode = os.lstat(path).st_mode | |
111 | if stat.S_ISLNK(mode): | |
112 | handle_link(outfile, path) | |
113 | elif stat.S_ISREG(mode): | |
114 | handle_file(outfile, path) | |
115 | ||
116 | ||
117 | def verify_dir(tmpd, path_a): | |
118 | path_b = os.path.join(tmpd, path_a) | |
119 | mode_a = os.lstat(path_a).st_mode | |
120 | mode_b = os.lstat(path_b).st_mode | |
121 | if not stat.S_ISDIR(mode_b): | |
122 | raise Exception("Not directory") | |
123 | if (mode_a & 0o777) != (mode_b & 0o777): | |
124 | raise Exception("Permissions mismatch") | |
125 | ||
126 | ||
127 | def verify_link(tmpd, path_a): | |
128 | path_b = os.path.join(tmpd, path_a) | |
129 | if not stat.S_ISLNK(os.lstat(path_b).st_mode): | |
130 | raise Exception("Not symlink") | |
131 | if os.readlink(path_a) != os.readlink(path_b): | |
132 | raise Exception("Symlink dest mismatch") | |
133 | ||
134 | ||
135 | def verify_file(tmpd, path_a): | |
136 | path_b = os.path.join(tmpd, path_a) | |
137 | mode_a = os.lstat(path_a).st_mode | |
138 | mode_b = os.lstat(path_b).st_mode | |
139 | if not stat.S_ISREG(mode_b): | |
140 | raise Exception("Not file") | |
141 | if (mode_a & 0o777) != (mode_b & 0o777): | |
142 | raise Exception("Permissions mismatch") | |
143 | if not filecmp.cmp(path_a, path_b, shallow=False): | |
144 | raise Exception("File contents mismatch") | |
145 | ||
146 | ||
147 | def verify_script(tmpd): | |
227ef9bc | 148 | any = False |
d001ac2c | 149 | for (dirpath, dirnames, filenames) in os.walk("sys"): |
227ef9bc | 150 | any = True |
d001ac2c DS |
151 | try: |
152 | path = dirpath | |
153 | verify_dir(tmpd, path) | |
154 | for d in dirnames: | |
155 | path = os.path.join(dirpath, d) | |
156 | if stat.S_ISLNK(os.lstat(path).st_mode): | |
157 | verify_link(tmpd, path) | |
158 | for f in filenames: | |
159 | path = os.path.join(dirpath, f) | |
160 | mode = os.lstat(path).st_mode | |
161 | if stat.S_ISLNK(mode): | |
162 | verify_link(tmpd, path) | |
163 | elif stat.S_ISREG(mode): | |
164 | verify_file(tmpd, path) | |
165 | except Exception: | |
227ef9bc | 166 | print(f'FAIL on "{path}"', file=sys.stderr) |
d001ac2c | 167 | raise |
227ef9bc ZJS |
168 | if not any: |
169 | exit('Nothing found!') | |
d001ac2c DS |
170 | |
171 | if __name__ == "__main__": | |
227ef9bc ZJS |
172 | if len(sys.argv) < 2: |
173 | exit('Usage: create-sys-script.py /path/to/test/') | |
d001ac2c | 174 | |
227ef9bc ZJS |
175 | outfile = os.path.abspath(os.path.dirname(sys.argv[0]) + '/sys-script.py') |
176 | print(f'Creating {outfile} using contents of {sys.argv[1]}/sys') | |
d001ac2c | 177 | |
227ef9bc | 178 | os.chdir(sys.argv[1]) |
d001ac2c | 179 | |
227ef9bc ZJS |
180 | with open(outfile, "w") as f: |
181 | os.chmod(outfile, OUTFILE_MODE) | |
182 | f.write(OUTFILE_HEADER.replace(os.path.basename(sys.argv[0]), | |
183 | os.path.basename(outfile))) | |
d001ac2c DS |
184 | f.write(OUTFILE_FUNCS) |
185 | f.write(OUTFILE_MAIN) | |
186 | process_sysdir(f) | |
187 | ||
188 | with tempfile.TemporaryDirectory() as tmpd: | |
227ef9bc ZJS |
189 | print(f'Recreating sys/ using {outfile} at {tmpd}') |
190 | subprocess.check_call([outfile, tmpd]) | |
d001ac2c DS |
191 | verify_script(tmpd) |
192 | ||
227ef9bc | 193 | print(f'Verification successful, {outfile} is correct') |