home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / bin / unattended-upgrade < prev    next >
Encoding:
Text File  |  2007-05-02  |  9.8 KB  |  270 lines

  1. #!/usr/bin/python
  2. #
  3. # (c) 2005 Canonical
  4. # Author: Michael Vogt <michael.vogt@ubuntu.com>
  5. #
  6. # Released under the GPL
  7.  
  8. import apt_pkg, apt_inst
  9. import sys, os, string, datetime
  10. from optparse import OptionParser
  11. from subprocess import Popen, PIPE
  12.  
  13. import warnings
  14. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  15. import apt
  16. import logging
  17. import subprocess
  18.  
  19. class MyCache(apt.Cache):
  20.     def __init__(self):
  21.         apt.Cache.__init__(self)
  22.     def clear(self):
  23.         self._depcache.Init()
  24.         assert self._depcache.InstCount == 0 and \
  25.                self._depcache.BrokenCount == 0 \
  26.                and self._depcache.DelCount == 0
  27.         
  28.  
  29. def is_allowed_origin(pkg, allowed_origins):
  30.     if not pkg.candidateOrigin:
  31.         return False
  32.     for origin in pkg.candidateOrigin:
  33.         for allowed in allowed_origins:
  34.             if origin.origin == allowed[0] and origin.archive == allowed[1]:
  35.                 return True
  36.     return False
  37.  
  38. def check_changes_for_sanity(cache, allowed_origins, blacklist):
  39.     if cache._depcache.BrokenCount != 0:
  40.         return False
  41.     for pkg in cache:
  42.         if pkg.markedDelete:
  43.             return False
  44.         if pkg.markedInstall or pkg.markedUpgrade:
  45.             if not is_allowed_origin(pkg, allowed_origins):
  46.                 return False
  47.             if pkg.name in blacklist:
  48.                 return False
  49.     return True
  50.  
  51. def pkgname_from_deb(debfile):
  52.     # FIXME: add error checking here
  53.     control = apt_inst.debExtractControl(open(debfile))
  54.     sections = apt_pkg.ParseSection(control)
  55.     return sections["Package"]
  56.  
  57. def conffile_prompt(destFile):
  58.     logging.debug("check_conffile_prompt('%s')" % destFile)
  59.     pkgname = pkgname_from_deb(destFile)
  60.     status_file = apt_pkg.Config.Find("Dir::State::status")
  61.     parse = apt_pkg.ParseTagFile(open(status_file,"r"))
  62.     while parse.Step() == 1:
  63.         if parse.Section.get("Package") == pkgname:
  64.             logging.debug("found pkg: %s" % pkgname)
  65.             if parse.Section.has_key("Conffiles"):
  66.                 conffiles = parse.Section.get("Conffiles")
  67.                 # Conffiles:
  68.                 # /etc/bash_completion.d/m-a c7780fab6b14d75ca54e11e992a6c11c
  69.                 for line in string.split(conffiles,"\n"):
  70.                     logging.debug("conffile line: %s", line)
  71.                     l = string.split(string.strip(line))
  72.                     file = l[0]
  73.                     md5 = l[1]
  74.                     if len(l) > 2:
  75.                         obs = l[2]
  76.                     else:
  77.                         obs = None
  78.                     if os.path.exists(file) and obs != "obsolete":
  79.                         current_md5 = apt_pkg.md5sum(open(file).read())
  80.                         if current_md5 != md5:
  81.                             return True
  82.     return False
  83.  
  84.  
  85. if __name__ == "__main__":
  86.  
  87.     if os.getuid() != 0:
  88.         print "You need to be root to run this application"
  89.         sys.exit(1)
  90.  
  91.     # init the logging
  92.     logdir = apt_pkg.Config.FindDir("APT::UnattendedUpgrades::LogDir",
  93.                                     "/var/log/unattended-upgrades/")
  94.     logfile = logdir+apt_pkg.Config.Find("APT::UnattendedUpgrades::LogFile",
  95.                                          "unattended-upgrades.log")
  96.     logging.basicConfig(level=logging.INFO,
  97.                         format='%(asctime)s %(levelname)s %(message)s',
  98.                         filename=logfile)
  99.  
  100.     # init the options
  101.     parser = OptionParser()
  102.     parser.add_option("-d", "--debug",
  103.                       action="store_true", dest="debug", default=False,
  104.                       help="print debug messages")
  105.     (options, args) = parser.parse_args()
  106.     if options.debug:
  107.         logging.getLogger().setLevel(logging.DEBUG)
  108.         pass
  109.  
  110.     #dldir = "/tmp/pyapt-test"
  111.     #try:
  112.     #    os.mkdir(dldir)
  113.     #    os.mkdir(dldir+"/partial")
  114.     #except OSError:
  115.     #    pass
  116.     #apt_pkg.Config.Set("Dir::Cache::archives",dldir)
  117.  
  118.     # format (origin, archive), e.g. ("Ubuntu","dapper-security")
  119.     allowed_origins = map(string.split, apt_pkg.Config.ValueList("Unattended-Upgrade::Allowed-Origins"))
  120.  
  121.     # pkgs that are (for some reason) not save to install
  122.     blacklisted_pkgs = apt_pkg.Config.ValueList("Unattended-Upgrade::Package-Blacklist")
  123.     logging.info("Initial blacklisted packages: %s", "".join(blacklisted_pkgs))
  124.  
  125.     logging.info("Starting unattended upgrades script")
  126.  
  127.     # display available origin
  128.     logging.info("Allowed origins are: %s" % map(str,allowed_origins))
  129.     
  130.     # get a cache
  131.     cache = MyCache()
  132.  
  133.     # find out about the packages that are upgradable (in a allowed_origin)
  134.     pkgs_to_upgrade = []
  135.     for pkg in cache:
  136.         if options.debug and pkg.isUpgradable:
  137.             logging.debug("Checking: %s (%s)" % (pkg.name,map(str, pkg.candidateOrigin)))
  138.         if pkg.isUpgradable and \
  139.                is_allowed_origin(pkg,allowed_origins):
  140.             try:
  141.                 pkg.markUpgrade()
  142.                 if check_changes_for_sanity(cache, allowed_origins,
  143.                                             blacklisted_pkgs):
  144.                     pkgs_to_upgrade.append(pkg)
  145.             except SystemError:
  146.                 # can't upgrade
  147.                 pass
  148.             else:
  149.                 cache.clear()
  150.                 for pkg2 in pkgs_to_upgrade:
  151.                     pkg2.markUpgrade()
  152.  
  153.     pkgs = "\n".join([pkg.name for pkg in pkgs_to_upgrade])
  154.     logging.debug("pkgs that look like they should be upgraded: %s" % pkgs)
  155.            
  156.     # download what looks good
  157.     if options.debug:
  158.         fetcher = apt_pkg.GetAcquire(apt.progress.TextFetchProgress())
  159.     else:
  160.         fetcher = apt_pkg.GetAcquire()
  161.     list = apt_pkg.GetPkgSourceList()
  162.     list.ReadMainList()
  163.     recs = cache._records
  164.     pm = apt_pkg.GetPackageManager(cache._depcache)
  165.     try:
  166.         pm.GetArchives(fetcher,list,recs)
  167.     except SystemError, e:
  168.         logging.error("GetArchives() failed: '%s'" % e)
  169.     res = fetcher.Run()
  170.  
  171.     # now check the downloaded debs for conffile conflicts and build
  172.     # a blacklist
  173.     for item in fetcher.Items:
  174.         logging.debug("%s" % item)
  175.         if item.Status == item.StatError:
  176.             print "A error ocured: '%s'" % item.ErrorText
  177.         if item.Complete == False:
  178.             print "The URI '%s' failed to download, aborting" % item.DescURI
  179.             sys.exit(1)
  180.         if item.IsTrusted == False:
  181.             blacklisted_pkgs.append(pkgname_from_deb(item.DestFile))
  182.         if conffile_prompt(item.DestFile):
  183.             # FIXME: skip package (means to re-run the whole marking again
  184.             # and making sure that the package will not be pulled in by
  185.             # some other package again!
  186.             logging.debug("pkg '%s' has conffile prompt" % pkgname_from_deb(item.DestFile))
  187.             blacklisted_pkgs.append(pkgname_from_deb(item.DestFile))
  188.  
  189.  
  190.     # redo the selection about the packages to upgrade based on the new
  191.     # blacklist
  192.     logging.debug("blacklist: %s" % blacklisted_pkgs)
  193.     # find out about the packages that are upgradable (in a allowed_origin)
  194.     if len(blacklisted_pkgs) > 0:
  195.         cache.clear()
  196.         old_pkgs_to_upgrade = pkgs_to_upgrade[:]
  197.         pkgs_to_upgrade = []
  198.         for pkg in old_pkgs_to_upgrade:
  199.             logging.debug("Checking (blacklist): %s" % (pkg.name))
  200.             pkg.markUpgrade()
  201.             if check_changes_for_sanity(cache, allowed_origins,
  202.                                         blacklisted_pkgs):
  203.                  pkgs_to_upgrade.append(pkg)
  204.             else:
  205.                 logging.info("package '%s' not upgraded" % pkg.name)
  206.                 cache.clear()
  207.                 for pkg2 in pkgs_to_upgrade:
  208.                     pkg2.markUpgrade()
  209.  
  210.     logging.debug("InstCount=%i DelCount=%i BrokenCout=%i" % (cache._depcache.InstCount, cache._depcache.DelCount, cache._depcache.BrokenCount))
  211.  
  212.     # check what we have
  213.     if len(pkgs_to_upgrade) == 0:
  214.         logging.info("No packages found that can be upgraded unattended")
  215.         sys.exit(0)    
  216.  
  217.     # do the install based on the new list of pkgs
  218.     pkgs = " ".join([pkg.name for pkg in pkgs_to_upgrade])
  219.     logging.info("Packages that are upgraded: %s" % pkgs)
  220.  
  221.     # set debconf to NON_INTERACTIVE, redirect output
  222.     os.putenv("DEBIAN_FRONTEND","noninteractive");
  223.     os.putenv("APT_LISTCHANGES_FRONTEND","none");
  224.     
  225.     # redirect to log
  226.     REDIRECT_INPUT = os.devnull
  227.     fd = os.open(REDIRECT_INPUT, os.O_RDWR)
  228.     os.dup2(fd,0)
  229.  
  230.     now = datetime.datetime.now()
  231.     logfile_dpkg = logdir+'unattended-upgrades-dpkg_%s.log' % now.isoformat('_')
  232.     logging.info("Writing dpkg log to '%s'" % logfile_dpkg)
  233.     fd = os.open(logfile_dpkg,os.O_RDWR|os.O_CREAT)
  234.     os.dup2(fd,1)
  235.     os.dup2(fd,2)
  236.  
  237.     # create a new package-manager. the blacklist may have changed
  238.     # the markings in the depcache
  239.     if options.debug:
  240.         apt_pkg.Config.Set("Debug::pkgDPkgPM","1")
  241.     #apt_pkg.Config.Set("Debug::pkgDPkgPM","1")    
  242.     pm = apt_pkg.GetPackageManager(cache._depcache)
  243.     pm.GetArchives(fetcher,list,recs)
  244.     try:
  245.         res = pm.DoInstall()
  246.     except SystemError,e:
  247.         logging.error("Installing the upgrades failed!")
  248.         logging.error("error message: '%s'" % e)
  249.         res = False
  250.                 
  251.     if res == pm.ResultFailed:
  252.         logging.error("dpkg returned a error! See '%s' for details" % logfile_dpkg)
  253.     else:
  254.         logging.info("All upgrades installed")
  255.  
  256.     # check if we need to send a mail
  257.     email = apt_pkg.Config.Find("Unattended-Upgrade::Mail", "")
  258.     if email != "":
  259.         logging.debug("Sending mail with '%s' to '%s'" % (logfile_dpkg, email))
  260.         mail = subprocess.Popen(["mail",
  261.                                  "-s","unattended-upgrades result",email],
  262.                                 stdin=subprocess.PIPE)
  263.         s = "Unattended upgrade returned: %s\n\n" % (res != pm.ResultFailed)
  264.         s += open(logfile_dpkg).read()
  265.         mail.stdin.write(s)
  266.         mail.stdin.close()
  267.         ret = mail.wait()
  268.         logging.debug("mail returned: %s" % ret)
  269.     
  270.