]>
Commit | Line | Data |
---|---|---|
1e80d5d7 | 1 | #!/usr/bin/python |
b792d887 MT |
2 | ############################################################################### |
3 | # # | |
4 | # Pakfire - The IPFire package management system # | |
5 | # Copyright (C) 2011 Pakfire development team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
1e80d5d7 MT |
21 | |
22 | import logging | |
94438733 | 23 | import os |
1e80d5d7 | 24 | |
94438733 | 25 | import chroot |
1e80d5d7 | 26 | import packages |
94438733 | 27 | import util |
1e80d5d7 MT |
28 | |
29 | from constants import * | |
30 | from i18n import _ | |
31 | ||
32 | class Action(object): | |
33 | def __init__(self, pakfire, pkg): | |
34 | self.pakfire = pakfire | |
6ee3d6b9 | 35 | self.pkg_solv = self.pkg = pkg |
1e80d5d7 MT |
36 | |
37 | # Try to get the binary version of the package from the cache if | |
38 | # any. | |
39 | binary_package = self.pkg.get_from_cache() | |
40 | if binary_package: | |
41 | self.pkg = binary_package | |
42 | ||
94438733 MT |
43 | self.init() |
44 | ||
aa9f2645 MT |
45 | def __cmp__(self, other): |
46 | return cmp(self.pkg, other.pkg) | |
47 | ||
1e80d5d7 MT |
48 | def __repr__(self): |
49 | return "<%s %s>" % (self.__class__.__name__, self.pkg.friendly_name) | |
50 | ||
94438733 MT |
51 | def init(self): |
52 | # A function to run additional initialization. | |
53 | pass | |
54 | ||
862bea4d MT |
55 | def check(self, filelist): |
56 | # This is just a dummy test that does nothing at all. | |
57 | return filelist | |
58 | ||
1e80d5d7 MT |
59 | @property |
60 | def needs_download(self): | |
61 | return self.type in ("install", "reinstall", "upgrade", "downgrade",) \ | |
62 | and not isinstance(self.pkg, packages.BinaryPackage) | |
63 | ||
64 | def download(self, text): | |
65 | if not self.needs_download: | |
66 | return | |
67 | ||
68 | self.pkg = self.pkg.download(text) | |
69 | ||
70 | def run(self): | |
71 | raise NotImplementedError | |
72 | ||
73 | @property | |
74 | def local(self): | |
75 | """ | |
76 | Reference to local repository. | |
77 | """ | |
78 | return self.pakfire.repos.local | |
79 | ||
1e80d5d7 MT |
80 | |
81 | class ActionScript(Action): | |
d767668b | 82 | type = "script" |
c07a3ca7 | 83 | script_action = None |
d767668b | 84 | |
94438733 MT |
85 | def init(self): |
86 | # Load the scriplet. | |
c07a3ca7 | 87 | self.scriptlet = self.pkg.get_scriptlet(self.script_action) |
94438733 MT |
88 | |
89 | @property | |
90 | def interpreter(self): | |
91 | """ | |
92 | Get the interpreter of this scriptlet. | |
93 | """ | |
c07a3ca7 | 94 | return util.scriptlet_interpreter(self.scriptlet) |
94438733 MT |
95 | |
96 | @property | |
97 | def args(self): | |
c07a3ca7 | 98 | return [] |
94438733 | 99 | |
1e80d5d7 | 100 | def run(self): |
94438733 MT |
101 | # Exit immediately, if the scriptlet is empty. |
102 | if not self.scriptlet: | |
103 | return | |
104 | ||
105 | # Actually run the scriplet. | |
106 | logging.debug("Running scriptlet %s" % self) | |
107 | ||
108 | # Check if the interpreter does exist and is executable. | |
c07a3ca7 MT |
109 | if self.interpreter: |
110 | interpreter = "%s/%s" % (self.pakfire.path, self.interpreter) | |
111 | if not os.path.exists(interpreter): | |
112 | raise ActionError, _("Cannot run scriptlet because no interpreter is available: %s" \ | |
113 | % self.interpreter) | |
94438733 | 114 | |
c07a3ca7 MT |
115 | if not os.access(interpreter, os.X_OK): |
116 | raise ActionError, _("Cannot run scriptlet because the interpreter is not executable: %s" \ | |
117 | % self.interpreter) | |
94438733 MT |
118 | |
119 | # Create a name for the temporary script file. | |
120 | script_file_chroot = os.path.join("/", LOCAL_TMP_PATH, | |
121 | "scriptlet_%s" % util.random_string(10)) | |
122 | script_file = os.path.join(self.pakfire.path, script_file_chroot[1:]) | |
123 | assert script_file.startswith("%s/" % self.pakfire.path) | |
124 | ||
125 | # Create script directory, if it does not exist. | |
126 | script_dir = os.path.dirname(script_file) | |
127 | if not os.path.exists(script_dir): | |
128 | os.makedirs(script_dir) | |
129 | ||
130 | # Write the scriptlet to a file that we can execute it. | |
131 | try: | |
132 | f = open(script_file, "wb") | |
133 | f.write(self.scriptlet) | |
134 | f.close() | |
135 | ||
136 | # The file is only accessable by root. | |
137 | os.chmod(script_file, 700) | |
138 | except: | |
139 | # Remove the file if an error occurs. | |
140 | try: | |
141 | os.unlink(script_file) | |
142 | except OSError: | |
143 | pass | |
144 | ||
145 | # XXX catch errors and return a beautiful message to the user | |
146 | raise | |
147 | ||
c07a3ca7 MT |
148 | # Generate the script command. |
149 | command = [script_file_chroot] + self.args | |
94438733 MT |
150 | |
151 | # If we are running in /, we do not need to chroot there. | |
c07a3ca7 | 152 | chroot_path = None |
94438733 | 153 | if not self.pakfire.path == "/": |
c07a3ca7 | 154 | chroot_path = self.pakfire.path |
94438733 MT |
155 | |
156 | try: | |
157 | ret = chroot.do(command, cwd="/tmp", | |
158 | chrootPath=chroot_path, | |
159 | personality=self.pakfire.distro.personality, | |
160 | shell=False, | |
161 | timeout=SCRIPTLET_TIMEOUT, | |
162 | logger=logging.getLogger()) | |
163 | ||
164 | except Error, e: | |
165 | raise ActionError, _("The scriptlet returned an error:\n%s" % e) | |
166 | ||
167 | except commandTimeoutExpired: | |
168 | raise ActionError, _("The scriptlet ran more than %s seconds and was killed." \ | |
169 | % SCRIPTLET_TIMEOUT) | |
170 | ||
171 | finally: | |
172 | # Remove the script file. | |
173 | try: | |
174 | os.unlink(script_file) | |
175 | except OSError: | |
176 | logging.debug("Could not remove scriptlet file: %s" % script_file) | |
1e80d5d7 MT |
177 | |
178 | ||
179 | class ActionScriptPreIn(ActionScript): | |
c07a3ca7 | 180 | script_action = "prein" |
1e80d5d7 MT |
181 | |
182 | ||
183 | class ActionScriptPostIn(ActionScript): | |
c07a3ca7 | 184 | script_action = "postin" |
1e80d5d7 MT |
185 | |
186 | ||
187 | class ActionScriptPreUn(ActionScript): | |
c07a3ca7 | 188 | script_action = "preun" |
1e80d5d7 MT |
189 | |
190 | ||
191 | class ActionScriptPostUn(ActionScript): | |
c07a3ca7 | 192 | script_action = "postun" |
1e80d5d7 MT |
193 | |
194 | ||
d767668b | 195 | class ActionScriptPreUp(ActionScript): |
c07a3ca7 | 196 | script_action = "preup" |
d767668b MT |
197 | |
198 | ||
199 | class ActionScriptPostUp(ActionScript): | |
c07a3ca7 | 200 | script_action = "postup" |
d767668b MT |
201 | |
202 | ||
203 | class ActionScriptPostTrans(ActionScript): | |
204 | pass | |
205 | ||
206 | ||
207 | class ActionScriptPostTransIn(ActionScriptPostTrans): | |
c07a3ca7 | 208 | script_action = "posttransin" |
d767668b MT |
209 | |
210 | ||
211 | class ActionScriptPostTransUn(ActionScriptPostTrans): | |
c07a3ca7 | 212 | script_action = "posttransun" |
d767668b MT |
213 | |
214 | ||
215 | class ActionScriptPostTransUp(ActionScriptPostTrans): | |
c07a3ca7 | 216 | script_action = "posttransup" |
d767668b MT |
217 | |
218 | ||
1e80d5d7 MT |
219 | class ActionInstall(Action): |
220 | type = "install" | |
221 | ||
862bea4d MT |
222 | def check(self, check): |
223 | logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) | |
224 | ||
225 | # Check if this package can be installed. | |
226 | check.install(self.pkg) | |
227 | ||
1e80d5d7 | 228 | def run(self): |
e871a081 MT |
229 | # Add package to the database. |
230 | self.local.add_package(self.pkg) | |
231 | ||
232 | self.pkg.extract(_("Installing"), prefix=self.pakfire.path) | |
1e80d5d7 MT |
233 | |
234 | ||
235 | class ActionUpdate(Action): | |
236 | type = "upgrade" | |
237 | ||
862bea4d MT |
238 | def check(self, check): |
239 | logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) | |
240 | ||
241 | # Check if this package can be updated. | |
242 | check.update(self.pkg) | |
243 | ||
1e80d5d7 | 244 | def run(self): |
e871a081 MT |
245 | # Add new package to the database. |
246 | self.local.add_package(self.pkg) | |
247 | ||
248 | self.pkg.extract(_("Updating"), prefix=self.pakfire.path) | |
1e80d5d7 MT |
249 | |
250 | ||
6ee3d6b9 | 251 | class ActionRemove(Action): |
1e80d5d7 MT |
252 | type = "erase" |
253 | ||
6ee3d6b9 MT |
254 | def __init__(self, *args, **kwargs): |
255 | Action.__init__(self, *args, **kwargs) | |
1e80d5d7 | 256 | |
6ee3d6b9 MT |
257 | # XXX This is ugly, but works for the moment. |
258 | self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv) | |
259 | assert self.pkg | |
260 | ||
862bea4d MT |
261 | def check(self, check): |
262 | logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) | |
263 | ||
264 | # Check if this package can be removed. | |
265 | check.remove(self.pkg) | |
266 | ||
6ee3d6b9 | 267 | def run(self): |
890c7e93 | 268 | self.pkg.cleanup(_("Removing"), prefix=self.pakfire.path) |
1e80d5d7 | 269 | |
e871a081 MT |
270 | # Remove package from the database. |
271 | self.local.rem_package(self.pkg) | |
272 | ||
273 | ||
274 | class ActionCleanup(Action): | |
275 | type = "ignore" | |
276 | ||
277 | def __init__(self, *args, **kwargs): | |
278 | Action.__init__(self, *args, **kwargs) | |
279 | ||
280 | # XXX This is ugly, but works for the moment. | |
281 | self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv) | |
282 | assert self.pkg | |
283 | ||
862bea4d MT |
284 | def check(self, check): |
285 | logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) | |
286 | ||
287 | # Check if this package can be removed. | |
288 | check.cleanup(self.pkg) | |
289 | ||
e871a081 MT |
290 | def run(self): |
291 | # Cleaning up leftover files and stuff. | |
292 | self.pkg.cleanup(_("Cleanup"), prefix=self.pakfire.path) | |
293 | ||
294 | # Remove package from the database. | |
295 | self.local.rem_package(self.pkg) | |
1e80d5d7 MT |
296 | |
297 | ||
298 | class ActionReinstall(Action): | |
299 | type = "reinstall" | |
300 | ||
862bea4d MT |
301 | def check(self, check): |
302 | logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) | |
303 | ||
304 | # Check if this package can be reinstalled. | |
305 | check.remove(self.pkg) | |
306 | check.install(self.pkg) | |
307 | ||
1e80d5d7 | 308 | def run(self): |
e871a081 MT |
309 | # Remove package from the database and add it afterwards. |
310 | # Sounds weird, but fixes broken entries in the database. | |
311 | self.local.rem_package(self.pkg) | |
312 | self.local.add_package(self.pkg) | |
313 | ||
314 | self.pkg.extract(_("Installing"), prefix=self.pakfire.path) | |
1e80d5d7 MT |
315 | |
316 | ||
317 | class ActionDowngrade(Action): | |
318 | type = "downgrade" | |
319 | ||
862bea4d MT |
320 | def check(self, check): |
321 | logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) | |
322 | ||
323 | # Check if this package can be downgraded. | |
324 | check.install(self.pkg) | |
325 | ||
1e80d5d7 | 326 | def run(self): |
e871a081 MT |
327 | # Add new package to database. |
328 | self.local.add_package(self.pkg) | |
329 | ||
330 | self.pkg.extract(_("Downgrading"), prefix=self.pakfire.path) | |
1e80d5d7 MT |
331 | |
332 | ||
333 | class ActionChange(Action): | |
334 | type = "change" | |
335 | ||
e871a081 MT |
336 | # XXX still need to find out what this should be doing |
337 | ||
1e80d5d7 MT |
338 | def run(self): |
339 | print "XXX Change: %s" % self.pkg |