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