1
2 """
3
4 @author: Fabio Erculiani <lxnay@sabayonlinux.org>
5 @contact: lxnay@sabayonlinux.org
6 @copyright: Fabio Erculiani
7 @license: GPL-2
8
9 B{Entropy Package Manager Client Package installation triggers Interface}.
10
11 """
12
13 import subprocess
14 import shutil
15 from entropy.client.interfaces.client import Client
16 from entropy.const import *
17 from entropy.exceptions import *
18 from entropy.output import *
19 from entropy.i18n import _
20 import entropy.tools
21
23
24 VALID_PHASES = ("preinstall", "postinstall", "preremove", "postremove",)
25
26 import entropy.tools as entropyTools
27 - def __init__(self, entropy_client, phase, pkgdata, package_action = None):
28
29 if not isinstance(entropy_client, Client):
30 mytxt = "A valid Entropy Instance is needed"
31 raise AttributeError(mytxt)
32
33 self.Entropy = entropy_client
34 self.pkgdata = pkgdata
35 self.prepared = False
36 self.triggers = []
37 self._trigger_data = {}
38 self.package_action = package_action
39
40 self.spm_support = True
41 try:
42 Spm = self.Entropy.Spm()
43 self.Spm = Spm
44 except Exception as e:
45 self.entropyTools.print_traceback()
46 mytxt = darkred("%s, %s: %s, %s !") % (
47 _("Source Package Manager interface can't be loaded"),
48 _("Error"),
49 e,
50 _("please fix"),
51 )
52 self.Entropy.updateProgress(
53 mytxt,
54 importance = 0,
55 header = bold(" !!! ")
56 )
57 self.spm_support = False
58
59 self.phase = phase
60
61 if self.phase not in Trigger.VALID_PHASES:
62 mytxt = "Valid phases: %s" % (Trigger.VALID_PHASES,)
63 raise AttributeError(mytxt)
64
66 func = getattr(self, self.phase)
67 self.triggers = func()
68 self.prepared = True
69 return len(self.triggers) > 0
70
72 for trigger_func in self.triggers:
73 trigger_func()
74
76 self.prepared = False
77 self._trigger_data.clear()
78 del self.triggers[:]
79
80 - def postinstall(self):
81
82 functions = []
83
84 if self.spm_support:
85 spm_class = self.Entropy.Spm_class()
86 phases_map = spm_class.package_phases_map()
87 while True:
88 if self.pkgdata['spm_phases'] is not None:
89 if phases_map.get('postinstall') not \
90 in self.pkgdata['spm_phases']:
91 break
92 functions.append(self.trigger_spm_postinstall)
93 break
94
95 cont_dirs = set((os.path.dirname(x) for x in self.pkgdata['content']))
96 ldpaths = entropy.tools.collect_linker_paths()
97 if len(cont_dirs) != len(cont_dirs - set(ldpaths)):
98 functions.append(self.trigger_env_update)
99
100 if self.pkgdata['trigger']:
101 functions.append(self.trigger_call_ext_postinstall)
102
103 return functions
104
106
107 functions = []
108
109
110 if self.spm_support:
111 spm_class = self.Entropy.Spm_class()
112 phases_map = spm_class.package_phases_map()
113 while True:
114 if self.pkgdata['spm_phases'] != None:
115 if phases_map.get('preinstall') not \
116 in self.pkgdata['spm_phases']:
117 break
118 functions.append(self.trigger_spm_preinstall)
119 break
120
121 if self.pkgdata['trigger']:
122 functions.append(self.trigger_call_ext_preinstall)
123
124 return functions
125
126 - def postremove(self):
127
128 functions = []
129
130 cont_dirs = set((os.path.dirname(x) for x in \
131 self.pkgdata['removecontent']))
132
133 ldpaths = entropy.tools.collect_linker_paths()
134 if len(cont_dirs) != len(cont_dirs - set(ldpaths)):
135 functions.append(self.trigger_env_update)
136
137 if self.pkgdata['trigger']:
138 functions.append(self.trigger_call_ext_postremove)
139
140 return functions
141
142
144
145 functions = []
146
147
148 if self.spm_support:
149 spm_class = self.Entropy.Spm_class()
150 phases_map = spm_class.package_phases_map()
151
152 while True:
153 if self.pkgdata['spm_phases'] != None:
154 if phases_map.get('preremove') not \
155 in self.pkgdata['spm_phases']:
156 break
157 functions.append(self.trigger_spm_preremove)
158 break
159
160
161
162 while True:
163 if self.pkgdata['spm_phases'] != None:
164 if phases_map.get('postremove') not \
165 in self.pkgdata['spm_phases']:
166 break
167 functions.append(self.trigger_spm_postremove)
168 break
169
170 if self.pkgdata['trigger']:
171 functions.append(self.trigger_call_ext_preremove)
172
173 return functions
174
175
178
181
184
187
189 try:
190 return self.do_trigger_call_ext_generic()
191 except Exception as e:
192 mykey = self.pkgdata['category']+"/"+self.pkgdata['name']
193 tb = self.entropyTools.get_traceback()
194 self.Entropy.updateProgress(tb, importance = 0, type = "error")
195 self.Entropy.clientLog.write(tb)
196 self.Entropy.clientLog.log(
197 ETP_LOGPRI_INFO,
198 ETP_LOGLEVEL_NORMAL,
199 "[POST] ATTENTION Cannot run External trigger for " + \
200 mykey + "!! " + str(Exception) + ": " + str(e)
201 )
202 mytxt = "%s: %s %s. %s." % (
203 bold(_("QA")),
204 brown(_("Cannot run External trigger for")),
205 bold(mykey),
206 brown(_("Please report it")),
207 )
208 self.Entropy.updateProgress(
209 mytxt,
210 importance = 0,
211 header = red(" ## ")
212 )
213 return 0
214
216
218 self.Entropy = Entropy
219 import entropy.tools as entropyTools
220 self.entropyTools = entropyTools
221
223
224
225 category = pkgdata.get('category')
226 if const_isunicode(category):
227 category = category.encode('utf-8')
228
229 pn = pkgdata.get('name')
230 if const_isunicode(pn):
231 pn = pn.encode('utf-8')
232
233 pv = pkgdata.get('version')
234 if const_isunicode(pv):
235 pv = pv.encode('utf-8')
236
237 pr = self.entropyTools.dep_get_spm_revision(pv)
238 pvr = pv
239 if pr == "r0": pvr += "-%s" % (pr,)
240
241 pet = pkgdata.get('versiontag')
242 if const_isunicode(pet):
243 pet = pet.encode('utf-8')
244
245 per = pkgdata.get('revision')
246 if const_isunicode(per):
247 per = per.encode('utf-8')
248
249 etp_branch = pkgdata.get('branch')
250 if const_isunicode(etp_branch):
251 etp_branch = etp_branch.encode('utf-8')
252
253 slot = pkgdata.get('slot')
254 if const_isunicode(slot):
255 slot = slot.encode('utf-8')
256
257 pkgatom = pkgdata.get('atom')
258 pkgkey = self.entropyTools.dep_getkey(pkgatom)
259 pvrte = pkgatom[len(pkgkey)+1:]
260 if const_isunicode(pvrte):
261 pvrte = pvrte.encode('utf-8')
262
263 etpapi = pkgdata.get('etpapi')
264 if const_isunicode(etpapi):
265 etpapi = etpapi.encode('utf-8')
266
267 p = pkgatom
268 if const_isunicode(p):
269 p = p.encode('utf-8')
270
271 chost, cflags, cxxflags = pkgdata.get('chost'), \
272 pkgdata.get('cflags'), pkgdata.get('cxxflags')
273
274 chost = pkgdata.get('etpapi')
275 if const_isunicode(chost):
276 chost = chost.encode('utf-8')
277
278 cflags = pkgdata.get('etpapi')
279 if const_isunicode(cflags):
280 cflags = cflags.encode('utf-8')
281
282 cxxflags = pkgdata.get('etpapi')
283 if const_isunicode(cxxflags):
284 cxxflags = cxxflags.encode('utf-8')
285
286
287
288 eclasses = ' '.join(pkgdata.get('eclasses', []))
289 if const_isunicode(eclasses):
290 eclasses = eclasses.encode('utf-8')
291
292 unpackdir = pkgdata.get('unpackdir', '')
293 if const_isunicode(unpackdir):
294 unpackdir = unpackdir.encode('utf-8')
295
296 imagedir = pkgdata.get('imagedir', '')
297 if const_isunicode(imagedir):
298 imagedir = imagedir.encode('utf-8')
299
300 sb_dirs = [unpackdir, imagedir]
301 sb_write = ':'.join(sb_dirs)
302
303 myenv = {
304 "ETP_API": etpSys['api'],
305 "ETP_LOG": self.Entropy.clientLog.get_fpath(),
306 "ETP_STAGE": stage,
307 "ETP_PHASE": self.__get_sh_stage(),
308 "ETP_BRANCH": etp_branch,
309 "CATEGORY": category,
310 "PN": pn,
311 "PV": pv,
312 "PR": pr,
313 "PVR": pvr,
314 "PVRTE": pvrte,
315 "PER": per,
316 "PET": pet,
317 "SLOT": slot,
318 "PAPI": etpapi,
319 "P": p,
320 "WORKDIR": unpackdir,
321 "B": unpackdir,
322 "D": imagedir,
323 "ENTROPY_TMPDIR": etpConst['packagestmpdir'],
324 "CFLAGS": cflags,
325 "CXXFLAGS": cxxflags,
326 "CHOST": chost,
327 "PORTAGE_ECLASSES": eclasses,
328 "ROOT": etpConst['systemroot'],
329 "SANDBOX_WRITE": sb_write,
330 }
331 sysenv = os.environ.copy()
332 sysenv.update(myenv)
333 return sysenv
334
336 mydict = {
337 "preinstall": "pkg_preinst",
338 "postinstall": "pkg_postinst",
339 "preremove": "pkg_prerm",
340 "postremove": "pkg_postrm",
341 }
342 return mydict.get(stage)
343
344 - def run(self, stage, pkgdata, trigger_file):
345 env = self.__env_setup(stage, pkgdata)
346 p = subprocess.Popen([trigger_file, stage],
347 stdout = sys.stdout, stderr = sys.stderr,
348 env = env
349 )
350 rc = p.wait()
351 if os.path.isfile(trigger_file):
352 os.remove(trigger_file)
353 return rc
354
356
358 self.Entropy = Entropy
359
360 - def run(self, stage, pkgdata, trigger_file):
361 my_ext_status = 1
362 if os.path.isfile(trigger_file):
363 with open(trigger_file) as trig_f:
364 exec(compile(trig_f.read(), trigger_file, 'exec'))
365 if os.path.isfile(trigger_file):
366 os.remove(trigger_file)
367 return my_ext_status
368
370
371
372 if etpUi['mute']:
373 oldsystderr = sys.stderr
374 oldsysstdout = sys.stdout
375 stdfile = open("/dev/null", "w")
376 sys.stdout = stdfile
377 sys.stderr = stdfile
378
379 tg_pfx = "%s/trigger-" % (etpConst['entropyunpackdir'],)
380 while True:
381 triggerfile = "%s%s" % (tg_pfx, entropy.tools.get_random_number(),)
382 if not os.path.isfile(triggerfile): break
383
384 triggerdir = os.path.dirname(triggerfile)
385 if not os.path.isdir(triggerdir):
386 os.makedirs(triggerdir)
387
388 f = open(triggerfile, "w")
389 chunk = 1024
390 start = 0
391 while True:
392 buf = self.pkgdata['trigger'][start:]
393 if not buf: break
394 f.write(buf)
395 start += chunk
396 f.flush()
397 f.close()
398
399
400 if etpUi['mute']:
401 sys.stderr = oldsystderr
402 sys.stdout = oldsysstdout
403 stdfile.close()
404
405 f = open(triggerfile, "r")
406 interpreter = f.readline().strip()
407 f.close()
408 entropy_sh = etpConst['trigger_sh_interpreter']
409 if interpreter == "#!%s" % (entropy_sh,):
410 os.chmod(triggerfile, 0o775)
411 my = self.EntropyShSandbox(self.Entropy)
412 else:
413 my = self.EntropyPySandbox(self.Entropy)
414 return my.run(self.phase, self.pkgdata, triggerfile)
415
424
426
427 self.Entropy.updateProgress(
428 "SPM: %s" % (brown(_("post-install phase")),),
429 importance = 0,
430 header = red(" ## ")
431 )
432 return self.Spm.execute_package_phase(self.pkgdata, 'postinstall')
433
442
451
453
454 self.Entropy.updateProgress(
455 "SPM: %s" % (brown(_("post-remove phase")),),
456 importance = 0,
457 header = red(" ## ")
458 )
459 return self.Spm.execute_package_phase(self.pkgdata, 'postremove')
460