1
2 '''
3 # DESCRIPTION:
4 # Entropy Object Oriented Interface
5
6 Copyright (C) 2007-2009 Fabio Erculiani
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 '''
22 from __future__ import with_statement
23 import os
24 import sys
25 import shutil
26 import time
27 import subprocess
28 from entropy.i18n import _
29 from entropy.const import *
30 from entropy.exceptions import *
31 from entropy.db import dbapi2, LocalRepository
32 from entropy.output import purple, bold, red, blue, darkgreen, darkred, brown
33
34
36
37 __repo_error_messages_cache = set()
38 __repodb_cache = {}
39 _memory_db_instances = {}
40
42 self.MirrorStatus.clear()
43 self.__repo_error_messages_cache.clear()
44 cl_id = self.sys_settings_client_plugin_id
45 self.SystemSettings[cl_id]['masking_validation']['cache'].clear()
46
47 del self.validRepositories[:]
48 for repoid in self.SystemSettings['repositories']['order']:
49
50 try:
51 dbc = self.open_repository(repoid)
52 dbc.listConfigProtectDirectories()
53 dbc.validateDatabase()
54 self.validRepositories.append(repoid)
55 except RepositoryError:
56 t = _("Repository") + " " + repoid + " " + _("is not available") + ". " + _("Cannot validate")
57 t2 = _("Please update your repositories now in order to remove this message!")
58 self.updateProgress(
59 darkred(t),
60 importance = 1,
61 type = "warning"
62 )
63 self.updateProgress(
64 purple(t2),
65 header = bold("!!! "),
66 importance = 1,
67 type = "warning"
68 )
69 continue
70 except (self.dbapi2.OperationalError,self.dbapi2.DatabaseError,SystemDatabaseError,):
71 t = _("Repository") + " " + repoid + " " + _("is corrupted") + ". " + _("Cannot validate")
72 self.updateProgress(
73 darkred(t),
74 importance = 1,
75 type = "warning"
76 )
77 continue
78
79 self.close_all_repositories(mask_clear = False)
80
82 return (repoid, etpConst['systemroot'],)
83
85 dbc = self.open_memory_database(dbname = repoid)
86 repo_key = self.__get_repository_cache_key(repoid)
87 self._memory_db_instances[repo_key] = dbc
88
89
90 repodata = {
91 'repoid': repoid,
92 'in_memory': True,
93 'description': description,
94 'packages': package_mirrors,
95 'dbpath': ':memory:',
96 }
97 self.add_repository(repodata)
98 return dbc
99
111
116
130
132
133 if isinstance(repoid,basestring):
134 if repoid.endswith(etpConst['packagesext']):
135 xcache = False
136
137 repo_data = self.SystemSettings['repositories']['available']
138 if repoid not in repo_data:
139 t = _("bad repository id specified")
140 if repoid not in self.__repo_error_messages_cache:
141 self.updateProgress(
142 darkred(t),
143 importance = 2,
144 type = "warning"
145 )
146 self.__repo_error_messages_cache.add(repoid)
147 raise RepositoryError("RepositoryError: %s" % (t,))
148
149 if repo_data[repoid].get('in_memory'):
150 repo_key = self.__get_repository_cache_key(repoid)
151 conn = self._memory_db_instances.get(repo_key)
152 else:
153 dbfile = repo_data[repoid]['dbpath']+"/"+etpConst['etpdatabasefile']
154 if not os.path.isfile(dbfile):
155 t = _("Repository %s hasn't been downloaded yet.") % (repoid,)
156 if repoid not in self.__repo_error_messages_cache:
157 self.updateProgress(
158 darkred(t),
159 importance = 2,
160 type = "warning"
161 )
162 self.__repo_error_messages_cache.add(repoid)
163 raise RepositoryError("RepositoryError: %s" % (t,))
164
165 conn = LocalRepository(
166 readOnly = True,
167 dbFile = dbfile,
168 clientDatabase = True,
169 dbname = etpConst['dbnamerepoprefix']+repoid,
170 xcache = xcache,
171 indexing = indexing,
172 OutputInterface = self,
173 ServiceInterface = self
174 )
175
176 if (repoid not in etpConst['client_treeupdatescalled']) and \
177 (self.entropyTools.is_root()) and \
178 (not repoid.endswith(etpConst['packagesext'])):
179
180 updated = False
181 try:
182 updated = conn.clientUpdatePackagesData(self.clientDbconn)
183 except (self.dbapi2.OperationalError, self.dbapi2.DatabaseError):
184 pass
185 if updated:
186 self.clear_dump_cache(etpCache['world_update'])
187 self.clear_dump_cache(etpCache['world'])
188 self.clear_dump_cache(etpCache['install'])
189 self.clear_dump_cache(etpCache['remove'])
190 self.calculate_world_updates(use_cache = False)
191 return conn
192
194 fname = self.SystemSettings['repositories']['available'][reponame]['dbpath']+"/"+etpConst['etpdatabaserevisionfile']
195 revision = -1
196 if os.path.isfile(fname) and os.access(fname,os.R_OK):
197 with open(fname,"r") as f:
198 try:
199 revision = int(f.readline().strip())
200 except (OSError, IOError, ValueError,):
201 pass
202 return revision
203
209
211 fname = self.SystemSettings['repositories']['available'][reponame]['dbpath']+"/"+etpConst['etpdatabasehashfile']
212 mhash = "-1"
213 if os.path.isfile(fname) and os.access(fname,os.R_OK):
214 with open(fname,"r") as f:
215 try:
216 mhash = f.readline().strip().split()[0]
217 except (OSError, IOError, IndexError,):
218 pass
219 return mhash
220
222 product = self.SystemSettings['repositories']['product']
223 branch = self.SystemSettings['repositories']['branch']
224
225 try:
226 self.SystemSettings['repositories']['available'][repodata['repoid']] = {}
227 self.SystemSettings['repositories']['available'][repodata['repoid']]['description'] = repodata['description']
228 except KeyError:
229 t = _("repodata dictionary is corrupted")
230 raise InvalidData("InvalidData: %s" % (t,))
231
232 if repodata['repoid'].endswith(etpConst['packagesext']) or repodata.get('in_memory'):
233 try:
234
235 self.SystemSettings['repositories']['available'][repodata['repoid']]['packages'] = repodata['packages'][:]
236 smart_package = repodata.get('smartpackage')
237 if smart_package != None:
238 self.SystemSettings['repositories']['available'][repodata['repoid']]['smartpackage'] = smart_package
239 except KeyError:
240 raise InvalidData("InvalidData: repodata dictionary is corrupted")
241 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbpath'] = repodata.get('dbpath')
242 self.SystemSettings['repositories']['available'][repodata['repoid']]['pkgpath'] = repodata.get('pkgpath')
243 self.SystemSettings['repositories']['available'][repodata['repoid']]['in_memory'] = repodata.get('in_memory')
244
245 self.SystemSettings['repositories']['order'].insert(0, repodata['repoid'])
246 else:
247
248 self.SystemSettings['repositories']['available'][repodata['repoid']]['plain_packages'] = repodata['plain_packages'][:]
249 self.SystemSettings['repositories']['available'][repodata['repoid']]['packages'] = [x+"/"+product for x in repodata['plain_packages']]
250 self.SystemSettings['repositories']['available'][repodata['repoid']]['plain_database'] = repodata['plain_database']
251 self.SystemSettings['repositories']['available'][repodata['repoid']]['database'] = repodata['plain_database'] + \
252 "/" + product + "/database/" + etpConst['currentarch'] + "/" + branch
253 if not repodata['dbcformat'] in etpConst['etpdatabasesupportedcformats']:
254 repodata['dbcformat'] = etpConst['etpdatabasesupportedcformats'][0]
255 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbcformat'] = repodata['dbcformat']
256 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbpath'] = etpConst['etpdatabaseclientdir'] + \
257 "/" + repodata['repoid'] + "/" + product + "/" + etpConst['currentarch'] + "/" + branch
258
259 myrev = self.get_repository_revision(repodata['repoid'])
260 if myrev == -1:
261 myrev = 0
262 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbrevision'] = str(myrev)
263 if repodata.has_key("position"):
264 self.SystemSettings['repositories']['order'].insert(
265 repodata['position'], repodata['repoid'])
266 else:
267 self.SystemSettings['repositories']['order'].append(
268 repodata['repoid'])
269 if not repodata.has_key("service_port"):
270 repodata['service_port'] = int(etpConst['socket_service']['port'])
271 if not repodata.has_key("ssl_service_port"):
272 repodata['ssl_service_port'] = int(etpConst['socket_service']['ssl_port'])
273 self.SystemSettings['repositories']['available'][repodata['repoid']]['service_port'] = repodata['service_port']
274 self.SystemSettings['repositories']['available'][repodata['repoid']]['ssl_service_port'] = repodata['ssl_service_port']
275 self.repository_move_clear_cache(repodata['repoid'])
276
277 self.entropyTools.save_repository_settings(repodata)
278 self.SystemSettings.clear()
279 self.close_all_repositories()
280 self.validate_repositories()
281
319
330
340
367
369 try:
370 repodata = self.SystemSettings['repositories']['available'][repoid].copy()
371 except KeyError:
372 if not self.SystemSettings['repositories']['excluded'].has_key(repoid):
373 raise
374 repodata = self.SystemSettings['repositories']['excluded'][repoid].copy()
375 return repodata
376
377
379 atoms_contained = []
380 basefile = os.path.basename(tbz2file)
381 cut_idx = -1*(len(etpConst['packagesext']))
382 db_dir = etpConst['entropyunpackdir']+"/"+basefile[:cut_idx]
383 if os.path.isdir(db_dir):
384 shutil.rmtree(db_dir)
385 os.makedirs(db_dir)
386 dbfile = self.entropyTools.extract_edb(tbz2file,
387 dbpath = db_dir+"/packages.db")
388 if dbfile == None:
389 return -1,atoms_contained
390 etpSys['dirstoclean'].add(os.path.dirname(dbfile))
391
392 repodata = {}
393 repodata['repoid'] = basefile
394 repodata['description'] = "Dynamic database from "+basefile
395 repodata['packages'] = []
396 repodata['dbpath'] = os.path.dirname(dbfile)
397 repodata['pkgpath'] = os.path.realpath(tbz2file)
398 repodata['smartpackage'] = False
399
400 mydbconn = self.open_generic_database(dbfile)
401
402 try:
403 myidpackages = mydbconn.listAllIdpackages()
404 except (AttributeError, self.dbapi2.DatabaseError, \
405 self.dbapi2.IntegrityError, self.dbapi2.OperationalError,):
406 return -2,atoms_contained
407 if len(myidpackages) > 1:
408 repodata[basefile]['smartpackage'] = True
409 for myidpackage in myidpackages:
410 compiled_arch = mydbconn.retrieveDownloadURL(myidpackage)
411 if compiled_arch.find("/"+etpSys['arch']+"/") == -1:
412 return -3,atoms_contained
413 atoms_contained.append((int(myidpackage),basefile))
414
415 self.add_repository(repodata)
416 self.validate_repositories()
417 if basefile not in self.validRepositories:
418 self.remove_repository(basefile)
419 return -4,atoms_contained
420 mydbconn.closeDB()
421 del mydbconn
422 return 0,atoms_contained
423
429
431
432 def load_db_from_ram():
433 self.safe_mode = etpConst['safemodeerrors']['clientdb']
434 mytxt = "%s, %s" % (_("System database not found or corrupted"),
435 _("running in safe mode using empty database from RAM"),)
436 self.updateProgress(
437 darkred(mytxt),
438 importance = 1,
439 type = "warning",
440 header = bold("!!!"),
441 )
442 conn = self.open_memory_database(dbname = etpConst['clientdbid'])
443 return conn
444
445 db_dir = os.path.dirname(etpConst['etpdatabaseclientfilepath'])
446 if not os.path.isdir(db_dir): os.makedirs(db_dir)
447
448 db_path = etpConst['etpdatabaseclientfilepath']
449 if (not self.noclientdb) and (not os.path.isfile(db_path)):
450 conn = load_db_from_ram()
451 self.entropyTools.print_traceback(f = self.clientLog)
452 else:
453 try:
454 conn = LocalRepository(readOnly = False, dbFile = db_path,
455 clientDatabase = True, dbname = etpConst['clientdbid'],
456 xcache = self.xcache, indexing = self.indexing,
457 OutputInterface = self, ServiceInterface = self
458 )
459 except (self.dbapi2.DatabaseError,):
460 self.entropyTools.print_traceback(f = self.clientLog)
461 conn = load_db_from_ram()
462 else:
463
464 if not self.noclientdb:
465 try:
466 conn.validateDatabase()
467 except SystemDatabaseError:
468 try:
469 conn.closeDB()
470 except:
471 pass
472 self.entropyTools.print_traceback(f = self.clientLog)
473 conn = load_db_from_ram()
474
475 self.clientDbconn = conn
476 return self.clientDbconn
477
479 self.updateProgress(
480 darkred(_("Sanity Check") + ": " + _("system database")),
481 importance = 2,
482 type = "warning"
483 )
484 idpkgs = self.clientDbconn.listAllIdpackages()
485 length = len(idpkgs)
486 count = 0
487 errors = False
488 scanning_txt = _("Scanning...")
489 for x in idpkgs:
490 count += 1
491 self.updateProgress(
492 darkgreen(scanning_txt),
493 importance = 0,
494 type = "info",
495 back = True,
496 count = (count,length),
497 percent = True
498 )
499 try:
500 self.clientDbconn.getPackageData(x)
501 except Exception ,e:
502 self.entropyTools.print_traceback()
503 errors = True
504 self.updateProgress(
505 darkred(_("Errors on idpackage %s, error: %s")) % (x,str(e)),
506 importance = 0,
507 type = "warning"
508 )
509
510 if not errors:
511 t = _("Sanity Check") + ": %s" % (bold(_("PASSED")),)
512 self.updateProgress(
513 darkred(t),
514 importance = 2,
515 type = "warning"
516 )
517 return 0
518 else:
519 t = _("Sanity Check") + ": %s" % (bold(_("CORRUPTED")),)
520 self.updateProgress(
521 darkred(t),
522 importance = 2,
523 type = "warning"
524 )
525 return -1
526
527 - def open_generic_database(self, dbfile, dbname = None, xcache = None,
528 readOnly = False, indexing_override = None, skipChecks = False):
529 if xcache == None:
530 xcache = self.xcache
531 if indexing_override != None:
532 indexing = indexing_override
533 else:
534 indexing = self.indexing
535 if dbname == None:
536 dbname = etpConst['genericdbid']
537 return LocalRepository(
538 readOnly = readOnly,
539 dbFile = dbfile,
540 clientDatabase = True,
541 dbname = dbname,
542 xcache = xcache,
543 indexing = indexing,
544 OutputInterface = self,
545 skipChecks = skipChecks
546 )
547
549 if dbname == None:
550 dbname = etpConst['genericdbid']
551 dbc = LocalRepository(
552 readOnly = False,
553 dbFile = ':memory:',
554 clientDatabase = True,
555 dbname = dbname,
556 xcache = False,
557 indexing = False,
558 OutputInterface = self,
559 skipChecks = True,
560 ServiceInterface = self
561 )
562 dbc.initializeDatabase()
563 return dbc
564
565 - def backup_database(self, dbpath, backup_dir = None, silent = False, compress_level = 9):
566
567 if compress_level not in range(1,10):
568 compress_level = 9
569
570 backup_dir = os.path.dirname(dbpath)
571 if not backup_dir: backup_dir = os.path.dirname(dbpath)
572 dbname = os.path.basename(dbpath)
573 bytes_required = 1024000*300
574 if not (os.access(backup_dir,os.W_OK) and \
575 os.path.isdir(backup_dir) and os.path.isfile(dbpath) and \
576 os.access(dbpath,os.R_OK) and self.entropyTools.check_required_space(backup_dir, bytes_required)):
577 if not silent:
578 mytxt = "%s: %s, %s" % (darkred(_("Cannot backup selected database")),blue(dbpath),darkred(_("permission denied")),)
579 self.updateProgress(
580 mytxt,
581 importance = 1,
582 type = "error",
583 header = red(" @@ ")
584 )
585 return False, mytxt
586
587 def get_ts():
588 from datetime import datetime
589 ts = datetime.fromtimestamp(time.time())
590 return "%s%s%s_%sh%sm%ss" % (ts.year,ts.month,ts.day,ts.hour,ts.minute,ts.second)
591
592 comp_dbname = "%s%s.%s.bz2" % (etpConst['dbbackupprefix'],dbname,get_ts(),)
593 comp_dbpath = os.path.join(backup_dir,comp_dbname)
594 if not silent:
595 mytxt = "%s: %s ..." % (darkgreen(_("Backing up database to")),blue(comp_dbpath),)
596 self.updateProgress(
597 mytxt,
598 importance = 1,
599 type = "info",
600 header = blue(" @@ "),
601 back = True
602 )
603 import bz2
604 try:
605 self.entropyTools.compress_file(dbpath, comp_dbpath, bz2.BZ2File, compress_level)
606 except:
607 if not silent:
608 self.entropyTools.print_traceback()
609 return False, _("Unable to compress")
610
611 if not silent:
612 mytxt = "%s: %s" % (darkgreen(_("Database backed up successfully")),blue(comp_dbpath),)
613 self.updateProgress(
614 mytxt,
615 importance = 1,
616 type = "info",
617 header = blue(" @@ "),
618 back = True
619 )
620 return True, _("All fine")
621
623
624 bytes_required = 1024000*300
625 db_dir = os.path.dirname(db_destination)
626 if not (os.access(db_dir,os.W_OK) and os.path.isdir(db_dir) and \
627 os.path.isfile(backup_path) and os.access(backup_path,os.R_OK) and \
628 self.entropyTools.check_required_space(db_dir, bytes_required)):
629
630 if not silent:
631 mytxt = "%s: %s, %s" % (darkred(_("Cannot restore selected backup")),
632 blue(backup_path),darkred(_("permission denied")),)
633 self.updateProgress(
634 mytxt,
635 importance = 1,
636 type = "error",
637 header = red(" @@ ")
638 )
639 return False, mytxt
640
641 if not silent:
642 mytxt = "%s: %s => %s ..." % (darkgreen(_("Restoring backed up database")),
643 blue(os.path.basename(backup_path)),blue(db_destination),)
644 self.updateProgress(
645 mytxt,
646 importance = 1,
647 type = "info",
648 header = blue(" @@ "),
649 back = True
650 )
651
652 import bz2
653 try:
654 self.entropyTools.uncompress_file(backup_path, db_destination, bz2.BZ2File)
655 except:
656 if not silent:
657 self.entropyTools.print_traceback()
658 return False, _("Unable to unpack")
659
660 if not silent:
661 mytxt = "%s: %s" % (darkgreen(_("Database restored successfully")),
662 blue(db_destination),)
663 self.updateProgress(
664 mytxt,
665 importance = 1,
666 type = "info",
667 header = blue(" @@ "),
668 back = True
669 )
670 self.purge_cache()
671 return True, _("All fine")
672
674 if not client_dbdir:
675 client_dbdir = os.path.dirname(etpConst['etpdatabaseclientfilepath'])
676 return [os.path.join(client_dbdir,x) for x in os.listdir(client_dbdir) \
677 if x.startswith(etpConst['dbbackupprefix']) and \
678 os.access(os.path.join(client_dbdir,x),os.R_OK)
679 ]
680
682
686
688
689 os.chmod(filepath,0664)
690 if etpConst['entropygid'] != None:
691 os.chown(filepath,-1,etpConst['entropygid'])
692
695
697 if os.path.isfile(etpConst['locks']['using_resources']):
698 os.remove(etpConst['locks']['using_resources'])
699
703
705 if not os.path.isfile(pidfile):
706 return False
707 f = open(pidfile)
708 s_pid = f.readline().strip()
709 f.close()
710 try:
711 s_pid = int(s_pid)
712 except ValueError:
713 return False
714
715 mypid = os.getpid()
716 if (s_pid != mypid) and os.path.isdir("%s/proc/%s" % (etpConst['systemroot'],s_pid,)):
717
718 return True
719 return False
720
722 lockdir = os.path.dirname(pidfile)
723 if not os.path.isdir(lockdir):
724 os.makedirs(lockdir,0775)
725 const_setup_perms(lockdir,etpConst['entropygid'])
726 if mypid == None:
727 mypid = os.getpid()
728 f = open(pidfile,"w")
729 f.write(str(mypid))
730 f.flush()
731 f.close()
732
734
735 etpConst['applicationlock'] = False
736 const_setup_entropy_pid(just_read = True)
737 locked = self.entropyTools.application_lock_check(option = None, gentle = True, silent = True)
738 if locked:
739 if not silent:
740 self.updateProgress(
741 red(_("Another Entropy instance is currently active, cannot satisfy your request.")),
742 importance = 1,
743 type = "error",
744 header = darkred(" @@ ")
745 )
746 return True
747 return False
748
750
751 lock_count = 0
752 max_lock_count = 600
753 sleep_seconds = 0.5
754
755
756 while 1:
757 locked = check_function()
758 if not locked:
759 if lock_count > 0:
760 self.updateProgress(
761 blue(_("Resources unlocked, let's go!")),
762 importance = 1,
763 type = "info",
764 header = darkred(" @@ ")
765 )
766 break
767 if lock_count >= max_lock_count:
768 mycalc = max_lock_count*sleep_seconds/60
769 self.updateProgress(
770 blue(_("Resources still locked after %s minutes, giving up!")) % (mycalc,),
771 importance = 1,
772 type = "warning",
773 header = darkred(" @@ ")
774 )
775 return True
776 lock_count += 1
777 self.updateProgress(
778 blue(_("Resources locked, sleeping %s seconds, check #%s/%s")) % (
779 sleep_seconds,
780 lock_count,
781 max_lock_count,
782 ),
783 importance = 1,
784 type = "warning",
785 header = darkred(" @@ "),
786 back = True
787 )
788 time.sleep(sleep_seconds)
789 return False
790
792 if etpConst.has_key(constant_name):
793 myinst = etpConst[constant_name]
794 if type(etpConst[constant_name]) in (list,tuple):
795 myinst = etpConst[constant_name][:]
796 elif type(etpConst[constant_name]) in (dict,set):
797 myinst = etpConst[constant_name].copy()
798 else:
799 myinst = etpConst[constant_name]
800 etpConst['backed_up'].update({constant_name: myinst})
801 else:
802 t = _("Nothing to backup in etpConst with %s key") % (constant_name,)
803 raise InvalidData("InvalidData: %s" % (t,))
804
807
809 if repositories == None:
810 repositories = self.validRepositories
811 for repoid in repositories:
812 dbconn = self.open_repository(repoid)
813
836
838 viewer = None
839 if os.access("/usr/bin/less",os.X_OK):
840 viewer = "/usr/bin/less"
841 elif os.access("/bin/more",os.X_OK):
842 viewer = "/bin/more"
843 if not viewer:
844 viewer = self.get_file_editor()
845 return viewer
846
848 editor = None
849 if os.getenv("EDITOR"):
850 editor = "$EDITOR"
851 elif os.access("/bin/nano",os.X_OK):
852 editor = "/bin/nano"
853 elif os.access("/bin/vi",os.X_OK):
854 editor = "/bin/vi"
855 elif os.access("/usr/bin/vi",os.X_OK):
856 editor = "/usr/bin/vi"
857 elif os.access("/usr/bin/emacs",os.X_OK):
858 editor = "/usr/bin/emacs"
859 elif os.access("/bin/emacs",os.X_OK):
860 editor = "/bin/emacs"
861 return editor
862
864
865 def _ensure_package_sets_dir():
866 sets_dir = etpConst['confsetsdir']
867 if not os.path.isdir(sets_dir):
868 if os.path.lexists(sets_dir):
869 os.remove(sets_dir)
870 os.makedirs(sets_dir,0775)
871 const_setup_perms(sets_dir, etpConst['entropygid'])
872
873 try:
874 set_name = str(set_name)
875 except (UnicodeEncodeError,UnicodeDecodeError,):
876 raise InvalidPackageSet("InvalidPackageSet: %s %s" % (set_name,_("must be an ASCII string"),))
877
878 if set_name.startswith(etpConst['packagesetprefix']):
879 raise InvalidPackageSet("InvalidPackageSet: %s %s '%s'" % (set_name,_("cannot start with"),etpConst['packagesetprefix'],))
880 set_match, rc = self.package_set_match(set_name)
881 if rc: return -1,_("Name already taken")
882
883 _ensure_package_sets_dir()
884 set_file = os.path.join(etpConst['confsetsdir'],set_name)
885 if os.path.isfile(set_file) and os.access(set_file,os.W_OK):
886 try:
887 os.remove(set_file)
888 except OSError:
889 return -2,_("Cannot remove the old element")
890 if not os.access(os.path.dirname(set_file),os.W_OK):
891 return -3,_("Cannot create the element")
892
893 f = open(set_file,"w")
894 for x in set_atoms: f.write("%s\n" % (x,))
895 f.flush()
896 f.close()
897 self.SystemSettings['system_package_sets'][set_name] = set(set_atoms)
898 return 0,_("All fine")
899
901
902 try:
903 set_name = str(set_name)
904 except (UnicodeEncodeError,UnicodeDecodeError,):
905 raise InvalidPackageSet("InvalidPackageSet: %s %s" % (set_name,_("must be an ASCII string"),))
906
907 if set_name.startswith(etpConst['packagesetprefix']):
908 raise InvalidPackageSet("InvalidPackageSet: %s %s '%s'" % (set_name,_("cannot start with"),etpConst['packagesetprefix'],))
909
910 set_match, rc = self.package_set_match(set_name)
911 if not rc: return -1,_("Already removed")
912 set_id, set_x, set_y = set_match
913
914 if set_id != etpConst['userpackagesetsid']:
915 return -2,_("Not defined by user")
916 set_file = os.path.join(etpConst['confsetsdir'],set_name)
917 if os.path.isfile(set_file) and os.access(set_file,os.W_OK):
918 os.remove(set_file)
919 if set_name in self.SystemSettings['system_package_sets']:
920 del self.SystemSettings['system_package_sets'][set_name]
921 return 0,_("All fine")
922 return -3,_("Set not found or unable to remove")
923
925 client_plugin_id = etpConst['system_settings_plugins_ids']['client_plugin']
926 mask_installed = self.SystemSettings[client_plugin_id]['system_mask']['repos_installed']
927 if idpackage in mask_installed:
928 return True
929 return False
930
932 return db_download_uri.split("/")[2]
933
938
942
944 if not install_queue:
945 return {}
946 licenses = {}
947 for match in install_queue:
948 repoid = match[1]
949 dbconn = self.open_repository(repoid)
950 wl = self.SystemSettings['repos_license_whitelist'].get(repoid)
951 if not wl:
952 continue
953 keys = dbconn.retrieveLicensedataKeys(match[0])
954 for key in keys:
955 if key not in wl:
956 found = self.clientDbconn.isLicenseAccepted(key)
957 if found:
958 continue
959 if not licenses.has_key(key):
960 licenses[key] = set()
961 licenses[key].add(match)
962 return licenses
963
964 - def get_text_license(self, license_name, repoid):
965 dbconn = self.open_repository(repoid)
966 text = dbconn.retrieveLicenseText(license_name)
967 tempfile = self.entropyTools.get_random_temp_file()
968 f = open(tempfile,"w")
969 f.write(text)
970 f.flush()
971 f.close()
972 return tempfile
973
974 - def libraries_test(self, dbconn = None, broken_symbols = False, task_bombing_func = None):
975
976 if dbconn == None:
977 dbconn = self.clientDbconn
978
979 self.updateProgress(
980 blue(_("Libraries test")),
981 importance = 2,
982 type = "info",
983 header = red(" @@ ")
984 )
985
986 if not etpConst['systemroot']:
987 myroot = "/"
988 else:
989 myroot = etpConst['systemroot']+"/"
990
991 subprocess.call("ldconfig -r %s &> /dev/null" % (myroot,), shell = True)
992
993 if not os.path.isfile(etpConst['systemroot']+"/etc/ld.so.conf"):
994 self.updateProgress(
995 blue(_("Cannot find "))+red(etpConst['systemroot']+"/etc/ld.so.conf"),
996 importance = 1,
997 type = "error",
998 header = red(" @@ ")
999 )
1000 return {},set(),-1
1001
1002 ldpaths = set(self.entropyTools.collect_linker_paths())
1003 ldpaths |= self.entropyTools.collect_paths()
1004
1005 if "/usr/lib64" in ldpaths and "/usr/lib" in ldpaths:
1006 if os.path.realpath("/usr/lib64") == "/usr/lib":
1007 ldpaths.discard("/usr/lib")
1008
1009 ldpaths.add("/usr/share")
1010
1011 ldpaths.add("/usr/libexec")
1012
1013 executables = set()
1014 total = len(ldpaths)
1015 count = 0
1016 sys_root_len = len(etpConst['systemroot'])
1017 for ldpath in ldpaths:
1018 if callable(task_bombing_func): task_bombing_func()
1019 count += 1
1020 self.updateProgress(
1021 blue("Tree: ")+red(etpConst['systemroot']+ldpath),
1022 importance = 0,
1023 type = "info",
1024 count = (count,total),
1025 back = True,
1026 percent = True,
1027 header = " "
1028 )
1029 ldpath = ldpath.encode(sys.getfilesystemencoding())
1030 mywalk_iter = os.walk(etpConst['systemroot']+ldpath)
1031
1032 def mywimf(dt):
1033
1034 currentdir, subdirs, files = dt
1035
1036 def mymf(item):
1037 filepath = os.path.join(currentdir,item)
1038 if filepath in etpConst['libtest_files_blacklist']:
1039 return 0
1040 if not os.access(filepath,os.R_OK):
1041 return 0
1042 if not os.path.isfile(filepath):
1043 return 0
1044 if not self.entropyTools.is_elf_file(filepath):
1045 return 0
1046 return filepath[sys_root_len:]
1047
1048 return set([x for x in map(mymf,files) if type(x) != int])
1049
1050 for x in map(mywimf,mywalk_iter): executables |= x
1051
1052 self.updateProgress(
1053 blue(_("Collecting broken executables")),
1054 importance = 2,
1055 type = "info",
1056 header = red(" @@ ")
1057 )
1058 t = red(_("Attention")) + ": " + \
1059 blue(_("don't worry about libraries that are shown here but not later."))
1060 self.updateProgress(
1061 t,
1062 importance = 1,
1063 type = "info",
1064 header = red(" @@ ")
1065 )
1066
1067 myQA = self.QA()
1068
1069 plain_brokenexecs = set()
1070 total = len(executables)
1071 count = 0
1072 scan_txt = blue("%s ..." % (_("Scanning libraries"),))
1073 for executable in executables:
1074 if callable(task_bombing_func): task_bombing_func()
1075 count += 1
1076 if (count%10 == 0) or (count == total) or (count == 1):
1077 self.updateProgress(
1078 scan_txt,
1079 importance = 0,
1080 type = "info",
1081 count = (count,total),
1082 back = True,
1083 percent = True,
1084 header = " "
1085 )
1086 myelfs = self.entropyTools.read_elf_dynamic_libraries(etpConst['systemroot']+executable)
1087 def mymf2(mylib):
1088 return not myQA.resolve_dynamic_library(mylib, executable)
1089 mylibs = set(filter(mymf2,myelfs))
1090 broken_sym_found = set()
1091 if broken_symbols and not mylibs: broken_sym_found |= self.entropyTools.read_elf_broken_symbols(etpConst['systemroot']+executable)
1092 if not (mylibs or broken_sym_found):
1093 continue
1094
1095 if mylibs:
1096 alllibs = blue(' :: ').join(list(mylibs))
1097 self.updateProgress(
1098 red(etpConst['systemroot']+executable)+" [ "+alllibs+" ]",
1099 importance = 1,
1100 type = "info",
1101 percent = True,
1102 count = (count,total),
1103 header = " "
1104 )
1105 elif broken_sym_found:
1106 allsyms = darkred(' :: ').join([brown(x) for x in list(broken_sym_found)])
1107 if len(allsyms) > 50: allsyms = brown(_('various broken symbols'))
1108 self.updateProgress(
1109 red(etpConst['systemroot']+executable)+" { "+allsyms+" }",
1110 importance = 1,
1111 type = "info",
1112 percent = True,
1113 count = (count,total),
1114 header = " "
1115 )
1116 plain_brokenexecs.add(executable)
1117
1118 del executables
1119 packagesMatched = {}
1120
1121 if not etpSys['serverside']:
1122
1123 self.updateProgress(
1124 blue(_("Matching broken libraries/executables")),
1125 importance = 1,
1126 type = "info",
1127 header = red(" @@ ")
1128 )
1129 matched = set()
1130 for brokenlib in plain_brokenexecs:
1131 idpackages = self.clientDbconn.searchBelongs(brokenlib)
1132 for idpackage in idpackages:
1133 key, slot = self.clientDbconn.retrieveKeySlot(idpackage)
1134 mymatch = self.atom_match(key, matchSlot = slot)
1135 if mymatch[0] == -1:
1136 matched.add(brokenlib)
1137 continue
1138 cmpstat = self.get_package_action(mymatch)
1139 if cmpstat == 0:
1140 continue
1141 if not packagesMatched.has_key(brokenlib):
1142 packagesMatched[brokenlib] = set()
1143 packagesMatched[brokenlib].add(mymatch)
1144 matched.add(brokenlib)
1145 plain_brokenexecs -= matched
1146
1147 return packagesMatched,plain_brokenexecs,0
1148
1150 """
1151 Set new Entropy branch. This is NOT thread-safe.
1152 Please note that if you call this method all your
1153 repository instance references will become invalid.
1154 This is caused by close_all_repositories and SystemSettings
1155 clear methods.
1156 Once you changed branch, the repository databases won't be
1157 available until you fetch them (through Repositories class)
1158
1159 @param branch -- new branch
1160 @type branch basestring
1161 @return None
1162 """
1163 self.Cacher.discard()
1164 self.Cacher.stop()
1165 self.purge_cache(showProgress = False)
1166 self.close_all_repositories()
1167
1168
1169 etpConst['branch'] = branch
1170 self.entropyTools.write_new_branch(branch)
1171 self.SystemSettings.clear()
1172
1173
1174 self.reopen_client_repository()
1175 self.clientDbconn.resetTreeupdatesDigests()
1176 self.close_all_repositories()
1177 if self.xcache:
1178 self.Cacher.start()
1179
1181
1182 pkg_data = []
1183 atom_srch = False
1184 if "/" in search_term: atom_srch = True
1185
1186 if not valid_repos: valid_repos = self.validRepositories
1187 if from_installed: valid_repos = [1]
1188 for repo in valid_repos:
1189 if isinstance(repo,basestring):
1190 dbconn = self.open_repository(repo)
1191 elif isinstance(repo,LocalRepository):
1192 dbconn = repo
1193 elif hasattr(self,'clientDbconn'):
1194 dbconn = self.clientDbconn
1195 else:
1196 continue
1197 pkg_data.extend([(x,repo,) for x in dbconn.searchSimilarPackages(search_term, atom = atom_srch)])
1198
1199 return pkg_data
1200
1202 categories = set()
1203 for repo in self.validRepositories:
1204 dbconn = self.open_repository(repo)
1205 catsdata = dbconn.listAllCategories()
1206 categories.update(set([x[1] for x in catsdata]))
1207 return categories
1208
1210 pkg_matches = []
1211 for repo in self.validRepositories:
1212 dbconn = self.open_repository(repo)
1213 branch = self.SystemSettings['repositories']['branch']
1214 catsdata = dbconn.searchPackagesByCategory(category, branch = branch)
1215 pkg_matches.extend([(x[1],repo,) for x in catsdata if (x[1],repo,) not in pkg_matches])
1216 return pkg_matches
1217
1219
1220 data = {}
1221 for repo in self.validRepositories:
1222 try:
1223 dbconn = self.open_repository(repo)
1224 except RepositoryError:
1225 continue
1226 try:
1227 data = dbconn.retrieveCategoryDescription(category)
1228 except (self.dbapi2.OperationalError, self.dbapi2.IntegrityError,):
1229 continue
1230 if data: break
1231
1232 return data
1233
1237
1239
1240 idpackage, repoid = match
1241 dbconn = self.open_repository(repoid)
1242 cl_id = self.sys_settings_client_plugin_id
1243 misc_data = self.SystemSettings[cl_id]['misc']
1244 if mask:
1245 config_protect = set(dbconn.retrieveProtectMask(idpackage).split())
1246 config_protect |= set(misc_data['configprotectmask'])
1247 else:
1248 config_protect = set(dbconn.retrieveProtect(idpackage).split())
1249 config_protect |= set(misc_data['configprotect'])
1250 config_protect = [etpConst['systemroot']+x for x in config_protect]
1251
1252 return sorted(config_protect)
1253
1255
1256 if self.clientDbconn == None:
1257 return []
1258 cl_id = self.sys_settings_client_plugin_id
1259 misc_data = self.SystemSettings[cl_id]['misc']
1260 if mask:
1261 _pmask = self.clientDbconn.retrieveProtectMask(idpackage).split()
1262 config_protect = set(_pmask)
1263 config_protect |= set(misc_data['configprotectmask'])
1264 else:
1265 _protect = self.clientDbconn.retrieveProtect(idpackage).split()
1266 config_protect = set(_protect)
1267 config_protect |= set(misc_data['configprotect'])
1268 config_protect = [etpConst['systemroot']+x for x in config_protect]
1269
1270 return sorted(config_protect)
1271
1273
1274 if self.clientDbconn == None:
1275 return []
1276
1277
1278
1279 cl_id = self.sys_settings_client_plugin_id
1280 misc_data = self.SystemSettings[cl_id]['misc']
1281 if mask:
1282 _pmask = self.clientDbconn.listConfigProtectDirectories(mask = True)
1283 config_protect = set(_pmask)
1284 config_protect |= set(misc_data['configprotectmask'])
1285 else:
1286 _protect = self.clientDbconn.listConfigProtectDirectories()
1287 config_protect = set(_protect)
1288 config_protect |= set(misc_data['configprotect'])
1289 config_protect = [etpConst['systemroot']+x for x in config_protect]
1290
1291 return sorted(config_protect)
1292
1304
1310
1311 - def quickpkg(self, atomstring, savedir = None):
1312 if savedir == None:
1313 savedir = etpConst['packagestmpdir']
1314 if not os.path.isdir(etpConst['packagestmpdir']):
1315 os.makedirs(etpConst['packagestmpdir'])
1316
1317 match = self.clientDbconn.atomMatch(atomstring)
1318 if match[0] == -1:
1319 return -1,None,None
1320 atom = self.clientDbconn.atomMatch(match[0])
1321 pkgdata = self.clientDbconn.getPackageData(match[0])
1322 resultfile = self.quickpkg_handler(pkgdata = pkgdata, dirpath = savedir)
1323 if resultfile == None:
1324 return -1,atom,None
1325 else:
1326 return 0,atom,resultfile
1327
1328 - def quickpkg_handler(self, pkgdata, dirpath, edb = True,
1329 portdbPath = None, fake = False, compression = "bz2", shiftpath = ""):
1330
1331 import stat
1332 import tarfile
1333
1334 if compression not in ("bz2","","gz"):
1335 compression = "bz2"
1336
1337
1338 pkgtag = ''
1339 pkgrev = "~"+str(pkgdata['revision'])
1340 if pkgdata['versiontag']: pkgtag = "#"+pkgdata['versiontag']
1341 pkgname = pkgdata['name']+"-"+pkgdata['version']+pkgrev+pkgtag
1342 pkgcat = pkgdata['category']
1343
1344 dirpath += "/"+pkgname+etpConst['packagesext']
1345 if os.path.isfile(dirpath):
1346 os.remove(dirpath)
1347 tar = tarfile.open(dirpath,"w:"+compression)
1348
1349 if not fake:
1350
1351 contents = sorted([x for x in pkgdata['content']])
1352
1353
1354 for path in contents:
1355
1356 encoded_path = path
1357 path = path.encode('raw_unicode_escape')
1358 path = shiftpath+path
1359 try:
1360 exist = os.lstat(path)
1361 except OSError:
1362 continue
1363 arcname = path[len(shiftpath):]
1364 if arcname.startswith("/"):
1365 arcname = arcname[1:]
1366 ftype = pkgdata['content'][encoded_path]
1367 if str(ftype) == '0': ftype = 'dir'
1368 if 'dir' == ftype and \
1369 not stat.S_ISDIR(exist.st_mode) and \
1370 os.path.isdir(path):
1371 path = os.path.realpath(path)
1372
1373 tarinfo = tar.gettarinfo(path, arcname)
1374
1375 if stat.S_ISREG(exist.st_mode):
1376 tarinfo.type = tarfile.REGTYPE
1377 f = open(path)
1378 try:
1379 tar.addfile(tarinfo, f)
1380 finally:
1381 f.close()
1382 else:
1383 tar.addfile(tarinfo)
1384
1385 tar.close()
1386
1387
1388 import entropy.xpak as xpak
1389 Spm = self.Spm()
1390
1391 gentoo_name = self.entropyTools.remove_tag(pkgname)
1392 gentoo_name = self.entropyTools.remove_entropy_revision(gentoo_name)
1393 if portdbPath == None:
1394 dbdir = Spm.get_vdb_path()+"/"+pkgcat+"/"+gentoo_name+"/"
1395 else:
1396 dbdir = portdbPath+"/"+pkgcat+"/"+gentoo_name+"/"
1397 if os.path.isdir(dbdir):
1398 tbz2 = xpak.tbz2(dirpath)
1399 tbz2.recompose(dbdir)
1400
1401 if edb:
1402 self.inject_entropy_database_into_package(dirpath, pkgdata)
1403
1404 if os.path.isfile(dirpath):
1405 return dirpath
1406 return None
1407
1408
1410
1412 """
1413 @input: matched atom (idpackage,repoid)
1414 @output:
1415 upgrade: int(2)
1416 install: int(1)
1417 reinstall: int(0)
1418 downgrade: int(-1)
1419 """
1420 dbconn = self.open_repository(match[1])
1421 pkgkey, pkgslot = dbconn.retrieveKeySlot(match[0])
1422 results = self.clientDbconn.searchKeySlot(pkgkey, pkgslot)
1423 if not results: return 1
1424
1425 installed_idpackage = results[0][0]
1426 pkgver, pkgtag, pkgrev = dbconn.getVersioningData(match[0])
1427 installedVer, installedTag, installedRev = self.clientDbconn.getVersioningData(installed_idpackage)
1428 pkgcmp = self.entropyTools.entropy_compare_versions((pkgver,pkgtag,pkgrev),(installedVer,installedTag,installedRev))
1429 if pkgcmp == 0:
1430 return 0
1431 elif pkgcmp > 0:
1432 return 2
1433 return -1
1434
1436 idpackage, repoid = match
1437 dbconn = self.open_repository(repoid)
1438 idpackage, idreason = dbconn.idpackageValidator(idpackage)
1439 masked = False
1440 if idpackage == -1: masked = True
1441 return masked, idreason, self.SystemSettings['pkg_masking_reasons'].get(idreason)
1442
1444 m_id, m_repo = match
1445 dbconn = self.open_repository(m_repo)
1446 conflicts = dbconn.retrieveConflicts(m_id)
1447 found_conflicts = set()
1448 for conflict in conflicts:
1449 my_m_id, my_m_rc = self.clientDbconn.atomMatch(conflict)
1450 if my_m_id != -1:
1451
1452 match_data = dbconn.retrieveKeySlot(m_id)
1453 installed_match_data = self.clientDbconn.retrieveKeySlot(my_m_id)
1454 if match_data != installed_match_data:
1455 found_conflicts.add(my_m_id)
1456 return found_conflicts
1457
1459 m_id, m_repo = match
1460 dbconn = self.open_repository(m_repo)
1461 idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check)
1462 if idpackage != -1:
1463 return False
1464 return True
1465
1467
1468 m_id, m_repo = match
1469 if m_repo not in self.validRepositories: return False
1470 dbconn = self.open_repository(m_repo)
1471 idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check)
1472 if idpackage != -1: return False
1473 myr = self.SystemSettings['pkg_masking_reference']
1474 user_masks = [myr['user_package_mask'],myr['user_license_mask'],myr['user_live_mask']]
1475 if idreason in user_masks:
1476 return True
1477 return False
1478
1480
1481 m_id, m_repo = match
1482 if m_repo not in self.validRepositories: return False
1483 dbconn = self.open_repository(m_repo)
1484 idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check)
1485 if idpackage == -1: return False
1486 myr = self.SystemSettings['pkg_masking_reference']
1487 user_masks = [
1488 myr['user_package_unmask'],myr['user_live_unmask'],myr['user_package_keywords'],
1489 myr['user_repo_package_keywords_all'], myr['user_repo_package_keywords']
1490 ]
1491 if idreason in user_masks:
1492 return True
1493 return False
1494
1495 - def mask_match(self, match, method = 'atom', dry_run = False, clean_all_cache = False):
1506
1507 - def unmask_match(self, match, method = 'atom', dry_run = False, clean_all_cache = False):
1518
1519 - def _mask_unmask_match(self, match, method, methods_reference, dry_run = False, clean_all_cache = False):
1545
1551
1557
1563
1569
1574
1579
1581 exist = False
1582 if not os.path.isfile(m_file):
1583 if not os.access(os.path.dirname(m_file),os.W_OK):
1584 return False
1585 elif not os.access(m_file, os.W_OK):
1586 return False
1587 elif not dry_run:
1588 exist = True
1589
1590 if dry_run:
1591 return True
1592
1593 content = []
1594 if exist:
1595 f = open(m_file,"r")
1596 content = [x.strip() for x in f.readlines()]
1597 f.close()
1598 content.append(keyword)
1599 m_file_tmp = m_file+".tmp"
1600 f = open(m_file_tmp,"w")
1601 for line in content:
1602 f.write(line+"\n")
1603 f.flush()
1604 f.close()
1605 shutil.move(m_file_tmp,m_file)
1606 return True
1607
1609 setting_data = self.SystemSettings.get_setting_files_data()
1610 masking_list = [setting_data['mask'],setting_data['unmask']]
1611 return self._clear_match_generic(match, masking_list = masking_list, dry_run = dry_run)
1612
1614
1615 self.SystemSettings['live_packagemasking']['unmask_matches'].discard(match)
1616 self.SystemSettings['live_packagemasking']['mask_matches'].discard(match)
1617
1618 if dry_run: return
1619
1620 for mask_file in masking_list:
1621 if not (os.path.isfile(mask_file) and os.access(mask_file,os.W_OK)): continue
1622 f = open(mask_file,"r")
1623 newf = self.entropyTools.open_buffer()
1624 line = f.readline()
1625 while line:
1626 line = line.strip()
1627 if line.startswith("#"):
1628 newf.write(line+"\n")
1629 line = f.readline()
1630 continue
1631 elif not line:
1632 newf.write("\n")
1633 line = f.readline()
1634 continue
1635 mymatch = self.atom_match(line, packagesFilter = False)
1636 if mymatch == match:
1637 line = f.readline()
1638 continue
1639 newf.write(line+"\n")
1640 line = f.readline()
1641 f.close()
1642 tmpfile = mask_file+".w_tmp"
1643 f = open(tmpfile,"w")
1644 f.write(newf.getvalue())
1645 f.flush()
1646 f.close()
1647 newf.close()
1648 shutil.move(tmpfile,mask_file)
1649