]> git.ipfire.org Git - pakfire.git/blob - python/pakfire/actions.py
Bump version 0.9.9.
[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 logging
23 import os
24
25 import chroot
26 import packages
27 import util
28
29 from constants import *
30 from i18n import _
31
32 class Action(object):
33 def __init__(self, pakfire, pkg):
34 self.pakfire = pakfire
35 self.pkg_solv = self.pkg = pkg
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
43 self.init()
44
45 def __cmp__(self, other):
46 return cmp(self.pkg, other.pkg)
47
48 def __repr__(self):
49 return "<%s %s>" % (self.__class__.__name__, self.pkg.friendly_name)
50
51 def init(self):
52 # A function to run additional initialization.
53 pass
54
55 @property
56 def needs_download(self):
57 return self.type in ("install", "reinstall", "upgrade", "downgrade",) \
58 and not isinstance(self.pkg, packages.BinaryPackage)
59
60 def download(self, text):
61 if not self.needs_download:
62 return
63
64 self.pkg = self.pkg.download(text)
65
66 def run(self):
67 raise NotImplementedError
68
69 @property
70 def local(self):
71 """
72 Reference to local repository.
73 """
74 return self.pakfire.repos.local
75
76
77 class ActionScript(Action):
78 type = "script"
79 script_action = None
80
81 def init(self):
82 # Load the scriplet.
83 self.scriptlet = self.pkg.get_scriptlet(self.script_action)
84
85 @property
86 def interpreter(self):
87 """
88 Get the interpreter of this scriptlet.
89 """
90 return util.scriptlet_interpreter(self.scriptlet)
91
92 @property
93 def args(self):
94 return []
95
96 def run(self):
97 # Exit immediately, if the scriptlet is empty.
98 if not self.scriptlet:
99 return
100
101 # Actually run the scriplet.
102 logging.debug("Running scriptlet %s" % self)
103
104 # Check if the interpreter does exist and is executable.
105 if self.interpreter:
106 interpreter = "%s/%s" % (self.pakfire.path, self.interpreter)
107 if not os.path.exists(interpreter):
108 raise ActionError, _("Cannot run scriptlet because no interpreter is available: %s" \
109 % self.interpreter)
110
111 if not os.access(interpreter, os.X_OK):
112 raise ActionError, _("Cannot run scriptlet because the interpreter is not executable: %s" \
113 % self.interpreter)
114
115 # Create a name for the temporary script file.
116 script_file_chroot = os.path.join("/", LOCAL_TMP_PATH,
117 "scriptlet_%s" % util.random_string(10))
118 script_file = os.path.join(self.pakfire.path, script_file_chroot[1:])
119 assert script_file.startswith("%s/" % self.pakfire.path)
120
121 # Create script directory, if it does not exist.
122 script_dir = os.path.dirname(script_file)
123 if not os.path.exists(script_dir):
124 os.makedirs(script_dir)
125
126 # Write the scriptlet to a file that we can execute it.
127 try:
128 f = open(script_file, "wb")
129 f.write(self.scriptlet)
130 f.close()
131
132 # The file is only accessable by root.
133 os.chmod(script_file, 700)
134 except:
135 # Remove the file if an error occurs.
136 try:
137 os.unlink(script_file)
138 except OSError:
139 pass
140
141 # XXX catch errors and return a beautiful message to the user
142 raise
143
144 # Generate the script command.
145 command = [script_file_chroot] + self.args
146
147 # If we are running in /, we do not need to chroot there.
148 chroot_path = None
149 if not self.pakfire.path == "/":
150 chroot_path = self.pakfire.path
151
152 try:
153 ret = chroot.do(command, cwd="/tmp",
154 chrootPath=chroot_path,
155 personality=self.pakfire.distro.personality,
156 shell=False,
157 timeout=SCRIPTLET_TIMEOUT,
158 logger=logging.getLogger())
159
160 except Error, e:
161 raise ActionError, _("The scriptlet returned an error:\n%s" % e)
162
163 except commandTimeoutExpired:
164 raise ActionError, _("The scriptlet ran more than %s seconds and was killed." \
165 % SCRIPTLET_TIMEOUT)
166
167 finally:
168 # Remove the script file.
169 try:
170 os.unlink(script_file)
171 except OSError:
172 logging.debug("Could not remove scriptlet file: %s" % script_file)
173
174
175 class ActionScriptPreIn(ActionScript):
176 script_action = "prein"
177
178
179 class ActionScriptPostIn(ActionScript):
180 script_action = "postin"
181
182
183 class ActionScriptPreUn(ActionScript):
184 script_action = "preun"
185
186
187 class ActionScriptPostUn(ActionScript):
188 script_action = "postun"
189
190
191 class ActionScriptPreUp(ActionScript):
192 script_action = "preup"
193
194
195 class ActionScriptPostUp(ActionScript):
196 script_action = "postup"
197
198
199 class ActionScriptPostTrans(ActionScript):
200 pass
201
202
203 class ActionScriptPostTransIn(ActionScriptPostTrans):
204 script_action = "posttransin"
205
206
207 class ActionScriptPostTransUn(ActionScriptPostTrans):
208 script_action = "posttransun"
209
210
211 class ActionScriptPostTransUp(ActionScriptPostTrans):
212 script_action = "posttransup"
213
214
215 class ActionInstall(Action):
216 type = "install"
217
218 def run(self):
219 # Add package to the database.
220 self.local.add_package(self.pkg)
221
222 self.pkg.extract(_("Installing"), prefix=self.pakfire.path)
223
224
225 class ActionUpdate(Action):
226 type = "upgrade"
227
228 def run(self):
229 # Add new package to the database.
230 self.local.add_package(self.pkg)
231
232 self.pkg.extract(_("Updating"), prefix=self.pakfire.path)
233
234
235 class ActionRemove(Action):
236 type = "erase"
237
238 def __init__(self, *args, **kwargs):
239 Action.__init__(self, *args, **kwargs)
240
241 # XXX This is ugly, but works for the moment.
242 self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv)
243 assert self.pkg
244
245 def run(self):
246 self.pkg.cleanup(_("Removing"), prefix=self.pakfire.path)
247
248 # Remove package from the database.
249 self.local.rem_package(self.pkg)
250
251
252 class ActionCleanup(Action):
253 type = "ignore"
254
255 def __init__(self, *args, **kwargs):
256 Action.__init__(self, *args, **kwargs)
257
258 # XXX This is ugly, but works for the moment.
259 self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv)
260 assert self.pkg
261
262 def run(self):
263 # Cleaning up leftover files and stuff.
264 self.pkg.cleanup(_("Cleanup"), prefix=self.pakfire.path)
265
266 # Remove package from the database.
267 self.local.rem_package(self.pkg)
268
269
270 class ActionReinstall(Action):
271 type = "reinstall"
272
273 def run(self):
274 # Remove package from the database and add it afterwards.
275 # Sounds weird, but fixes broken entries in the database.
276 self.local.rem_package(self.pkg)
277 self.local.add_package(self.pkg)
278
279 self.pkg.extract(_("Installing"), prefix=self.pakfire.path)
280
281
282 class ActionDowngrade(Action):
283 type = "downgrade"
284
285 def run(self):
286 # Add new package to database.
287 self.local.add_package(self.pkg)
288
289 self.pkg.extract(_("Downgrading"), prefix=self.pakfire.path)
290
291
292 class ActionChange(Action):
293 type = "change"
294
295 # XXX still need to find out what this should be doing
296
297 def run(self):
298 print "XXX Change: %s" % self.pkg