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