Package entropy :: Package client :: Package interfaces :: Module package

Source Code for Module entropy.client.interfaces.package

   1  # -*- coding: utf-8 -*- 
   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 Interface}. 
  10   
  11  """ 
  12  from __future__ import with_statement 
  13  import os 
  14  import errno 
  15  import stat 
  16  import shutil 
  17  from entropy.const import etpConst, etpSys, etpCache, const_setup_perms, \ 
  18      ETP_LOGPRI_INFO, ETP_LOGLEVEL_NORMAL, ETP_LOGLEVEL_VERBOSE 
  19  from entropy.exceptions import PermissionDenied, InvalidData, IncorrectParameter 
  20  from entropy.i18n import _ 
  21  from entropy.output import TextInterface, brown, blue, bold, darkgreen, \ 
  22      darkblue, red, purple, darkred, print_info, print_error, print_warning 
  23  from entropy.misc import TimeScheduled 
  24  from entropy.db import dbapi2, EntropyRepository 
  25  from entropy.client.interfaces.client import Client 
  26  from entropy.cache import EntropyCacher 
  27  import entropy.tools 
  28   
29 -class Package:
30
31 - def __init__(self, EquoInstance):
32 33 if not isinstance(EquoInstance, Client): 34 mytxt = _("A valid Client instance or subclass is needed") 35 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,)) 36 self.Entropy = EquoInstance 37 38 self.Cacher = EntropyCacher() 39 self.pkgmeta = {} 40 self.__prepared = False 41 self.matched_atom = () 42 self.valid_actions = ("source", "fetch", "multi_fetch", "remove", 43 "remove_conflict", "install", "config" 44 ) 45 self.action = None 46 self.fetch_abort_function = None 47 self.xterm_title = ''
48
49 - def kill(self):
50 self.pkgmeta.clear() 51 52 self.matched_atom = () 53 self.valid_actions = () 54 self.action = None 55 self.__prepared = False 56 self.fetch_abort_function = None
57
58 - def error_on_prepared(self):
59 if self.__prepared: 60 mytxt = _("Already prepared") 61 raise PermissionDenied("PermissionDenied: %s" % (mytxt,))
62
63 - def error_on_not_prepared(self):
64 if not self.__prepared: 65 mytxt = _("Not yet prepared") 66 raise PermissionDenied("PermissionDenied: %s" % (mytxt,))
67
68 - def check_action_validity(self, action):
69 if action not in self.valid_actions: 70 mytxt = _("Action must be in") 71 raise InvalidData("InvalidData: %s %s" % (mytxt, 72 self.valid_actions,) 73 )
74
75 - def match_checksum(self, repository = None, checksum = None, 76 download = None, signatures = None):
77 78 self.error_on_not_prepared() 79 80 if repository is None: 81 repository = self.pkgmeta['repository'] 82 if checksum is None: 83 checksum = self.pkgmeta['checksum'] 84 if download is None: 85 download = self.pkgmeta['download'] 86 if signatures is None: 87 signatures = self.pkgmeta['signatures'] 88 89 def do_signatures_validation(signatures): 90 # check signatures, if available 91 if isinstance(signatures, dict): 92 for hash_type in sorted(signatures): 93 hash_val = signatures[hash_type] 94 # XXX workaround bug on unreleased 95 # entropy versions 96 if hash_val in signatures: 97 continue 98 elif hash_val is None: 99 continue 100 elif not hasattr(entropy.tools, 'compare_%s' % (hash_type,)): 101 continue 102 103 self.Entropy.updateProgress( 104 "%s: %s" % (blue(_("Checking package hash")), 105 purple(hash_type.upper()),), 106 importance = 0, 107 type = "info", 108 header = red(" ## "), 109 back = True 110 ) 111 cmp_func = getattr(entropy.tools, 112 'compare_%s' % (hash_type,)) 113 mydownload = os.path.join(etpConst['entropyworkdir'], 114 download) 115 valid = cmp_func(mydownload, hash_val) 116 if not valid: 117 self.Entropy.updateProgress( 118 "%s: %s %s" % ( 119 darkred(_("Package hash")), 120 purple(hash_type.upper()), 121 darkred(_("does not match the recorded one")), 122 ), 123 importance = 0, 124 type = "warning", 125 header = darkred(" ## ") 126 ) 127 return 1 128 self.Entropy.updateProgress( 129 "%s %s" % ( 130 purple(hash_type.upper()), 131 darkgreen(_("matches")), 132 ), 133 importance = 0, 134 type = "info", 135 header = " : " 136 ) 137 return 0
138 139 dlcount = 0 140 match = False 141 while dlcount <= 5: 142 143 self.Entropy.updateProgress( 144 blue(_("Checking package checksum...")), 145 importance = 0, 146 type = "info", 147 header = red(" ## "), 148 back = True 149 ) 150 151 dlcheck = self.Entropy.check_needed_package_download(download, 152 checksum = checksum) 153 if dlcheck == 0: 154 basef = os.path.basename(download) 155 self.Entropy.updateProgress( 156 "%s: %s" % ( 157 blue(_("Package checksum matches")), 158 darkgreen(basef), 159 ), 160 importance = 0, 161 type = "info", 162 header = red(" ## ") 163 ) 164 165 dlcheck = do_signatures_validation(signatures) 166 if dlcheck == 0: 167 self.pkgmeta['verified'] = True 168 match = True 169 break # file downloaded successfully 170 171 if dlcheck != 0: 172 dlcount += 1 173 mytxt = _("Checksum does not match. Download attempt #%s") % ( 174 dlcount, 175 ) 176 self.Entropy.updateProgress( 177 darkred(mytxt), 178 importance = 0, 179 type = "warning", 180 header = darkred(" ## ") 181 ) 182 myrelative_uri = \ 183 self.Entropy.get_branch_from_download_relative_uri(download) 184 fetch = self.Entropy.fetch_file_on_mirrors( 185 repository, 186 myrelative_uri, 187 download, 188 checksum, 189 fetch_abort_function = self.fetch_abort_function 190 ) 191 if fetch != 0: 192 self.Entropy.updateProgress( 193 blue(_("Cannot properly fetch package! Quitting.")), 194 importance = 0, 195 type = "error", 196 header = darkred(" ## ") 197 ) 198 return fetch 199 200 # package is fetched, let's loop one more time 201 # to make sure to run all the checksum checks 202 continue 203 204 if not match: 205 mytxt = _("Cannot fetch package or checksum does not match") 206 mytxt2 = _("Try to download latest repositories") 207 for txt in (mytxt, mytxt2,): 208 self.Entropy.updateProgress( 209 "%s." % (blue(txt),), 210 importance = 0, 211 type = "info", 212 header = red(" ## ") 213 ) 214 return 1 215 216 return 0
217
218 - def multi_match_checksum(self):
219 rc = 0 220 for repository, branch, download, digest, signatures in \ 221 self.pkgmeta['multi_checksum_list']: 222 223 rc = self.match_checksum(repository, digest, download, signatures) 224 if rc != 0: 225 break 226 227 return rc
228
229 - def __unpack_package(self):
230 231 if not self.pkgmeta['merge_from']: 232 self.Entropy.clientLog.log( 233 ETP_LOGPRI_INFO, 234 ETP_LOGLEVEL_NORMAL, 235 "Unpacking package: %s" % (self.pkgmeta['atom'],) 236 ) 237 else: 238 self.Entropy.clientLog.log( 239 ETP_LOGPRI_INFO, 240 ETP_LOGLEVEL_NORMAL, 241 "Merging package: %s" % (self.pkgmeta['atom'],) 242 ) 243 244 unpack_dir = self.pkgmeta['unpackdir'] 245 unpack_dir_raw = self.pkgmeta['unpackdir'].encode('raw_unicode_escape') 246 247 if os.path.isdir(unpack_dir): 248 shutil.rmtree(unpack_dir_raw) 249 elif os.path.isfile(unpack_dir): 250 os.remove(unpack_dir_raw) 251 os.makedirs(self.pkgmeta['imagedir']) 252 253 if not os.path.isfile(self.pkgmeta['pkgpath']) and \ 254 not self.pkgmeta['merge_from']: 255 256 if os.path.isdir(self.pkgmeta['pkgpath']): 257 shutil.rmtree(self.pkgmeta['pkgpath']) 258 if os.path.islink(self.pkgmeta['pkgpath']): 259 os.remove(self.pkgmeta['pkgpath']) 260 self.pkgmeta['verified'] = False 261 rc = self.fetch_step() 262 if rc != 0: 263 return rc 264 265 if not self.pkgmeta['merge_from']: 266 267 # extract entropy database from package file 268 # in order to avoid having to read content data 269 # from the repository database, which, in future 270 # is allowed to not provide such info. 271 pkg_dbdir = os.path.dirname(self.pkgmeta['pkgdbpath']) 272 if not os.path.isdir(pkg_dbdir): 273 os.makedirs(pkg_dbdir, 0755) 274 275 # extract edb 276 entropy.tools.extract_edb(self.pkgmeta['pkgpath'], 277 self.pkgmeta['pkgdbpath']) 278 279 unpack_tries = 3 280 while 1: 281 unpack_tries -= 1 282 try: 283 rc = entropy.tools.spawn_function( 284 entropy.tools.uncompress_tar_bz2, 285 self.pkgmeta['pkgpath'], 286 self.pkgmeta['imagedir'], 287 catchEmpty = True 288 ) 289 except EOFError: 290 self.Entropy.clientLog.log( 291 ETP_LOGPRI_INFO, ETP_LOGLEVEL_NORMAL, 292 "EOFError on " + self.pkgmeta['pkgpath'] 293 ) 294 rc = 1 295 except: 296 # this will make devs to actually catch the 297 # right exception and prepare a fix 298 self.Entropy.clientLog.log( 299 ETP_LOGPRI_INFO, 300 ETP_LOGLEVEL_NORMAL, 301 "Raising Unicode/Pickling Error for " + \ 302 self.pkgmeta['pkgpath'] 303 ) 304 rc = entropy.tools.uncompress_tar_bz2( 305 self.pkgmeta['pkgpath'], 306 self.pkgmeta['imagedir'], 307 catchEmpty = True 308 ) 309 if rc == 0: 310 break 311 312 if unpack_tries <= 0: 313 return rc 314 # otherwise, try to download it again 315 self.pkgmeta['verified'] = False 316 f_rc = self.fetch_step() 317 if f_rc != 0: 318 return f_rc 319 320 else: 321 322 pid = os.fork() 323 if pid > 0: 324 os.waitpid(pid, 0) 325 else: 326 self.__fill_image_dir(self.pkgmeta['merge_from'], 327 self.pkgmeta['imagedir']) 328 os._exit(0) 329 330 # FIXME: add new entropy.spm plugin method 331 332 # unpack xpak ? 333 if os.path.isdir(self.pkgmeta['xpakpath']): 334 shutil.rmtree(self.pkgmeta['xpakpath'], True) 335 336 # create data dir where we'll unpack the xpak 337 xpak_dir = self.pkgmeta['xpakpath'] + os.path.sep + \ 338 etpConst['entropyxpakdatarelativepath'] 339 340 os.makedirs(xpak_dir, 0755) 341 342 xpak_path = self.pkgmeta['xpakpath'] + os.path.sep + \ 343 etpConst['entropyxpakfilename'] 344 345 if not self.pkgmeta['merge_from']: 346 347 if self.pkgmeta['smartpackage']: 348 349 # we need to get the .xpak from database 350 xdbconn = self.Entropy.open_repository( 351 self.pkgmeta['repository']) 352 xpakdata = xdbconn.retrieveXpakMetadata( 353 self.pkgmeta['idpackage']) 354 if xpakdata: 355 # save into a file 356 f = open(xpak_path, "wb") 357 f.write(xpakdata) 358 f.flush() 359 f.close() 360 self.pkgmeta['xpakstatus'] = entropy.tools.unpack_xpak( 361 xpak_path, 362 xpak_dir 363 ) 364 else: 365 self.pkgmeta['xpakstatus'] = None 366 del xpakdata 367 368 else: 369 self.pkgmeta['xpakstatus'] = entropy.tools.extract_xpak( 370 self.pkgmeta['pkgpath'], 371 xpak_dir 372 ) 373 374 else: # merge_from 375 376 tolink_dir = xpak_dir 377 if os.path.isdir(tolink_dir): 378 shutil.rmtree(tolink_dir, True) 379 # now link 380 os.symlink(self.pkgmeta['xpakdir'], tolink_dir) 381 382 # create fake portage ${D} linking it to imagedir 383 portage_cpv = self.pkgmeta['category'] + "/" + \ 384 self.pkgmeta['name'] + "-" + self.pkgmeta['version'] 385 386 portage_db_fakedir = os.path.join( 387 self.pkgmeta['unpackdir'], 388 "portage/" + portage_cpv 389 ) 390 391 os.makedirs(portage_db_fakedir, 0755) 392 # now link it to self.pkgmeta['imagedir'] 393 os.symlink(self.pkgmeta['imagedir'], 394 os.path.join(portage_db_fakedir, "image")) 395 396 return 0
397
398 - def __configure_package(self):
399 400 try: 401 Spm = self.Entropy.Spm() 402 except Exception, err: 403 self.Entropy.clientLog.log( 404 ETP_LOGPRI_INFO, 405 ETP_LOGLEVEL_NORMAL, 406 "Source Package Manager not available: %s | %s" % ( 407 type(Exception), err, 408 ) 409 ) 410 return 1 411 412 self.Entropy.updateProgress( 413 "SPM: %s" % (brown(_("configuration phase")),), 414 importance = 0, 415 header = red(" ## ") 416 ) 417 return Spm.configure_installed_package(self.pkgmeta)
418 419
420 - def __remove_package(self):
421 422 self.__clear_cache() 423 424 self.Entropy.clientLog.log(ETP_LOGPRI_INFO, ETP_LOGLEVEL_NORMAL, 425 "Removing package: %s" % (self.pkgmeta['removeatom'],)) 426 427 mytxt = "%s: %s" % ( 428 blue(_("Removing from Entropy")), 429 red(self.pkgmeta['removeatom']), 430 ) 431 self.Entropy.updateProgress( 432 mytxt, 433 importance = 1, 434 type = "info", 435 header = red(" ## ") 436 ) 437 automerge_metadata = \ 438 self.Entropy.clientDbconn.retrieveAutomergefiles( 439 self.pkgmeta['removeidpackage'], get_dict = True 440 ) 441 self.remove_installed_package(self.pkgmeta['removeidpackage']) 442 443 spm_rc = self.spm_remove_package() 444 if spm_rc != 0: 445 return spm_rc 446 447 self.remove_content_from_system(self.pkgmeta['removeidpackage'], 448 automerge_metadata) 449 450 return 0
451
452 - def remove_installed_package(self, idpackage):
453 """ 454 Remove installed package from Entropy installed packages repository. 455 456 @param idpackage: Entropy Repository package identifier 457 @type idpackage: int 458 """ 459 self.Entropy.clientDbconn.removePackage(idpackage, do_commit = False, 460 do_cleanup = False)
461
462 - def remove_content_from_system(self, idpackage, automerge_metadata = None):
463 """ 464 Remove installed package content (files/directories) from live system. 465 466 @param idpackage: Entropy Repository package identifier 467 @type idpackage: int 468 @keyword automerge_metadata: Entropy "automerge metadata" 469 @type automerge_metadata: dict 470 """ 471 if automerge_metadata is None: 472 automerge_metadata = {} 473 474 sys_root = etpConst['systemroot'] 475 # load CONFIG_PROTECT and CONFIG_PROTECT_MASK 476 sys_settings = self.Entropy.SystemSettings 477 protect = self.Entropy.get_installed_package_config_protect(idpackage) 478 mask = self.Entropy.get_installed_package_config_protect(idpackage, 479 mask = True) 480 481 sys_set_plg_id = \ 482 etpConst['system_settings_plugins_ids']['client_plugin'] 483 col_protect = sys_settings[sys_set_plg_id]['misc']['collisionprotect'] 484 485 # remove files from system 486 directories = set() 487 directories_cache = set() 488 not_removed_due_to_collisions = set() 489 colliding_path_messages = set() 490 491 remove_content = sorted(self.pkgmeta['removecontent'], reverse = True) 492 for item in remove_content: 493 494 if not item: 495 continue # empty element?? 496 497 sys_root_item = sys_root + item 498 499 # collision check 500 if col_protect > 0: 501 502 if self.Entropy.clientDbconn.isFileAvailable(item) \ 503 and os.path.isfile(sys_root_item): 504 505 # in this way we filter out directories 506 colliding_path_messages.add(sys_root_item) 507 not_removed_due_to_collisions.add(item) 508 continue 509 510 protected = False 511 in_mask = False 512 513 if (not self.pkgmeta['removeconfig']) and \ 514 (not self.pkgmeta['diffremoval']): 515 516 protected_item_test = sys_root_item 517 if isinstance(protected_item_test, unicode): 518 protected_item_test = protected_item_test.encode('utf-8') 519 520 in_mask, protected, x, do_continue = \ 521 self._handle_config_protect( 522 protect, mask, None, protected_item_test, 523 do_allocation_check = False, do_quiet = True 524 ) 525 526 if do_continue: 527 protected = True 528 529 # when files have not been modified by the user 530 # and they are inside a config protect directory 531 # we could even remove them directly 532 if in_mask: 533 534 oldprot_md5 = automerge_metadata.get(item) 535 if oldprot_md5 and os.path.exists(protected_item_test) and \ 536 os.access(protected_item_test, os.R_OK): 537 538 in_system_md5 = entropy.tools.md5sum( 539 protected_item_test) 540 541 if oldprot_md5 == in_system_md5: 542 prot_msg = _("Removing config file, never modified") 543 mytxt = "%s: %s" % ( 544 darkgreen(prot_msg), 545 blue(item), 546 ) 547 self.Entropy.updateProgress( 548 mytxt, 549 importance = 1, 550 type = "info", 551 header = red(" ## ") 552 ) 553 protected = False 554 do_continue = False 555 556 # Is file or directory a protected item? 557 if protected: 558 self.Entropy.clientLog.log( 559 ETP_LOGPRI_INFO, 560 ETP_LOGLEVEL_VERBOSE, 561 "[remove] Protecting config file: %s" % (sys_root_item,) 562 ) 563 mytxt = "[%s] %s: %s" % ( 564 red(_("remove")), 565 brown(_("Protecting config file")), 566 sys_root_item, 567 ) 568 self.Entropy.updateProgress( 569 mytxt, 570 importance = 1, 571 type = "warning", 572 header = red(" ## ") 573 ) 574 continue 575 576 577 try: 578 os.lstat(sys_root_item) 579 580 except OSError: 581 continue # skip file, does not exist 582 583 except UnicodeEncodeError: 584 msg = _("This package contains a badly encoded file !!!") 585 mytxt = brown(msg) 586 self.Entropy.updateProgress( 587 red("QA: ")+mytxt, 588 importance = 1, 589 type = "warning", 590 header = darkred(" ## ") 591 ) 592 continue # file has a really bad encoding 593 594 if os.path.isdir(sys_root_item) and \ 595 os.path.islink(sys_root_item): 596 # S_ISDIR returns False for directory symlinks, 597 # so using os.path.isdir valid directory symlink 598 if sys_root_item not in directories_cache: 599 directories.add((sys_root_item, "link")) 600 directories_cache.add(sys_root_item) 601 continue 602 603 if os.path.isdir(sys_root_item): 604 # plain directory 605 if sys_root_item not in directories_cache: 606 directories.add((sys_root_item, "dir")) 607 directories_cache.add(sys_root_item) 608 continue 609 610 # files, symlinks or not 611 # just a file or symlink or broken 612 # directory symlink (remove now) 613 614 try: 615 os.remove(sys_root_item) 616 except OSError, err: 617 self.Entropy.clientLog.log( 618 ETP_LOGPRI_INFO, 619 ETP_LOGLEVEL_NORMAL, 620 "[remove] Unable to remove %s, error: %s" % ( 621 sys_root_item, err,) 622 ) 623 continue 624 625 # add its parent directory 626 dirobj = os.path.dirname(sys_root_item) 627 if dirobj not in directories_cache: 628 if os.path.isdir(dirobj) and os.path.islink(dirobj): 629 directories.add((dirobj, "link")) 630 elif os.path.isdir(dirobj): 631 directories.add((dirobj, "dir")) 632 633 directories_cache.add(dirobj) 634 635 636 if colliding_path_messages: 637 self.Entropy.updateProgress( 638 "%s:" % (_("Collision found during removal of"),), 639 importance = 1, 640 type = "warning", 641 header = red(" ## ") 642 ) 643 644 for path in sorted(colliding_path_messages): 645 self.Entropy.updateProgress( 646 purple(path), 647 importance = 0, 648 type = "warning", 649 header = red(" ## ") 650 ) 651 self.Entropy.clientLog.log(ETP_LOGPRI_INFO, ETP_LOGLEVEL_NORMAL, 652 "Collision found during removal of %s - cannot overwrite" % ( 653 path,) 654 ) 655 656 # removing files not removed from removecontent. 657 # it happened that boot services not removed due to 658 # collisions got removed from their belonging runlevels 659 # by postremove step. 660 # since this is a set, it is a mapped type, so every 661 # other instance around will feature this update 662 self.pkgmeta['removecontent'] -= not_removed_due_to_collisions 663 664 # now handle directories 665 directories = sorted(directories, reverse = True) 666 while 1: 667 taint = False 668 for directory, dirtype in directories: 669 mydir = "%s%s" % (sys_root, directory,) 670 if dirtype == "link": 671 try: 672 mylist = os.listdir(mydir) 673 if not mylist: 674 try: 675 os.remove(mydir) 676 taint = True 677 except OSError: 678 pass 679 except OSError: 680 pass 681 elif dirtype == "dir": 682 try: 683 mylist = os.listdir(mydir) 684 if not mylist: 685 try: 686 os.rmdir(mydir) 687 taint = True 688 except OSError: 689 pass 690 except OSError: 691 pass 692 693 if not taint: 694 break 695 696 del directories_cache 697 del directories
698
699 - def _cleanup_package(self, unpack_dir):
700 # remove unpack dir 701 shutil.rmtree(unpack_dir, True) 702 try: 703 os.rmdir(unpack_dir) 704 except OSError: 705 pass 706 return 0
707
708 - def __clear_cache(self):
709 self.Entropy.clear_dump_cache(etpCache['advisories']) 710 self.Entropy.clear_dump_cache(etpCache['filter_satisfied_deps']) 711 self.Entropy.clear_dump_cache(etpCache['depends_tree']) 712 self.Entropy.clear_dump_cache(etpCache['check_package_update']) 713 self.Entropy.clear_dump_cache(etpCache['dep_tree']) 714 self.Entropy.clear_dump_cache(etpCache['dbMatch'] + \ 715 etpConst['clientdbid']+"/") 716 self.Entropy.clear_dump_cache(etpCache['dbSearch'] + \ 717 etpConst['clientdbid']+"/") 718 719 # clear caches, the bad way 720 self.Entropy.clear_dump_cache(etpCache['world_available']) 721 self.Entropy.clear_dump_cache(etpCache['world_update']) 722 self.Entropy.clear_dump_cache(etpCache['critical_update'])
723
724 - def __install_package(self):
725 726 # clear on-disk cache 727 self.__clear_cache() 728 729 self.Entropy.clientLog.log( 730 ETP_LOGPRI_INFO, 731 ETP_LOGLEVEL_NORMAL, 732 "Installing package: %s" % (self.pkgmeta['atom'],) 733 ) 734 735 already_protected_config_files = {} 736 if self.pkgmeta['removeidpackage'] != -1: 737 already_protected_config_files = \ 738 self.Entropy.clientDbconn.retrieveAutomergefiles( 739 self.pkgmeta['removeidpackage'], get_dict = True 740 ) 741 742 # copy files over - install 743 # use fork? (in this case all the changed structures 744 # need to be pushed back) 745 rc = self.move_image_to_system(already_protected_config_files) 746 if rc != 0: 747 return rc 748 749 # inject into database 750 mytxt = "%s: %s" % ( 751 blue(_("Updating database")), 752 red(self.pkgmeta['atom']), 753 ) 754 self.Entropy.updateProgress( 755 mytxt, 756 importance = 1, 757 type = "info", 758 header = red(" ## ") 759 ) 760 idpackage = self.add_installed_package() 761 762 # remove old files and spm stuff 763 if self.pkgmeta['removeidpackage'] != -1: 764 765 # doing a diff removal 766 self.Entropy.clientLog.log( 767 ETP_LOGPRI_INFO, 768 ETP_LOGLEVEL_NORMAL, 769 "Remove old package: %s" % (self.pkgmeta['removeatom'],) 770 ) 771 772 self.Entropy.updateProgress( 773 blue(_("Cleaning previously installed information...")), 774 importance = 1, 775 type = "info", 776 header = red(" ## ") 777 ) 778 779 spm_rc = self.spm_remove_package() 780 if spm_rc != 0: 781 return spm_rc 782 783 self.remove_content_from_system(self.pkgmeta['removeidpackage'], 784 automerge_metadata = already_protected_config_files) 785 786 return self.spm_install_package(idpackage)
787
788 - def spm_install_package(self, idpackage):
789 """ 790 Call Source Package Manager interface and tell it to register our 791 newly installed package. 792 793 @param idpackage: Entropy repository package identifier 794 @type idpackage: int 795 @return: execution status 796 @rtype: int 797 """ 798 try: 799 Spm = self.Entropy.Spm() 800 except Exception, err: 801 self.Entropy.clientLog.log( 802 ETP_LOGPRI_INFO, 803 ETP_LOGLEVEL_NORMAL, 804 "Source Package Manager not available: %s | %s" % ( 805 type(Exception), err, 806 ) 807 ) 808 return -1 809 810 self.Entropy.clientLog.log( 811 ETP_LOGPRI_INFO, 812 ETP_LOGLEVEL_NORMAL, 813 "Installing new SPM entry: %s" % (self.pkgmeta['atom'],) 814 ) 815 816 spm_uid = Spm.add_installed_package(self.pkgmeta) 817 if spm_uid != -1: 818 self.Entropy.clientDbconn.insertSpmUid(idpackage, spm_uid) 819 820 return 0
821
822 - def spm_remove_package(self):
823 """ 824 Call Source Package Manager interface and tell it to remove our 825 just removed package. 826 827 @return: execution status 828 @rtype: int 829 """ 830 try: 831 Spm = self.Entropy.Spm() 832 except Exception, err: 833 self.Entropy.clientLog.log( 834 ETP_LOGPRI_INFO, 835 ETP_LOGLEVEL_NORMAL, 836 "Source Package Manager not available: %s | %s" % ( 837 type(Exception), err, 838 ) 839 ) 840 return -1 841 842 self.Entropy.clientLog.log( 843 ETP_LOGPRI_INFO, 844 ETP_LOGLEVEL_NORMAL, 845 "Removing from SPM: %s" % (self.pkgmeta['removeatom'],) 846 ) 847 848 return Spm.remove_installed_package(self.pkgmeta)
849 850
851 - def add_installed_package(self):
852 """ 853 For internal use only. 854 Copy package from repository to installed packages one. 855 """ 856 857 # fetch info 858 smart_pkg = self.pkgmeta['smartpackage'] 859 dbconn = self.Entropy.open_repository(self.pkgmeta['repository']) 860 861 if smart_pkg or self.pkgmeta['merge_from']: 862 863 data = dbconn.getPackageData(self.pkgmeta['idpackage'], 864 content_insert_formatted = True) 865 866 if self.pkgmeta['removeidpackage'] != -1: 867 self.pkgmeta['removecontent'].update( 868 self.Entropy.clientDbconn.contentDiff( 869 self.pkgmeta['removeidpackage'], 870 dbconn, 871 self.pkgmeta['idpackage'] 872 ) 873 ) 874 875 else: 876 877 # normal repositories 878 data = dbconn.getPackageData(self.pkgmeta['idpackage'], 879 get_content = False) 880 pkg_dbconn = self.Entropy.open_generic_database( 881 self.pkgmeta['pkgdbpath']) 882 # it is safe to consider that package dbs coming from repos 883 # contain only one entry 884 pkg_idpackage = sorted(pkg_dbconn.listAllIdpackages())[0] 885 content = pkg_dbconn.retrieveContent( 886 pkg_idpackage, extended = True, 887 formatted = True, insert_formatted = True 888 ) 889 real_idpk = self.pkgmeta['idpackage'] 890 content = [(real_idpk, x, y,) for orig_idpk, x, y in content] 891 data['content'] = content 892 893 if self.pkgmeta['removeidpackage'] != -1: 894 self.pkgmeta['removecontent'].update( 895 self.Entropy.clientDbconn.contentDiff( 896 self.pkgmeta['removeidpackage'], 897 pkg_dbconn, 898 pkg_idpackage 899 ) 900 ) 901 902 pkg_dbconn.closeDB() 903 904 # this is needed to make postinstall trigger to work properly 905 trigger_content = set((x[1] for x in data['content'])) 906 self.pkgmeta['triggers']['install']['content'] = trigger_content 907 908 # open client db 909 # always set data['injected'] to False 910 # installed packages database SHOULD never have more 911 # than one package for scope (key+slot) 912 data['injected'] = False 913 # spm counter will be set in self._install_package_into_spm_database() 914 data['counter'] = -1 915 # branch must be always set properly, it could happen it's not 916 # when installing packages through their .tbz2s 917 data['branch'] = self.Entropy.SystemSettings['repositories']['branch'] 918 # there is no need to store needed paths into db 919 if data.get('needed_paths'): 920 del data['needed_paths'] 921 922 idpackage, rev, x = self.Entropy.clientDbconn.handlePackage( 923 data, forcedRevision = data['revision'], formattedContent = True) 924 925 # update datecreation 926 ctime = entropy.tools.get_current_unix_time() 927 self.Entropy.clientDbconn.setCreationDate(idpackage, str(ctime)) 928 929 # add idpk to the installedtable 930 self.Entropy.clientDbconn.dropInstalledPackageFromStore(idpackage) 931 self.Entropy.clientDbconn.storeInstalledPackage(idpackage, 932 self.pkgmeta['repository'], self.pkgmeta['install_source']) 933 934 automerge_data = self.pkgmeta.get('configprotect_data') 935 if automerge_data: 936 self.Entropy.clientDbconn.insertAutomergefiles(idpackage, 937 automerge_data) 938 939 # clear depends table, this will make clientdb dependstable to be 940 # regenerated during the next request (retrieveReverseDependencies) 941 self.Entropy.clientDbconn.taintReverseDependenciesMetadata() 942 return idpackage
943
944 - def __fill_image_dir(self, mergeFrom, image_dir):
945 946 dbconn = self.Entropy.open_repository(self.pkgmeta['repository']) 947 # this is triggered by merge_from pkgmeta metadata 948 # even if repositories are allowed to not have content 949 # metadata, in this particular case, it is mandatory 950 package_content = dbconn.retrieveContent( 951 self.pkgmeta['idpackage'], extended = True, formatted = True) 952 contents = sorted(package_content) 953 954 # collect files 955 for path in contents: 956 # convert back to filesystem str 957 encoded_path = path 958 path = os.path.join(mergeFrom, encoded_path[1:]) 959 topath = os.path.join(image_dir, encoded_path[1:]) 960 path = path.encode('raw_unicode_escape') 961 topath = topath.encode('raw_unicode_escape') 962 963 try: 964 exist = os.lstat(path) 965 except OSError: 966 continue # skip file 967 ftype = package_content[encoded_path] 968 969 if 'dir' == ftype and \ 970 not stat.S_ISDIR(exist.st_mode) and \ 971 os.path.isdir(path): 972 # workaround for directory symlink issues 973 path = os.path.realpath(path) 974 975 copystat = False 976 # if our directory is a symlink instead, then copy the symlink 977 if os.path.islink(path): 978 tolink = os.readlink(path) 979 if os.path.islink(topath): 980 os.remove(topath) 981 os.symlink(tolink, topath) 982 elif os.path.isdir(path): 983 if not os.path.isdir(topath): 984 os.makedirs(topath) 985 copystat = True 986 elif os.path.isfile(path): 987 if os.path.isfile(topath): 988 os.remove(topath) # should never happen 989 shutil.copy2(path, topath) 990 copystat = True 991 992 if copystat: 993 user = os.stat(path)[stat.ST_UID] 994 group = os.stat(path)[stat.ST_GID] 995 os.chown(topath, user, group) 996 shutil.copystat(path, topath)
997 998
999 - def move_image_to_system(self, already_protected_config_files):
1000 1001 # load CONFIG_PROTECT and its mask 1002 protect = self.Entropy.get_package_match_config_protect( 1003 self.matched_atom) 1004 mask = self.Entropy.get_package_match_config_protect( 1005 self.matched_atom, mask = True) 1006 sys_root = etpConst['systemroot'] 1007 sys_set_plg_id = \ 1008 etpConst['system_settings_plugins_ids']['client_plugin'] 1009 misc_data = self.Entropy.SystemSettings[sys_set_plg_id]['misc'] 1010 col_protect = misc_data['collisionprotect'] 1011 items_installed = set() 1012 1013 # setup image_dir properly 1014 image_dir = self.pkgmeta['imagedir'] 1015 encoded_image_dir = image_dir.encode('utf-8') 1016 movefile = entropy.tools.movefile 1017 1018 def workout_subdir(currentdir, subdir): 1019 1020 imagepath_dir = "%s/%s" % (currentdir, subdir,) 1021 rootdir = "%s%s" % (sys_root, imagepath_dir[len(image_dir):],) 1022 1023 # handle broken symlinks 1024 if os.path.islink(rootdir) and not os.path.exists(rootdir): 1025 # broken symlink 1026 os.remove(rootdir) 1027 1028 # if our directory is a file on the live system 1029 elif os.path.isfile(rootdir): # really weird...! 1030 1031 self.Entropy.clientLog.log( 1032 ETP_LOGPRI_INFO, 1033 ETP_LOGLEVEL_NORMAL, 1034 "WARNING!!! %s is a file when it should be " \ 1035 "a directory !! Removing in 20 seconds..." % (rootdir,) 1036 ) 1037 mytxt = darkred(_("%s is a file when should be a " \ 1038 "directory !! Removing in 20 seconds...") % (rootdir,)) 1039 1040 self.Entropy.updateProgress( 1041 red("QA: ")+mytxt, 1042 importance = 1, 1043 type = "warning", 1044 header = red(" !!! ") 1045 ) 1046 entropy.tools.ebeep(20) 1047 os.remove(rootdir) 1048 1049 # if our directory is a symlink instead, then copy the symlink 1050 if os.path.islink(imagepath_dir): 1051 1052 # if our live system features a directory instead of 1053 # a symlink, we should consider removing the directory 1054 if not os.path.islink(rootdir) and os.path.isdir(rootdir): 1055 self.Entropy.clientLog.log( 1056 ETP_LOGPRI_INFO, 1057 ETP_LOGLEVEL_NORMAL, 1058 "WARNING!!! %s is a directory when it should be " \ 1059 "a symlink !! Removing in 20 seconds..." % ( 1060 rootdir,) 1061 ) 1062 mytxt = "%s: %s" % ( 1063 _("directory expected, symlink found"), 1064 rootdir, 1065 ) 1066 mytxt2 = _("Removing in 20 seconds !!") 1067 for txt in (mytxt, mytxt2,): 1068 self.Entropy.updateProgress( 1069 darkred("QA: ") + darkred(txt), 1070 importance = 1, 1071 type = "warning", 1072 header = red(" !!! ") 1073 ) 1074 1075 entropy.tools.ebeep(20) 1076 # fucking kill it in any case! 1077 # rootdir must die! die die die die! 1078 # /me brings chainsaw 1079 try: 1080 shutil.rmtree(rootdir, True) 1081 except (shutil.Error, OSError,), err: 1082 self.Entropy.clientLog.log( 1083 ETP_LOGPRI_INFO, 1084 ETP_LOGLEVEL_NORMAL, 1085 "WARNING!!! Failed to rm %s " \ 1086 "directory ! [workout_subdir/1]: %s" % ( 1087 rootdir, err, 1088 ) 1089 ) 1090 1091 tolink = os.readlink(imagepath_dir) 1092 live_tolink = None 1093 if os.path.islink(rootdir): 1094 live_tolink = os.readlink(rootdir) 1095 1096 if tolink != live_tolink: 1097 if os.path.lexists(rootdir): 1098 # at this point, it must be a file 1099 os.remove(rootdir) 1100 os.symlink(tolink, rootdir) 1101 1102 elif not os.path.isdir(rootdir) and not \ 1103 os.access(rootdir, os.R_OK): 1104 # directory not found, we need to create it 1105 1106 try: 1107 # really force a simple mkdir first of all 1108 os.mkdir(rootdir) 1109 except OSError: 1110 os.makedirs(rootdir) 1111 1112 1113 if not os.path.islink(rootdir) and os.access(rootdir, os.W_OK): 1114 1115 # symlink doesn't need permissions, also 1116 # until os.walk ends they might be broken 1117 # XXX also, added os.access() check because 1118 # there might be directories/files unwritable 1119 # what to do otherwise? 1120 user = os.stat(imagepath_dir)[stat.ST_UID] 1121 group = os.stat(imagepath_dir)[stat.ST_GID] 1122 os.chown(rootdir, user, group) 1123 shutil.copystat(imagepath_dir, rootdir) 1124 1125 item_dir, item_base = os.path.split(rootdir) 1126 item_dir = os.path.realpath(item_dir) 1127 item_inst = os.path.join(item_dir, item_base) 1128 items_installed.add(item_inst)
1129 1130 1131 def workout_file(currentdir, item): 1132 1133 fromfile = "%s/%s" % (currentdir, item,) 1134 tofile = "%s%s" % (sys_root, fromfile[len(image_dir):],) 1135 1136 if col_protect > 1: 1137 todbfile = fromfile[len(image_dir):] 1138 myrc = self._handle_install_collision_protect(tofile, 1139 todbfile) 1140 if not myrc: 1141 return 1142 1143 prot_old_tofile = tofile[len(sys_root):] 1144 pre_tofile = tofile[:] 1145 in_mask, protected, tofile, do_return = \ 1146 self._handle_config_protect(protect, mask, fromfile, tofile) 1147 1148 # collect new config automerge data 1149 if in_mask and os.path.exists(fromfile): 1150 try: 1151 prot_md5 = entropy.tools.md5sum(fromfile) 1152 self.pkgmeta['configprotect_data'].append( 1153 (prot_old_tofile, prot_md5,)) 1154 except (IOError,), err: 1155 self.Entropy.clientLog.log( 1156 ETP_LOGPRI_INFO, 1157 ETP_LOGLEVEL_NORMAL, 1158 "WARNING!!! Failed to get md5 of %s " \ 1159 "file ! [workout_file/1]: %s" % ( 1160 fromfile, err, 1161 ) 1162 ) 1163 1164 # check if it's really necessary to protect file 1165 if protected: 1166 1167 # second task 1168 oldprot_md5 = already_protected_config_files.get( 1169 prot_old_tofile) 1170 1171 if oldprot_md5 and os.path.exists(pre_tofile) and \ 1172 os.access(pre_tofile, os.R_OK): 1173 1174 try: 1175 in_system_md5 = entropy.tools.md5sum(pre_tofile) 1176 except (IOError,): 1177 # which is a clearly invalid value 1178 in_system_md5 = "0000" 1179 1180 if oldprot_md5 == in_system_md5: 1181 # we can merge it, files, even if 1182 # contains changes have not been modified 1183 # by the user 1184 msg = _("Automerging config file, never modified") 1185 mytxt = "%s: %s" % ( 1186 darkgreen(msg), 1187 blue(pre_tofile), 1188 ) 1189 self.Entropy.updateProgress( 1190 mytxt, 1191 importance = 1, 1192 type = "info", 1193 header = red(" ## ") 1194 ) 1195 protected = False 1196 do_return = False 1197 tofile = pre_tofile 1198 1199 if do_return: 1200 return 1201 1202 if os.path.realpath(fromfile) == os.path.realpath(tofile) and \ 1203 os.path.islink(tofile): 1204 # there is a serious issue here, better removing tofile, 1205 # happened to someone. 1206 1207 try: 1208 # try to cope... 1209 os.remove(tofile) 1210 except (OSError, IOError,), err: 1211 self.Entropy.clientLog.log( 1212 ETP_LOGPRI_INFO, 1213 ETP_LOGLEVEL_NORMAL, 1214 "WARNING!!! Failed to cope to oddity of %s " \ 1215 "file ! [workout_file/2]: %s" % ( 1216 tofile, err, 1217 ) 1218 ) 1219 1220 # if our file is a dir on the live system 1221 if os.path.isdir(tofile) and not os.path.islink(tofile): 1222 1223 # really weird...! 1224 self.Entropy.clientLog.log( 1225 ETP_LOGPRI_INFO, 1226 ETP_LOGLEVEL_NORMAL, 1227 "WARNING!!! %s is a directory when it should " \ 1228 "be a file !! Removing in 20 seconds..." % (tofile,) 1229 ) 1230 1231 mytxt = "%s: %s" % ( 1232 _("file expected, directory found"), 1233 tofile, 1234 ) 1235 mytxt2 = _("Removing in 20 seconds !!") 1236 for txt in (mytxt, mytxt2,): 1237 self.Entropy.updateProgress( 1238 darkred("QA: ") + darkred(txt), 1239 importance = 1, 1240 type = "warning", 1241 header = red(" !!! ") 1242 ) 1243 entropy.tools.ebeep(20) 1244 1245 try: 1246 shutil.rmtree(tofile, True) 1247 except (shutil.Error, IOError,), err: 1248 self.Entropy.clientLog.log( 1249 ETP_LOGPRI_INFO, 1250 ETP_LOGLEVEL_NORMAL, 1251 "WARNING!!! Failed to cope to oddity of %s " \ 1252 "file ! [workout_file/3]: %s" % ( 1253 tofile, err, 1254 ) 1255 ) 1256 1257 # moving file using the raw format 1258 try: 1259 done = movefile(fromfile, tofile, 1260 src_basedir = encoded_image_dir) 1261 except (IOError,), err: 1262 # try to move forward, sometimes packages might be 1263 # fucked up and contain broken things 1264 if err.errno not in (errno.ENOENT, errno.EACCES,): 1265 raise 1266 1267 self.Entropy.clientLog.log( 1268 ETP_LOGPRI_INFO, 1269 ETP_LOGLEVEL_NORMAL, 1270 "WARNING!!! Error during file move" \ 1271 " to system: %s => %s | IGNORED: %s" % ( 1272 fromfile, 1273 tofile, 1274 err, 1275 ) 1276 ) 1277 done = True 1278 1279 if not done: 1280 self.Entropy.clientLog.log( 1281 ETP_LOGPRI_INFO, 1282 ETP_LOGLEVEL_NORMAL, 1283 "WARNING!!! Error during file move" \ 1284 " to system: %s => %s" % (fromfile, tofile,) 1285 ) 1286 mytxt = "%s: %s => %s, %s" % ( 1287 _("File move error"), 1288 fromfile, 1289 tofile, 1290 _("please report"), 1291 ) 1292 self.Entropy.updateProgress( 1293 red("QA: ")+darkred(mytxt), 1294 importance = 1, 1295 type = "warning", 1296 header = red(" !!! ") 1297 ) 1298 return 4 1299 1300 item_dir = os.path.realpath(os.path.dirname(tofile)) 1301 item_inst = os.path.join(item_dir, os.path.basename(tofile)) 1302 items_installed.add(item_inst) 1303 1304 if protected: 1305 # add to disk cache 1306 self.Entropy.FileUpdates.add_to_cache(tofile, quiet = True) 1307 1308 1309 # merge data into system 1310 for currentdir, subdirs, files in os.walk(encoded_image_dir): 1311 1312 # create subdirs 1313 for subdir in subdirs: 1314 workout_subdir(currentdir, subdir) 1315 1316 for item in files: 1317 workout_file(currentdir, item) 1318 1319 # this is useful to avoid the removal of installed 1320 # files by __remove_package just because 1321 # there's a difference in the directory path, perhaps, 1322 # which is not handled correctly by 1323 # EntropyRepository.contentDiff for obvious reasons 1324 # (think about stuff in /usr/lib and /usr/lib64, 1325 # where the latter is just a symlink to the former) 1326 if self.pkgmeta.get('removecontent'): 1327 my_remove_content = set() 1328 for mypath in self.pkgmeta['removecontent']: 1329 1330 if not mypath: 1331 continue # empty? 1332 1333 item_dir = os.path.dirname("%s%s" % (sys_root, mypath,)) 1334 item = os.path.join(os.path.realpath(item_dir), 1335 os.path.basename(mypath)) 1336 1337 if item in items_installed: 1338 my_remove_content.add(item) 1339 1340 self.pkgmeta['removecontent'] -= my_remove_content 1341 1342 return 0 1343
1344 - def _handle_config_protect(self, protect, mask, fromfile, tofile, 1345 do_allocation_check = True, do_quiet = False):
1346 """ 1347 Handle configuration file protection. This method contains the logic 1348 for determining if a file should be protected from overwrite. 1349 """ 1350 1351 protected = False 1352 tofile_before_protect = tofile 1353 do_continue = False 1354 in_mask = False 1355 encoded_protect = [x.encode('raw_unicode_escape') for x in protect] 1356 1357 if tofile in encoded_protect: 1358 protected = True 1359 in_mask = True 1360 1361 elif os.path.dirname(tofile) in encoded_protect: 1362 protected = True 1363 in_mask = True 1364 1365 else: 1366 tofile_testdir = os.path.dirname(tofile) 1367 old_tofile_testdir = None 1368 while tofile_testdir != old_tofile_testdir: 1369 if tofile_testdir in encoded_protect: 1370 protected = True 1371 in_mask = True 1372 break 1373 old_tofile_testdir = tofile_testdir 1374 tofile_testdir = os.path.dirname(tofile_testdir) 1375 1376 if protected: # check if perhaps, file is masked, so unprotected 1377 newmask = [x.encode('raw_unicode_escape') for x in mask] 1378 1379 if tofile in newmask: 1380 protected = False 1381 in_mask = False 1382 1383 elif os.path.dirname(tofile) in newmask: 1384 protected = False 1385 in_mask = False 1386 1387 else: 1388 tofile_testdir = os.path.dirname(tofile) 1389 old_tofile_testdir = None 1390 while tofile_testdir != old_tofile_testdir: 1391 if tofile_testdir in newmask: 1392 protected = False 1393 in_mask = False 1394 break 1395 old_tofile_testdir = tofile_testdir 1396 tofile_testdir = os.path.dirname(tofile_testdir) 1397 1398 if not os.path.lexists(tofile): 1399 protected = False # file doesn't exist 1400 1401 # check if it's a text file 1402 if protected and os.access(tofile, os.F_OK | os.R_OK): 1403 protected = entropy.tools.istextfile(tofile) 1404 in_mask = protected 1405 else: 1406 protected = False # it's not a file 1407 1408 if not protected: 1409 return in_mask, protected, tofile, do_continue 1410 1411 ## ## 1412 # file is protected # 1413 ##__________________## 1414 1415 sys_set_plg_id = \ 1416 etpConst['system_settings_plugins_ids']['client_plugin'] 1417 client_settings = self.Entropy.SystemSettings[sys_set_plg_id] 1418 misc_settings = client_settings['misc'] 1419 1420 # check if protection is disabled for this element 1421 if tofile in misc_settings['configprotectskip']: 1422 self.Entropy.clientLog.log( 1423 ETP_LOGPRI_INFO, 1424 ETP_LOGLEVEL_NORMAL, 1425 "Skipping config file installation/removal, " \ 1426 "as stated in client.conf: %s" % (tofile,) 1427 ) 1428 if not do_quiet: 1429 mytxt = "%s: %s" % ( 1430 _("Skipping file installation/removal"), 1431 tofile, 1432 ) 1433 self.Entropy.updateProgress( 1434 mytxt, 1435 importance = 1, 1436 type = "warning", 1437 header = darkred(" ## ") 1438 ) 1439 do_continue = True 1440 return in_mask, protected, tofile, do_continue 1441 1442 ## ## 1443 # file is protected (2) # 1444 ##______________________## 1445 1446 prot_status = True 1447 if do_allocation_check: 1448 tofile, prot_status = entropy.tools.allocate_masked_file( 1449 tofile, fromfile) 1450 1451 if not prot_status: 1452 # a protected file with the same content 1453 # is already in place, so not going to protect 1454 # the same file twice 1455 protected = False 1456 return in_mask, protected, tofile, do_continue 1457 1458 ## ## 1459 # file is protected (3) # 1460 ##______________________## 1461 1462 oldtofile = tofile 1463 if oldtofile.find("._cfg") != -1: 1464 oldtofile = os.path.join(os.path.dirname(oldtofile), 1465 os.path.basename(oldtofile)[10:]) 1466 1467 if not do_quiet: 1468 self.Entropy.clientLog.log( 1469 ETP_LOGPRI_INFO, 1470 ETP_LOGLEVEL_NORMAL, 1471 "Protecting config file: %s" % (oldtofile,) 1472 ) 1473 mytxt = red("%s: %s") % (_("Protecting config file"), oldtofile,) 1474 self.Entropy.updateProgress( 1475 mytxt, 1476 importance = 1, 1477 type = "warning", 1478 header = darkred(" ## ") 1479 ) 1480 1481 return in_mask, protected, tofile, do_continue
1482 1483
1484 - def _handle_install_collision_protect(self, tofile, todbfile):
1485 1486 avail = self.Entropy.clientDbconn.isFileAvailable(todbfile, 1487 get_id = True) 1488 1489 if (self.pkgmeta['removeidpackage'] not in avail) and avail: 1490 mytxt = darkred(_("Collision found during install for")) 1491 mytxt += " %s - %s" % ( 1492 blue(tofile), 1493 darkred(_("cannot overwrite")), 1494 ) 1495 self.Entropy.updateProgress( 1496 red("QA: ")+mytxt, 1497 importance = 1, 1498 type = "warning", 1499 header = darkred(" ## ") 1500 ) 1501 self.Entropy.clientLog.log( 1502 ETP_LOGPRI_INFO, 1503 ETP_LOGLEVEL_NORMAL, 1504 "WARNING!!! Collision found during install " \ 1505 "for %s - cannot overwrite" % (tofile,) 1506 ) 1507 return False 1508 1509 return True
1510
1511 - def sources_fetch_step(self):
1512 self.error_on_not_prepared() 1513 1514 down_data = self.pkgmeta['download'] 1515 down_keys = down_data.keys() 1516 d_cache = set() 1517 rc = 0 1518 key_cache = [os.path.basename(x) for x in down_keys] 1519 1520 for key in sorted(down_keys): 1521 1522 key_name = os.path.basename(key) 1523 if key_name in d_cache: continue 1524 # first fine wins 1525 1526 for url in down_data[key]: 1527 1528 file_name = os.path.basename(url) 1529 if self.pkgmeta.get('fetch_path'): 1530 dest_file = os.path.join(self.pkgmeta['fetch_path'], 1531 file_name) 1532 else: 1533 dest_file = os.path.join(self.pkgmeta['unpackdir'], 1534 file_name) 1535 1536 rc = self._fetch_source(url, dest_file) 1537 if rc == 0: 1538 d_cache.add(key_name) 1539 break 1540 1541 key_cache.remove(key_name) 1542 if rc != 0 and key_name not in key_cache: 1543 break 1544 1545 rc = 0 1546 1547 return rc
1548
1549 - def _fetch_source(self, url, dest_file):
1550 rc = 1 1551 try: 1552 1553 mytxt = "%s: %s" % (blue(_("Downloading")), brown(url),) 1554 # now fetch the new one 1555 self.Entropy.updateProgress( 1556 mytxt, 1557 importance = 1, 1558 type = "info", 1559 header = red(" ## ") 1560 ) 1561 1562 rc, data_transfer, resumed = self.Entropy.fetch_file( 1563 url, 1564 None, 1565 None, 1566 False, 1567 fetch_file_abort_function = self.fetch_abort_function, 1568 filepath = dest_file 1569 ) 1570 if rc == 0: 1571 mytxt = blue("%s: ") % (_("Successfully downloaded from"),) 1572 mytxt += red(entropy.tools.spliturl(url)[1]) 1573 human_bytes = entropy.tools.bytes_into_human(data_transfer) 1574 mytxt += " %s %s/%s" % (_("at"), human_bytes, _("second"),) 1575 self.Entropy.updateProgress( 1576 mytxt, 1577 importance = 1, 1578 type = "info", 1579 header = red(" ## ") 1580 ) 1581 self.Entropy.updateProgress( 1582 "%s: %s" % (blue(_("Local path")), brown(dest_file),), 1583 importance = 1, 1584 type = "info", 1585 header = red(" # ") 1586 ) 1587 else: 1588 error_message = blue("%s: %s") % ( 1589 _("Error downloading from"), 1590 red(entropy.tools.spliturl(url)[1]), 1591 ) 1592 # something bad happened 1593 if rc == -1: 1594 error_message += " - %s." % ( 1595 _("file not available on this mirror"), 1596 ) 1597 elif rc == -3: 1598 error_message += " - not found." 1599 elif rc == -100: 1600 error_message += " - %s." % (_("discarded download"),) 1601 else: 1602 error_message += " - %s: %s" % (_("unknown reason"), rc,) 1603 self.Entropy.updateProgress( 1604 error_message, 1605 importance = 1, 1606 type = "warning", 1607 header = red(" ## ") 1608 ) 1609 1610 except KeyboardInterrupt: 1611 pass 1612 1613 return rc
1614
1615 - def fetch_step(self):
1616 self.error_on_not_prepared() 1617 1618 mytxt = "%s: %s" % (blue(_("Downloading archive")), 1619 red(os.path.basename(self.pkgmeta['download'])),) 1620 self.Entropy.updateProgress( 1621 mytxt, 1622 importance = 1, 1623 type = "info", 1624 header = red(" ## ") 1625 ) 1626 1627 rc = 0 1628 if not self.pkgmeta['verified']: 1629 1630 branch = self.Entropy.get_branch_from_download_relative_uri( 1631 self.pkgmeta['download']) 1632 rc = self.Entropy.fetch_file_on_mirrors( 1633 self.pkgmeta['repository'], 1634 branch, 1635 self.pkgmeta['download'], 1636 self.pkgmeta['checksum'], 1637 fetch_abort_function = self.fetch_abort_function 1638 ) 1639 1640 if rc == 0: 1641 return 0 1642 1643 mytxt = "%s. %s: %s" % ( 1644 red(_("Package cannot be fetched. Try to update repositories")), 1645 blue(_("Error")), 1646 rc, 1647 ) 1648 self.Entropy.updateProgress( 1649 mytxt, 1650 importance = 1, 1651 type = "error", 1652 header = darkred(" ## ") 1653 ) 1654 1655 return rc
1656
1657 - def multi_fetch_step(self):
1658 self.error_on_not_prepared() 1659 1660 m_fetch_len = len(self.pkgmeta['multi_fetch_list']) 1661 mytxt = "%s: %s %s" % ( 1662 blue(_("Downloading")), 1663 darkred(str(m_fetch_len)), 1664 _("archives"), 1665 ) 1666 1667 self.Entropy.updateProgress( 1668 mytxt, 1669 importance = 1, 1670 type = "info", 1671 header = red(" ## ") 1672 ) 1673 rc, err_list = self.Entropy.fetch_files_on_mirrors( 1674 self.pkgmeta['multi_fetch_list'], 1675 self.pkgmeta['checksum'], 1676 fetch_abort_function = self.fetch_abort_function 1677 ) 1678 1679 if rc == 0: 1680 return 0 1681 1682 mytxt = _("Some packages cannot be fetched") 1683 mytxt2 = _("Try to update your repositories and retry") 1684 mytxt3 = "%s: %s" % (brown(_("Error")), bold(str(rc)),) 1685 for txt in (mytxt, mytxt2,): 1686 self.Entropy.updateProgress( 1687 "%s." % (darkred(txt),), 1688 importance = 0, 1689 type = "info", 1690 header = red(" ## ") 1691 ) 1692 self.Entropy.updateProgress( 1693 mytxt3, 1694 importance = 0, 1695 type = "info", 1696 header = red(" ## ") 1697 ) 1698 1699 for repo, branch, fname, cksum, signatures in err_list: 1700 self.Entropy.updateProgress( 1701 "[%s:%s|%s] %s" % (blue(repo), brown(branch), 1702 darkgreen(cksum), darkred(fname),), 1703 importance = 1, 1704 type = "error", 1705 header = darkred(" # ") 1706 ) 1707 1708 return rc
1709
1710 - def fetch_not_available_step(self):
1711 self.Entropy.updateProgress( 1712 blue(_("Package cannot be downloaded, unknown error.")), 1713 importance = 1, 1714 type = "info", 1715 header = red(" ## ") 1716 ) 1717 return 0
1718
1719 - def vanished_step(self):
1720 self.Entropy.updateProgress( 1721 blue(_("Installed package in queue vanished, skipping.")), 1722 importance = 1, 1723 type = "info", 1724 header = red(" ## ") 1725 ) 1726 return 0
1727
1728 - def checksum_step(self):
1729 self.error_on_not_prepared() 1730 return self.match_checksum()
1731
1732 - def multi_checksum_step(self):
1733 self.error_on_not_prepared() 1734 return self.multi_match_checksum()
1735
1736 - def unpack_step(self):
1737 self.error_on_not_prepared() 1738 1739 if not self.pkgmeta['merge_from']: 1740 mytxt = "%s: %s" % ( 1741 blue(_("Unpacking package")), 1742 red(os.path.basename(self.pkgmeta['download'])), 1743 ) 1744 self.Entropy.updateProgress( 1745 mytxt, 1746 importance = 1, 1747 type = "info", 1748 header = red(" ## ") 1749 ) 1750 else: 1751 mytxt = "%s: %s" % ( 1752 blue(_("Merging package")), 1753 red(os.path.basename(self.pkgmeta['atom'])), 1754 ) 1755 self.Entropy.updateProgress( 1756 mytxt, 1757 importance = 1, 1758 type = "info", 1759 header = red(" ## ") 1760 ) 1761 rc = self.__unpack_package() 1762 if rc != 0: 1763 if rc == 512: 1764 errormsg = "%s. %s. %s: 512" % ( 1765 red(_("You are running out of disk space")), 1766 red(_("I bet, you're probably Michele")), 1767 blue(_("Error")), 1768 ) 1769 else: 1770 msg = _("An error occured while trying to unpack the package") 1771 errormsg = "%s. %s. %s: %s" % ( 1772 red(msg), 1773 red(_("Check if your system is healthy")), 1774 blue(_("Error")), 1775 rc, 1776 ) 1777 self.Entropy.updateProgress( 1778 errormsg, 1779 importance = 1, 1780 type = "error", 1781 header = red(" ## ") 1782 ) 1783 return rc
1784
1785 - def install_step(self):
1786 self.error_on_not_prepared() 1787 mytxt = "%s: %s" % ( 1788 blue(_("Installing package")), 1789 red(self.pkgmeta['atom']), 1790 ) 1791 self.Entropy.updateProgress( 1792 mytxt, 1793 importance = 1, 1794 type = "info", 1795 header = red(" ## ") 1796 ) 1797 if self.pkgmeta.get('description'): 1798 mytxt = "[%s]" % (purple(self.pkgmeta.get('description')),) 1799 self.Entropy.updateProgress( 1800 mytxt, 1801 importance = 1, 1802 type = "info", 1803 header = red(" ## ") 1804 ) 1805 1806 rc = self.__install_package() 1807 if rc != 0: 1808 mytxt = "%s. %s. %s: %s" % ( 1809 red(_("An error occured while trying to install the package")), 1810 red(_("Check if your system is healthy")), 1811 blue(_("Error")), 1812 rc, 1813 ) 1814 self.Entropy.updateProgress( 1815 mytxt, 1816 importance = 1, 1817 type = "error", 1818 header = red(" ## ") 1819 ) 1820 return rc
1821
1822 - def remove_step(self):
1823 self.error_on_not_prepared() 1824 mytxt = "%s: %s" % ( 1825 blue(_("Removing data")), 1826 red(self.pkgmeta['removeatom']), 1827 ) 1828 self.Entropy.updateProgress( 1829 mytxt, 1830 importance = 1, 1831 type = "info", 1832 header = red(" ## ") 1833 ) 1834 1835 rc = self.__remove_package() 1836 if rc != 0: 1837 mytxt = "%s. %s. %s: %s" % ( 1838 red(_("An error occured while trying to remove the package")), 1839 red(_("Check if you have enough disk space on your hard disk")), 1840 blue(_("Error")), 1841 rc, 1842 ) 1843 self.Entropy.updateProgress( 1844 mytxt, 1845 importance = 1, 1846 type = "error", 1847 header = red(" ## ") 1848 ) 1849 return rc
1850
1851 - def cleanup_step(self):
1852 self.error_on_not_prepared() 1853 mytxt = "%s: %s" % ( 1854 blue(_("Cleaning")), 1855 red(self.pkgmeta['atom']), 1856 ) 1857 self.Entropy.updateProgress( 1858 mytxt, 1859 importance = 1, 1860 type = "info", 1861 header = red(" ## ") 1862 ) 1863 self._cleanup_package(self.pkgmeta['unpackdir']) 1864 # we don't care if cleanupPackage fails since it's not critical 1865 return 0
1866
1867 - def logmessages_step(self):
1868 for msg in self.pkgmeta['messages']: 1869 self.Entropy.clientLog.write(">>> "+msg) 1870 return 0
1871
1872 - def postinstall_step(self):
1873 self.error_on_not_prepared() 1874 pkgdata = self.pkgmeta['triggers'].get('install') 1875 if pkgdata: 1876 trigger = self.Entropy.Triggers('postinstall', pkgdata, self.action) 1877 do = trigger.prepare() 1878 if do: 1879 trigger.run() 1880 trigger.kill() 1881 del pkgdata 1882 return 0
1883
1884 - def preinstall_step(self):
1885 self.error_on_not_prepared() 1886 pkgdata = self.pkgmeta['triggers'].get('install') 1887 if pkgdata: 1888 1889 trigger = self.Entropy.Triggers('preinstall', pkgdata, self.action) 1890 do = trigger.prepare() 1891 if self.pkgmeta.get("diffremoval") and do: 1892 # diffremoval is true only when the 1893 # removal is triggered by a package install 1894 remdata = self.pkgmeta['triggers'].get('remove') 1895 if remdata: 1896 r_trigger = self.Entropy.Triggers('preremove', remdata, 1897 self.action) 1898 r_trigger.prepare() 1899 r_trigger.triggers = [x for x in trigger.triggers if x \ 1900 not in r_trigger.triggers] 1901 r_trigger.kill() 1902 del remdata 1903 if do: 1904 trigger.run() 1905 trigger.kill() 1906 1907 del pkgdata 1908 return 0
1909
1910 - def preremove_step(self):
1911 self.error_on_not_prepared() 1912 remdata = self.pkgmeta['triggers'].get('remove') 1913 if remdata: 1914 trigger = self.Entropy.Triggers('preremove', remdata, self.action) 1915 do = trigger.prepare() 1916 if do: 1917 trigger.run() 1918 trigger.kill() 1919 del remdata 1920 return 0
1921
1922 - def postremove_step(self):
1923 self.error_on_not_prepared() 1924 remdata = self.pkgmeta['triggers'].get('remove') 1925 if remdata: 1926 1927 trigger = self.Entropy.Triggers('postremove', remdata, self.action) 1928 do = trigger.prepare() 1929 if self.pkgmeta['diffremoval'] and \ 1930 (self.pkgmeta.get("atom") is not None) and do: 1931 # diffremoval is true only when the remove 1932 # action is triggered by installPackages() 1933 pkgdata = self.pkgmeta['triggers'].get('install') 1934 if pkgdata: 1935 i_trigger = self.Entropy.Triggers('postinstall', pkgdata, 1936 self.action) 1937 i_trigger.prepare() 1938 i_trigger.triggers = [x for x in trigger.triggers if x \ 1939 not in i_trigger.triggers] 1940 i_trigger.kill() 1941 del pkgdata 1942 if do: 1943 trigger.run() 1944 trigger.kill() 1945 1946 del remdata 1947 return 0
1948
1949 - def removeconflict_step(self):
1950 self.error_on_not_prepared() 1951 1952 for idpackage in self.pkgmeta['conflicts']: 1953 if not self.Entropy.clientDbconn.isIdpackageAvailable(idpackage): 1954 continue 1955 1956 pkg = self.Entropy.Package() 1957 pkg.prepare((idpackage,), "remove_conflict", 1958 self.pkgmeta['remove_metaopts']) 1959 1960 rc = pkg.run(xterm_header = self.xterm_title) 1961 pkg.kill() 1962 if rc != 0: 1963 return rc 1964 1965 return 0
1966
1967 - def config_step(self):
1968 self.error_on_not_prepared() 1969 1970 mytxt = "%s: %s" % ( 1971 blue(_("Configuring package")), 1972 red(self.pkgmeta['atom']), 1973 ) 1974 self.Entropy.updateProgress( 1975 mytxt, 1976 importance = 1, 1977 type = "info", 1978 header = red(" ## ") 1979 ) 1980 1981 conf_rc = self.__configure_package() 1982 if conf_rc == 1: 1983 mytxt = _("An error occured while trying to configure the package") 1984 mytxt2 = "%s. %s: %s" % ( 1985 red(_("Make sure that your system is healthy")), 1986 blue(_("Error")), 1987 conf_rc, 1988 ) 1989 self.Entropy.updateProgress( 1990 darkred(mytxt), 1991 importance = 1, 1992 type = "error", 1993 header = red(" ## ") 1994 ) 1995 self.Entropy.updateProgress( 1996 mytxt2, 1997 importance = 1, 1998 type = "error", 1999 header = red(" ## ") 2000 ) 2001 2002 elif conf_rc == 2: 2003 mytxt = _("An error occured while trying to configure the package") 2004 mytxt2 = "%s. %s: %s" % ( 2005 red(_("It seems that Source Package Manager entry is missing")), 2006 blue(_("Error")), 2007 conf_rc, 2008 ) 2009 self.Entropy.updateProgress( 2010 darkred(mytxt), 2011 importance = 1, 2012 type = "error", 2013 header = red(" ## ") 2014 ) 2015 self.Entropy.updateProgress( 2016 mytxt2, 2017 importance = 1, 2018 type = "error", 2019 header = red(" ## ") 2020 ) 2021 2022 return conf_rc
2023
2024 - def run_stepper(self, xterm_header):
2025 if xterm_header is None: 2026 xterm_header = "" 2027 2028 if self.pkgmeta.has_key('remove_installed_vanished'): 2029 self.xterm_title += ' %s' % (_("Installed package vanished"),) 2030 self.Entropy.setTitle(self.xterm_title) 2031 rc = self.vanished_step() 2032 return rc 2033 2034 if self.pkgmeta.has_key('fetch_not_available'): 2035 self.xterm_title += ' %s' % (_("Fetch not available"),) 2036 self.Entropy.setTitle(self.xterm_title) 2037 rc = self.fetch_not_available_step() 2038 return rc 2039 2040 def do_fetch(): 2041 self.xterm_title += ' %s: %s' % ( 2042 _("Fetching"), 2043 os.path.basename(self.pkgmeta['download']), 2044 ) 2045 self.Entropy.setTitle(self.xterm_title) 2046 return self.fetch_step()
2047 2048 def do_multi_fetch(): 2049 self.xterm_title += ' %s: %s %s' % (_("Multi Fetching"), 2050 len(self.pkgmeta['multi_fetch_list']), _("packages"),) 2051 self.Entropy.setTitle(self.xterm_title) 2052 return self.multi_fetch_step() 2053 2054 def do_sources_fetch(): 2055 self.xterm_title += ' %s: %s' % ( 2056 _("Fetching sources"), 2057 os.path.basename(self.pkgmeta['atom']),) 2058 self.Entropy.setTitle(self.xterm_title) 2059 return self.sources_fetch_step() 2060 2061 def do_checksum(): 2062 self.xterm_title += ' %s: %s' % (_("Verifying"), 2063 os.path.basename(self.pkgmeta['download']),) 2064 self.Entropy.setTitle(self.xterm_title) 2065 return self.checksum_step() 2066 2067 def do_multi_checksum(): 2068 self.xterm_title += ' %s: %s %s' % (_("Multi Verification"), 2069 len(self.pkgmeta['multi_checksum_list']), _("packages"),) 2070 self.Entropy.setTitle(self.xterm_title) 2071 return self.multi_checksum_step() 2072 2073 def do_unpack(): 2074 if not self.pkgmeta['merge_from']: 2075 mytxt = _("Unpacking") 2076 self.xterm_title += ' %s: %s' % ( 2077 mytxt, 2078 os.path.basename(self.pkgmeta['download']), 2079 ) 2080 else: 2081 mytxt = _("Merging") 2082 self.xterm_title += ' %s: %s' % ( 2083 mytxt, 2084 os.path.basename(self.pkgmeta['atom']), 2085 ) 2086 self.Entropy.setTitle(self.xterm_title) 2087 return self.unpack_step() 2088 2089 def do_remove_conflicts(): 2090 return self.removeconflict_step() 2091 2092 def do_install(): 2093 self.xterm_title += ' %s: %s' % ( 2094 _("Installing"), 2095 self.pkgmeta['atom'], 2096 ) 2097 self.Entropy.setTitle(self.xterm_title) 2098 return self.install_step() 2099 2100 def do_remove(): 2101 self.xterm_title += ' %s: %s' % ( 2102 _("Removing"), 2103 self.pkgmeta['removeatom'], 2104 ) 2105 self.Entropy.setTitle(self.xterm_title) 2106 return self.remove_step() 2107 2108 def do_logmessages(): 2109 return self.logmessages_step() 2110 2111 def do_cleanup(): 2112 self.xterm_title += ' %s: %s' % ( 2113 _("Cleaning"), 2114 self.pkgmeta['atom'], 2115 ) 2116 self.Entropy.setTitle(self.xterm_title) 2117 return self.cleanup_step() 2118 2119 def do_postinstall(): 2120 self.xterm_title += ' %s: %s' % ( 2121 _("Postinstall"), 2122 self.pkgmeta['atom'], 2123 ) 2124 self.Entropy.setTitle(self.xterm_title) 2125 return self.postinstall_step() 2126 2127 def do_preinstall(): 2128 self.xterm_title += ' %s: %s' % ( 2129 _("Preinstall"), 2130 self.pkgmeta['atom'], 2131 ) 2132 self.Entropy.setTitle(self.xterm_title) 2133 return self.preinstall_step() 2134 2135 def do_preremove(): 2136 self.xterm_title += ' %s: %s' % ( 2137 _("Preremove"), 2138 self.pkgmeta['removeatom'], 2139 ) 2140 self.Entropy.setTitle(self.xterm_title) 2141 return self.preremove_step() 2142 2143 def do_postremove(): 2144 self.xterm_title += ' %s: %s' % ( 2145 _("Postremove"), 2146 self.pkgmeta['removeatom'], 2147 ) 2148 self.Entropy.setTitle(self.xterm_title) 2149 return self.postremove_step() 2150 2151 def do_config(): 2152 self.xterm_title += ' %s: %s' % ( 2153 _("Configuring"), 2154 self.pkgmeta['atom'], 2155 ) 2156 self.Entropy.setTitle(self.xterm_title) 2157 return self.config_step() 2158 2159 steps_data = { 2160 "fetch": do_fetch, 2161 "multi_fetch": do_multi_fetch, 2162 "multi_checksum": do_multi_checksum, 2163 "sources_fetch": do_sources_fetch, 2164 "checksum": do_checksum, 2165 "unpack": do_unpack, 2166 "remove_conflicts": do_remove_conflicts, 2167 "install": do_install, 2168 "remove": do_remove, 2169 "logmessages": do_logmessages, 2170 "cleanup": do_cleanup, 2171 "postinstall": do_postinstall, 2172 "preinstall": do_preinstall, 2173 "postremove": do_postremove, 2174 "preremove": do_preremove, 2175 "config": do_config, 2176 } 2177 2178 rc = 0 2179 for step in self.pkgmeta['steps']: 2180 self.xterm_title = xterm_header 2181 rc = steps_data.get(step)() 2182 if rc != 0: 2183 break 2184 return rc 2185 2186
2187 - def run(self, xterm_header = None):
2188 self.error_on_not_prepared() 2189 2190 gave_up = self.Entropy.lock_check(self.Entropy.resources_check_lock) 2191 if gave_up: 2192 return 20 2193 2194 locked = self.Entropy.application_lock_check() 2195 if locked: 2196 return 21 2197 2198 # lock 2199 acquired = self.Entropy.resources_create_lock() 2200 if not acquired: 2201 self.Entropy.updateProgress( 2202 blue(_("Cannot acquire Entropy resources lock.")), 2203 importance = 2, 2204 type = "error", 2205 header = darkred(" ## ") 2206 ) 2207 return 4 # app locked during lock acquire 2208 try: 2209 rc = self.run_stepper(xterm_header) 2210 finally: 2211 self.Entropy.resources_remove_lock() 2212 2213 # remove lock 2214 self.Entropy.resources_remove_lock() 2215 2216 if rc != 0: 2217 self.Entropy.updateProgress( 2218 blue(_("An error occured. Action aborted.")), 2219 importance = 2, 2220 type = "error", 2221 header = darkred(" ## ") 2222 ) 2223 return rc
2224
2225 - def prepare(self, matched_atom, action, metaopts = None):
2226 self.error_on_prepared() 2227 2228 self.check_action_validity(action) 2229 2230 self.action = action 2231 self.matched_atom = matched_atom 2232 2233 if metaopts is None: 2234 metaopts = {} 2235 self.metaopts = metaopts 2236 2237 # generate metadata dictionary 2238 self.generate_metadata()
2239
2240 - def generate_metadata(self):
2241 self.error_on_prepared() 2242 2243 self.check_action_validity(self.action) 2244 2245 if self.action == "fetch": 2246 self.__generate_fetch_metadata() 2247 elif self.action == "multi_fetch": 2248 self.__generate_multi_fetch_metadata() 2249 elif self.action in ("remove", "remove_conflict"): 2250 self.__generate_remove_metadata() 2251 elif self.action == "install": 2252 self.__generate_install_metadata() 2253 elif self.action == "source": 2254 self.__generate_fetch_metadata(sources = True) 2255 elif self.action == "config": 2256 self.__generate_config_metadata() 2257 2258 self.__prepared = True
2259
2260 - def __generate_remove_metadata(self):
2261 2262 self.pkgmeta.clear() 2263 idpackage = self.matched_atom[0] 2264 2265 if not self.Entropy.clientDbconn.isIdpackageAvailable(idpackage): 2266 self.pkgmeta['remove_installed_vanished'] = True 2267 return 0 2268 2269 self.pkgmeta['idpackage'] = idpackage 2270 self.pkgmeta['removeidpackage'] = idpackage 2271 self.pkgmeta['configprotect_data'] = [] 2272 self.pkgmeta['triggers'] = {} 2273 self.pkgmeta['removeatom'] = \ 2274 self.Entropy.clientDbconn.retrieveAtom(idpackage) 2275 self.pkgmeta['slot'] = \ 2276 self.Entropy.clientDbconn.retrieveSlot(idpackage) 2277 self.pkgmeta['versiontag'] = \ 2278 self.Entropy.clientDbconn.retrieveVersionTag(idpackage) 2279 self.pkgmeta['diffremoval'] = False 2280 2281 remove_config = False 2282 if self.metaopts.has_key('removeconfig'): 2283 remove_config = self.metaopts.get('removeconfig') 2284 self.pkgmeta['removeconfig'] = remove_config 2285 2286 self.pkgmeta['removecontent'] = \ 2287 self.Entropy.clientDbconn.retrieveContent(idpackage) 2288 self.pkgmeta['triggers']['remove'] = \ 2289 self.Entropy.clientDbconn.getTriggerInfo(idpackage) 2290 self.pkgmeta['triggers']['remove']['removecontent'] = \ 2291 self.pkgmeta['removecontent'] 2292 self.pkgmeta['triggers']['remove']['accept_license'] = \ 2293 self.Entropy.clientDbconn.retrieveLicensedataKeys(idpackage) 2294 2295 self.pkgmeta['steps'] = [ 2296 "preremove", "remove", "postremove" 2297 ] 2298 2299 return 0
2300
2301 - def __generate_config_metadata(self):
2302 self.pkgmeta.clear() 2303 idpackage = self.matched_atom[0] 2304 2305 self.pkgmeta['atom'] = \ 2306 self.Entropy.clientDbconn.retrieveAtom(idpackage) 2307 key, slot = self.Entropy.clientDbconn.retrieveKeySlot(idpackage) 2308 self.pkgmeta['key'], self.pkgmeta['slot'] = key, slot 2309 self.pkgmeta['version'] = \ 2310 self.Entropy.clientDbconn.retrieveVersion(idpackage) 2311 self.pkgmeta['accept_license'] = \ 2312 self.Entropy.clientDbconn.retrieveLicensedataKeys(idpackage) 2313 self.pkgmeta['steps'] = [] 2314 self.pkgmeta['steps'].append("config") 2315 2316 return 0
2317
2318 - def __generate_install_metadata(self):
2319 self.pkgmeta.clear() 2320 2321 idpackage, repository = self.matched_atom 2322 self.pkgmeta['idpackage'] = idpackage 2323 self.pkgmeta['repository'] = repository 2324 2325 # fetch abort function 2326 if self.metaopts.has_key('fetch_abort_function'): 2327 self.fetch_abort_function = \ 2328 self.metaopts.pop('fetch_abort_function') 2329 2330 install_source = etpConst['install_sources']['unknown'] 2331 meta_inst_source = self.metaopts.get('install_source', install_source) 2332 if meta_inst_source in etpConst['install_sources'].values(): 2333 install_source = meta_inst_source 2334 self.pkgmeta['install_source'] = install_source 2335 2336 self.pkgmeta['configprotect_data'] = [] 2337 dbconn = self.Entropy.open_repository(repository) 2338 self.pkgmeta['triggers'] = {} 2339 self.pkgmeta['atom'] = dbconn.retrieveAtom(idpackage) 2340 self.pkgmeta['slot'] = dbconn.retrieveSlot(idpackage) 2341 2342 ver, tag, rev = dbconn.getVersioningData(idpackage) 2343 self.pkgmeta['version'] = ver 2344 self.pkgmeta['versiontag'] = tag 2345 self.pkgmeta['revision'] = rev 2346 2347 self.pkgmeta['category'] = dbconn.retrieveCategory(idpackage) 2348 self.pkgmeta['download'] = dbconn.retrieveDownloadURL(idpackage) 2349 self.pkgmeta['name'] = dbconn.retrieveName(idpackage) 2350 self.pkgmeta['messages'] = dbconn.retrieveMessages(idpackage) 2351 self.pkgmeta['checksum'] = dbconn.retrieveDigest(idpackage) 2352 sha1, sha256, sha512 = dbconn.retrieveSignatures(idpackage) 2353 signatures = { 2354 'sha1': sha1, 2355 'sha256': sha256, 2356 'sha512': sha512, 2357 } 2358 self.pkgmeta['signatures'] = signatures 2359 self.pkgmeta['accept_license'] = \ 2360 dbconn.retrieveLicensedataKeys(idpackage) 2361 self.pkgmeta['conflicts'] = \ 2362 self.Entropy.get_match_conflicts(self.matched_atom) 2363 2364 description = dbconn.retrieveDescription(idpackage) 2365 if description: 2366 if len(description) > 74: 2367 description = description[:74].strip() 2368 description += "..." 2369 self.pkgmeta['description'] = description 2370 2371 # fill action queue 2372 self.pkgmeta['removeidpackage'] = -1 2373 removeConfig = False 2374 if self.metaopts.has_key('removeconfig'): 2375 removeConfig = self.metaopts.get('removeconfig') 2376 2377 self.pkgmeta['remove_metaopts'] = { 2378 'removeconfig': True, 2379 } 2380 if self.metaopts.has_key('remove_metaopts'): 2381 self.pkgmeta['remove_metaopts'] = \ 2382 self.metaopts.get('remove_metaopts') 2383 2384 self.pkgmeta['merge_from'] = None 2385 mf = self.metaopts.get('merge_from') 2386 if mf != None: 2387 self.pkgmeta['merge_from'] = unicode(mf) 2388 self.pkgmeta['removeconfig'] = removeConfig 2389 2390 pkgkey = entropy.tools.dep_getkey(self.pkgmeta['atom']) 2391 inst_idpackage, inst_rc = self.Entropy.clientDbconn.atomMatch(pkgkey, 2392 matchSlot = self.pkgmeta['slot']) 2393 2394 # filled later... 2395 self.pkgmeta['removecontent'] = set() 2396 self.pkgmeta['removeidpackage'] = inst_idpackage 2397 2398 if self.pkgmeta['removeidpackage'] != -1: 2399 avail = self.Entropy.clientDbconn.isIdpackageAvailable( 2400 self.pkgmeta['removeidpackage']) 2401 if avail: 2402 inst_atom = self.Entropy.clientDbconn.retrieveAtom( 2403 self.pkgmeta['removeidpackage']) 2404 self.pkgmeta['removeatom'] = inst_atom 2405 else: 2406 self.pkgmeta['removeidpackage'] = -1 2407 2408 # smartpackage ? 2409 self.pkgmeta['smartpackage'] = False 2410 # set unpack dir and image dir 2411 if self.pkgmeta['repository'].endswith(etpConst['packagesext']): 2412 2413 # FIXME: add repository arch metadata to entropy.db 2414 # do arch check 2415 compiled_arch = dbconn.retrieveDownloadURL(idpackage) 2416 if compiled_arch.find("/"+etpSys['arch']+"/") == -1: 2417 self.pkgmeta.clear() 2418 self.__prepared = False 2419 return -1 2420 2421 repo_data = self.Entropy.SystemSettings['repositories'] 2422 repo_meta = repo_data['available'][self.pkgmeta['repository']] 2423 self.pkgmeta['smartpackage'] = repo_meta['smartpackage'] 2424 self.pkgmeta['pkgpath'] = repo_meta['pkgpath'] 2425 2426 else: 2427 self.pkgmeta['pkgpath'] = etpConst['entropyworkdir'] + \ 2428 os.path.sep + self.pkgmeta['download'] 2429 2430 self.pkgmeta['unpackdir'] = etpConst['entropyunpackdir'] + \ 2431 os.path.sep + self.pkgmeta['download'] 2432 2433 self.pkgmeta['imagedir'] = etpConst['entropyunpackdir'] + \ 2434 os.path.sep + self.pkgmeta['download'] + os.path.sep + \ 2435 etpConst['entropyimagerelativepath'] 2436 2437 self.pkgmeta['pkgdbpath'] = os.path.join(self.pkgmeta['unpackdir'], 2438 "edb/pkg.db") 2439 2440 # compare both versions and if they match, disable removeidpackage 2441 if self.pkgmeta['removeidpackage'] != -1: 2442 2443 # differential remove list 2444 self.pkgmeta['diffremoval'] = True 2445 self.pkgmeta['removeatom'] = \ 2446 self.Entropy.clientDbconn.retrieveAtom( 2447 self.pkgmeta['removeidpackage']) 2448 2449 self.pkgmeta['triggers']['remove'] = \ 2450 self.Entropy.clientDbconn.getTriggerInfo( 2451 self.pkgmeta['removeidpackage'] 2452 ) 2453 self.pkgmeta['triggers']['remove']['removecontent'] = \ 2454 self.pkgmeta['removecontent'] # pass reference, not copy! nevva! 2455 self.pkgmeta['triggers']['remove']['accept_license'] = \ 2456 self.Entropy.clientDbconn.retrieveLicensedataKeys( 2457 self.pkgmeta['removeidpackage']) 2458 2459 # set steps 2460 self.pkgmeta['steps'] = [] 2461 if self.pkgmeta['conflicts']: 2462 self.pkgmeta['steps'].append("remove_conflicts") 2463 # install 2464 self.pkgmeta['steps'].append("unpack") 2465 # preinstall placed before preremove in order 2466 # to respect Spm order 2467 self.pkgmeta['steps'].append("preinstall") 2468 if (self.pkgmeta['removeidpackage'] != -1): 2469 self.pkgmeta['steps'].append("preremove") 2470 self.pkgmeta['steps'].append("install") 2471 if (self.pkgmeta['removeidpackage'] != -1): 2472 self.pkgmeta['steps'].append("postremove") 2473 self.pkgmeta['steps'].append("postinstall") 2474 self.pkgmeta['steps'].append("logmessages") 2475 self.pkgmeta['steps'].append("cleanup") 2476 2477 self.pkgmeta['triggers']['install'] = dbconn.getTriggerInfo(idpackage) 2478 self.pkgmeta['triggers']['install']['accept_license'] = \ 2479 self.pkgmeta['accept_license'] 2480 self.pkgmeta['triggers']['install']['unpackdir'] = \ 2481 self.pkgmeta['unpackdir'] 2482 self.pkgmeta['triggers']['install']['imagedir'] = \ 2483 self.pkgmeta['imagedir'] 2484 2485 # FIXME: move to entropy.spm 2486 2487 self.pkgmeta['xpakpath'] = etpConst['entropyunpackdir'] + \ 2488 os.path.sep + self.pkgmeta['download'] + os.path.sep + \ 2489 etpConst['entropyxpakrelativepath'] 2490 2491 if not self.pkgmeta['merge_from']: 2492 self.pkgmeta['xpakstatus'] = None 2493 self.pkgmeta['xpakdir'] = self.pkgmeta['xpakpath'] + \ 2494 os.path.sep + etpConst['entropyxpakdatarelativepath'] 2495 2496 else: 2497 self.pkgmeta['xpakstatus'] = True 2498 portdbdir = 'var/db/pkg' # XXX hard coded ? 2499 portdbdir = os.path.join(self.pkgmeta['merge_from'], portdbdir) 2500 portdbdir = os.path.join(portdbdir, self.pkgmeta['category']) 2501 portdbdir = os.path.join(portdbdir, self.pkgmeta['name'] + "-" + \ 2502 self.pkgmeta['version']) 2503 2504 self.pkgmeta['xpakdir'] = portdbdir 2505 2506 self.pkgmeta['triggers']['install']['xpakdir'] = \ 2507 self.pkgmeta['xpakdir'] 2508 2509 2510 return 0
2511
2512 - def __generate_fetch_metadata(self, sources = False):
2513 self.pkgmeta.clear() 2514 2515 idpackage, repository = self.matched_atom 2516 dochecksum = True 2517 2518 # fetch abort function 2519 if self.metaopts.has_key('fetch_abort_function'): 2520 self.fetch_abort_function = \ 2521 self.metaopts.pop('fetch_abort_function') 2522 2523 if self.metaopts.has_key('dochecksum'): 2524 dochecksum = self.metaopts.get('dochecksum') 2525 2526 # fetch_path is the path where data should be downloaded 2527 # at the moment is implemented only for sources = True 2528 if self.metaopts.has_key('fetch_path'): 2529 fetch_path = self.metaopts.get('fetch_path') 2530 if entropy.tools.is_valid_path(fetch_path): 2531 self.pkgmeta['fetch_path'] = fetch_path 2532 2533 self.pkgmeta['repository'] = repository 2534 self.pkgmeta['idpackage'] = idpackage 2535 dbconn = self.Entropy.open_repository(repository) 2536 self.pkgmeta['atom'] = dbconn.retrieveAtom(idpackage) 2537 if sources: 2538 self.pkgmeta['download'] = dbconn.retrieveSources(idpackage, 2539 extended = True) 2540 else: 2541 self.pkgmeta['checksum'] = dbconn.retrieveDigest(idpackage) 2542 sha1, sha256, sha512 = dbconn.retrieveSignatures(idpackage) 2543 signatures = { 2544 'sha1': sha1, 2545 'sha256': sha256, 2546 'sha512': sha512, 2547 } 2548 self.pkgmeta['signatures'] = signatures 2549 self.pkgmeta['download'] = dbconn.retrieveDownloadURL(idpackage) 2550 2551 if not self.pkgmeta['download']: 2552 self.pkgmeta['fetch_not_available'] = True 2553 return 0 2554 2555 self.pkgmeta['verified'] = False 2556 self.pkgmeta['steps'] = [] 2557 if not repository.endswith(etpConst['packagesext']) and not sources: 2558 dl_check = self.Entropy.check_needed_package_download( 2559 self.pkgmeta['download'], None) 2560 2561 if dl_check < 0: 2562 self.pkgmeta['steps'].append("fetch") 2563 if dochecksum: 2564 self.pkgmeta['steps'].append("checksum") 2565 2566 elif sources: 2567 self.pkgmeta['steps'].append("sources_fetch") 2568 2569 if sources: 2570 # create sources destination directory 2571 unpack_dir = os.path.join(etpConst['entropyunpackdir'], 2572 "sources", self.pkgmeta['atom']) 2573 self.pkgmeta['unpackdir'] = unpack_dir 2574 2575 if not self.pkgmeta.get('fetch_path'): 2576 if os.path.lexists(unpack_dir): 2577 if os.path.isfile(unpack_dir): 2578 os.remove(unpack_dir) 2579 elif os.path.isdir(unpack_dir): 2580 shutil.rmtree(unpack_dir, True) 2581 if not os.path.lexists(unpack_dir): 2582 os.makedirs(unpack_dir, 0775) 2583 const_setup_perms(unpack_dir, etpConst['entropygid']) 2584 return 0 2585 2586 # downloading binary package 2587 # if file exists, first checksum then fetch 2588 down_path = os.path.join(etpConst['entropyworkdir'], 2589 self.pkgmeta['download']) 2590 if os.access(down_path, os.R_OK | os.F_OK): 2591 # check size first 2592 repo_size = dbconn.retrieveSize(idpackage) 2593 f = open(down_path, "r") 2594 f.seek(0, os.SEEK_END) 2595 disk_size = f.tell() 2596 f.close() 2597 if repo_size == disk_size: 2598 self.pkgmeta['steps'].reverse() 2599 2600 return 0
2601
2602 - def __generate_multi_fetch_metadata(self):
2603 self.pkgmeta.clear() 2604 2605 if not isinstance(self.matched_atom, list): 2606 raise IncorrectParameter("IncorrectParameter: " 2607 "matched_atom must be a list of tuples, not %s" % ( 2608 type(self.matched_atom,) 2609 ) 2610 ) 2611 2612 dochecksum = True 2613 2614 # meta options 2615 if self.metaopts.has_key('fetch_abort_function'): 2616 self.fetch_abort_function = \ 2617 self.metaopts.pop('fetch_abort_function') 2618 2619 if self.metaopts.has_key('dochecksum'): 2620 dochecksum = self.metaopts.get('dochecksum') 2621 self.pkgmeta['checksum'] = dochecksum 2622 2623 matches = self.matched_atom 2624 self.pkgmeta['matches'] = matches 2625 self.pkgmeta['atoms'] = [] 2626 self.pkgmeta['repository_atoms'] = {} 2627 temp_fetch_list = [] 2628 temp_checksum_list = [] 2629 temp_already_downloaded_count = 0 2630 etp_workdir = etpConst['entropyworkdir'] 2631 for idpackage, repository in matches: 2632 2633 if repository.endswith(etpConst['packagesext']): 2634 continue 2635 2636 dbconn = self.Entropy.open_repository(repository) 2637 myatom = dbconn.retrieveAtom(idpackage) 2638 2639 # general purpose metadata 2640 self.pkgmeta['atoms'].append(myatom) 2641 if not self.pkgmeta['repository_atoms'].has_key(repository): 2642 self.pkgmeta['repository_atoms'][repository] = set() 2643 self.pkgmeta['repository_atoms'][repository].add(myatom) 2644 2645 download = dbconn.retrieveDownloadURL(idpackage) 2646 digest = dbconn.retrieveDigest(idpackage) 2647 2648 sha1, sha256, sha512 = dbconn.retrieveSignatures(idpackage) 2649 signatures = { 2650 'sha1': sha1, 2651 'sha256': sha256, 2652 'sha512': sha512, 2653 } 2654 2655 repo_size = dbconn.retrieveSize(idpackage) 2656 orig_branch = self.Entropy.get_branch_from_download_relative_uri( 2657 download) 2658 if self.Entropy.check_needed_package_download(download, None) < 0: 2659 obj = (repository, orig_branch, download, digest, signatures,) 2660 temp_fetch_list.append(obj) 2661 continue 2662 2663 elif dochecksum: 2664 obj = (repository, orig_branch, download, digest, signatures,) 2665 temp_checksum_list.append(obj) 2666 2667 down_path = os.path.join(etp_workdir, download) 2668 if os.path.isfile(down_path): 2669 with open(down_path, "r") as f: 2670 f.seek(0, os.SEEK_END) 2671 disk_size = f.tell() 2672 if repo_size == disk_size: 2673 temp_already_downloaded_count += 1 2674 2675 self.pkgmeta['steps'] = [] 2676 self.pkgmeta['multi_fetch_list'] = temp_fetch_list 2677 self.pkgmeta['multi_checksum_list'] = temp_checksum_list 2678 if self.pkgmeta['multi_fetch_list']: 2679 self.pkgmeta['steps'].append("multi_fetch") 2680 if self.pkgmeta['multi_checksum_list']: 2681 self.pkgmeta['steps'].append("multi_checksum") 2682 if temp_already_downloaded_count == len(temp_checksum_list): 2683 self.pkgmeta['steps'].reverse() 2684 2685 return 0
2686