]> git.ipfire.org Git - people/stevee/pakfire.git/blob - python/pakfire/actions.py
logging: Make own pakfire logger.
[people/stevee/pakfire.git] / python / pakfire / actions.py
1 #!/usr/bin/python
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 ###############################################################################
21
22 import os
23
24 import chroot
25 import packages
26 import util
27
28 import logging
29 log = logging.getLogger("pakfire")
30
31 from constants import *
32 from i18n import _
33
34 class Action(object):
35 def __init__(self, pakfire, pkg):
36 self.pakfire = pakfire
37 self.pkg_solv = self.pkg = pkg
38
39 # Try to get the binary version of the package from the cache if
40 # any.
41 binary_package = self.pkg.get_from_cache()
42 if binary_package:
43 self.pkg = binary_package
44
45 self.init()
46
47 def __cmp__(self, other):
48 return cmp(self.pkg, other.pkg)
49
50 def __repr__(self):
51 return "<%s %s>" % (self.__class__.__name__, self.pkg.friendly_name)
52
53 def init(self):
54 # A function to run additional initialization.
55 pass
56
57 def check(self, filelist):
58 # This is just a dummy test that does nothing at all.
59 return filelist
60
61 @property
62 def needs_download(self):
63 return self.type in ("install", "reinstall", "upgrade", "downgrade", "change",) \
64 and not isinstance(self.pkg, packages.BinaryPackage)
65
66 def download(self, text):
67 if not self.needs_download:
68 return
69
70 self.pkg = self.pkg.download(text)
71
72 def run(self):
73 raise NotImplementedError
74
75 @property
76 def local(self):
77 """
78 Reference to local repository.
79 """
80 return self.pakfire.repos.local
81
82 def do(self, cmd, **kwargs):
83 # If we are running in /, we do not need to chroot there.
84 chroot_path = None
85 if not self.pakfire.path == "/":
86 chroot_path = self.pakfire.path
87
88 # Find suitable cwd.
89 cwd = "/"
90 for i in ("tmp", "root"):
91 if chroot_path:
92 _cwd = os.path.join(chroot_path, i)
93 else:
94 _cwd = i
95
96 if os.path.exists(_cwd):
97 cwd = _cwd
98 break
99
100 args = {
101 "cwd" : cwd,
102 "logger" : log,
103 "personality" : self.pakfire.distro.personality,
104 "shell" : False,
105 "timeout" : SCRIPTLET_TIMEOUT,
106 }
107
108 # Overwrite by args that were passed.
109 args.update(kwargs)
110
111 # You can never overwrite chrootPath.
112 args.update({
113 "chrootPath" : chroot_path,
114 })
115
116 return chroot.do(cmd, **args)
117
118
119 class ActionScript(Action):
120 type = "script"
121 script_action = None
122
123 def init(self):
124 # Load the scriplet.
125 self.scriptlet = self.pkg.get_scriptlet(self.script_action)
126
127 @property
128 def interpreter(self):
129 """
130 Get the interpreter of this scriptlet.
131 """
132 return util.scriptlet_interpreter(self.scriptlet)
133
134 @property
135 def args(self):
136 return []
137
138 def run(self):
139 # Exit immediately, if the scriptlet is empty.
140 if not self.scriptlet:
141 return
142
143 # Actually run the scriplet.
144 log.debug("Running scriptlet %s" % self)
145
146 # Check if the interpreter does exist and is executable.
147 if self.interpreter:
148 interpreter = "%s/%s" % (self.pakfire.path, self.interpreter)
149 if not os.path.exists(interpreter):
150 raise ActionError, _("Cannot run scriptlet because no interpreter is available: %s" \
151 % self.interpreter)
152
153 if not os.access(interpreter, os.X_OK):
154 raise ActionError, _("Cannot run scriptlet because the interpreter is not executable: %s" \
155 % self.interpreter)
156
157 # Create a name for the temporary script file.
158 script_file_chroot = os.path.join("/", LOCAL_TMP_PATH,
159 "scriptlet_%s" % util.random_string(10))
160 script_file = os.path.join(self.pakfire.path, script_file_chroot[1:])
161 assert script_file.startswith(self.pakfire.path)
162
163 # Create script directory, if it does not exist.
164 script_dir = os.path.dirname(script_file)
165 if not os.path.exists(script_dir):
166 os.makedirs(script_dir)
167
168 # Write the scriptlet to a file that we can execute it.
169 try:
170 f = open(script_file, "wb")
171 f.write(self.scriptlet)
172 f.close()
173
174 # The file is only accessable by root.
175 os.chmod(script_file, 700)
176 except:
177 # Remove the file if an error occurs.
178 try:
179 os.unlink(script_file)
180 except OSError:
181 pass
182
183 # XXX catch errors and return a beautiful message to the user
184 raise
185
186 # Generate the script command.
187 command = [script_file_chroot] + self.args
188
189 try:
190 self.do(command)
191
192 except Error, e:
193 raise ActionError, _("The scriptlet returned an error:\n%s" % e)
194
195 except commandTimeoutExpired:
196 raise ActionError, _("The scriptlet ran more than %s seconds and was killed." \
197 % SCRIPTLET_TIMEOUT)
198
199 finally:
200 # Remove the script file.
201 try:
202 os.unlink(script_file)
203 except OSError:
204 log.debug("Could not remove scriptlet file: %s" % script_file)
205
206
207 class ActionScriptPreIn(ActionScript):
208 script_action = "prein"
209
210
211 class ActionScriptPostIn(ActionScript):
212 script_action = "postin"
213
214
215 class ActionScriptPreUn(ActionScript):
216 script_action = "preun"
217
218
219 class ActionScriptPostUn(ActionScript):
220 script_action = "postun"
221
222
223 class ActionScriptPreUp(ActionScript):
224 script_action = "preup"
225
226
227 class ActionScriptPostUp(ActionScript):
228 script_action = "postup"
229
230
231 class ActionScriptPostTrans(ActionScript):
232 pass
233
234
235 class ActionScriptPostTransIn(ActionScriptPostTrans):
236 script_action = "posttransin"
237
238
239 class ActionScriptPostTransUn(ActionScriptPostTrans):
240 script_action = "posttransun"
241
242
243 class ActionScriptPostTransUp(ActionScriptPostTrans):
244 script_action = "posttransup"
245
246
247 class ActionInstall(Action):
248 type = "install"
249
250 def check(self, check):
251 log.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
252
253 # Check if this package can be installed.
254 check.install(self.pkg)
255
256 def run(self):
257 # Add package to the database.
258 self.local.add_package(self.pkg)
259
260 self.pkg.extract(_("Installing"), prefix=self.pakfire.path)
261
262 # Check if shared objects were extracted. If this is the case, we need
263 # to run ldconfig.
264 ldconfig_needed = False
265 for file in self.pkg.filelist:
266 if ".so." in file.name:
267 ldconfig_needed = True
268 break
269
270 if "etc/ld.so.conf" in file.name:
271 ldconfig_needed = True
272 break
273
274 if ldconfig_needed:
275 # Check if ldconfig is present.
276 ldconfig = os.path.join(self.pakfire.path, LDCONFIG[1:])
277
278 if os.path.exists(ldconfig) and os.access(ldconfig, os.X_OK):
279 self.do(LDCONFIG)
280
281 else:
282 log.debug("ldconfig is not present or not executable.")
283
284
285 class ActionUpdate(Action):
286 type = "upgrade"
287
288 def check(self, check):
289 log.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
290
291 # Check if this package can be updated.
292 check.update(self.pkg)
293
294 def run(self):
295 # Add new package to the database.
296 self.local.add_package(self.pkg)
297
298 self.pkg.extract(_("Updating"), prefix=self.pakfire.path)
299
300
301 class ActionRemove(Action):
302 type = "erase"
303
304 def __init__(self, *args, **kwargs):
305 Action.__init__(self, *args, **kwargs)
306
307 # XXX This is ugly, but works for the moment.
308 self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv)
309 assert self.pkg
310
311 def check(self, check):
312 log.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
313
314 # Check if this package can be removed.
315 check.remove(self.pkg)
316
317 def run(self):
318 self.pkg.cleanup(_("Removing"), prefix=self.pakfire.path)
319
320 # Remove package from the database.
321 self.local.rem_package(self.pkg)
322
323
324 class ActionCleanup(Action):
325 type = "ignore"
326
327 def __init__(self, *args, **kwargs):
328 Action.__init__(self, *args, **kwargs)
329
330 # XXX This is ugly, but works for the moment.
331 self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv)
332 assert self.pkg
333
334 def check(self, check):
335 log.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
336
337 # Check if this package can be removed.
338 check.cleanup(self.pkg)
339
340 def run(self):
341 # Cleaning up leftover files and stuff.
342 self.pkg.cleanup(_("Cleanup"), prefix=self.pakfire.path)
343
344 # Remove package from the database.
345 self.local.rem_package(self.pkg)
346
347
348 class ActionReinstall(Action):
349 type = "reinstall"
350
351 def check(self, check):
352 log.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
353
354 # Check if this package can be reinstalled.
355 check.remove(self.pkg)
356 check.install(self.pkg)
357
358 def run(self):
359 # Remove package from the database and add it afterwards.
360 # Sounds weird, but fixes broken entries in the database.
361 self.local.rem_package(self.pkg)
362 self.local.add_package(self.pkg)
363
364 self.pkg.extract(_("Installing"), prefix=self.pakfire.path)
365
366
367 class ActionDowngrade(Action):
368 type = "downgrade"
369
370 def check(self, check):
371 log.debug(_("Running transaction test for %s") % self.pkg.friendly_name)
372
373 # Check if this package can be downgraded.
374 check.install(self.pkg)
375
376 def run(self):
377 # Add new package to database.
378 self.local.add_package(self.pkg)
379
380 self.pkg.extract(_("Downgrading"), prefix=self.pakfire.path)