Linux lhjmq-records 5.15.0-118-generic #128-Ubuntu SMP Fri Jul 5 09:28:59 UTC 2024 x86_64
Your IP : 18.117.104.132
# DistUpgradeFetcherCore.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2006 Canonical
#
# Author: Michael Vogt <michael.vogt@ubuntu.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from string import Template
import os
import apt_pkg
import logging
import tarfile
import tempfile
import shutil
import socket
import sys
import subprocess
from gettext import gettext as _
from aptsources.sourceslist import SourcesList
from urllib.request import urlopen
from urllib.error import HTTPError
from .utils import get_dist, url_downloadable, country_mirror
from .DistUpgradeViewText import readline
class DistUpgradeFetcherCore(object):
" base class (without GUI) for the upgrade fetcher "
DEFAULT_MIRROR = "http://archive.ubuntu.com/ubuntu"
DEFAULT_COMPONENT = "main"
DEBUG = "DEBUG_UPDATE_MANAGER" in os.environ
def __init__(self, new_dist, progress):
self.new_dist = new_dist
self.current_dist_name = get_dist()
self._progress = progress
# options to pass to the release upgrader when it is run
self.run_options = []
def _debug(self, msg):
" helper to show debug information "
if self.DEBUG:
sys.stderr.write(msg + "\n")
def showReleaseNotes(self):
if '--frontend=DistUpgradeViewNonInteractive' in self.run_options:
return True
if self.new_dist.releaseNotesURI is not None:
uri = self._expandUri(self.new_dist.releaseNotesURI)
timeout = socket.getdefaulttimeout()
try:
socket.setdefaulttimeout(5)
release_notes = urlopen(uri)
notes = release_notes.read().decode("UTF-8", "replace")
except HTTPError:
self.error(_("Could not find the release announcement"),
_("The server may be overloaded."))
return False
except IOError:
self.error(_("Could not download the release announcement"),
_("Please check your internet connection."))
return False
socket.setdefaulttimeout(timeout)
print()
print(notes)
print(_("Continue [yN] "), end="")
res = readline()
if res.strip().lower().startswith(_("y")):
return True
return False
def error(self, summary, message):
""" minimal implementation for error display, should be overwriten
by subclasses that want to more fancy method
"""
print(summary)
print(message)
return False
def authenticate(self):
if self.new_dist.upgradeToolSig:
f = self.tmpdir + "/" + os.path.basename(self.new_dist.upgradeTool)
sig = self.tmpdir + "/" + os.path.basename(
self.new_dist.upgradeToolSig)
print(_("authenticate '%(file)s' against '%(signature)s' ") % {
'file': os.path.basename(f),
'signature': os.path.basename(sig)})
if self.gpgauthenticate(f, sig):
return True
return False
def gpgauthenticate(self, file, signature,
keyring=None):
""" authenticated a file against a given signature, if no keyring
is given use the apt default keyring
"""
gpg = ["apt-key"]
if not keyring:
keyring = '/usr/share/keyrings/ubuntu-archive-keyring.gpg'
gpg += ["--keyring", keyring]
gpg += ["verify", signature, file]
ret = subprocess.call(gpg, stderr=subprocess.PIPE)
return ret == 0
def extractDistUpgrader(self):
# extract the tarball
fname = os.path.join(self.tmpdir, os.path.basename(self.uri))
print(_("extracting '%s'") % os.path.basename(fname))
if not os.path.exists(fname):
return False
try:
tar = tarfile.open(self.tmpdir + "/" +
os.path.basename(self.uri), "r")
for tarinfo in tar:
tar.extract(tarinfo)
tar.close()
except tarfile.ReadError as e:
logging.error("failed to open tarfile (%s)" % e)
return False
return True
def verifyDistUprader(self):
# FIXME: check an internal dependency file to make sure
# that the script will run correctly
# see if we have a script file that we can run
self.script = script = "%s/%s" % (self.tmpdir, self.new_dist.name)
if not os.path.exists(script):
return self.error(_("Could not run the upgrade tool"),
_("Could not run the upgrade tool") + ". " +
_("This is most likely a bug in the upgrade "
"tool. Please report it as a bug using the "
"command 'ubuntu-bug "
"ubuntu-release-upgrader-core'."))
return True
def mirror_from_sources_list(self, uri, default_uri):
"""
try to figure what the mirror is from current sources.list
do this by looing for matching DEFAULT_COMPONENT, current dist
in sources.list and then doing a http HEAD/ftp size request
to see if the uri is available on this server
"""
self._debug("mirror_from_sources_list: %s" % self.current_dist_name)
sources = SourcesList(withMatcher=False)
seen = set()
for e in sources.list:
if e.disabled or e.invalid or not e.type == "deb":
continue
# check if we probed this mirror already
if e.uri in seen:
continue
# we are using the main mirror already, so we are fine
if (e.uri.startswith(default_uri) and
e.dist == self.current_dist_name and
self.DEFAULT_COMPONENT in e.comps):
return uri
elif (e.dist == self.current_dist_name and "main" in e.comps):
mirror_uri = e.uri + uri[len(default_uri):]
if url_downloadable(mirror_uri, self._debug):
return mirror_uri
seen.add(e.uri)
self._debug("no mirror found")
return ""
def _expandUri(self, uri):
"""
expand the uri so that it uses a mirror if the url starts
with a well known string (like archive.ubuntu.com)
"""
# try to guess the mirror from the sources.list
if uri.startswith(self.DEFAULT_MIRROR):
self._debug("trying to find suitable mirror")
new_uri = self.mirror_from_sources_list(uri, self.DEFAULT_MIRROR)
if new_uri:
return new_uri
# if that fails, use old method
uri_template = Template(uri)
m = country_mirror()
new_uri = uri_template.safe_substitute(countrymirror=m)
# be paranoid and check if the given uri is really downloadable
try:
if not url_downloadable(new_uri, self._debug):
raise Exception("failed to download %s" % new_uri)
except Exception as e:
self._debug("url '%s' could not be downloaded" % e)
# else fallback to main server
new_uri = uri_template.safe_substitute(countrymirror='')
return new_uri
def fetchDistUpgrader(self):
" download the tarball with the upgrade script "
tmpdir = tempfile.mkdtemp(prefix="ubuntu-release-upgrader-")
self.tmpdir = tmpdir
os.chdir(tmpdir)
logging.debug("using tmpdir: '%s'" % tmpdir)
# turn debugging on here (if required)
if self.DEBUG > 0:
apt_pkg.config.set("Debug::Acquire::http", "1")
apt_pkg.config.set("Debug::Acquire::ftp", "1")
#os.listdir(tmpdir)
fetcher = apt_pkg.Acquire(self._progress)
if self.new_dist.upgradeToolSig is not None:
uri = self._expandUri(self.new_dist.upgradeToolSig)
af1 = apt_pkg.AcquireFile(fetcher,
uri,
descr=_("Upgrade tool signature"))
# reference it here to shut pyflakes up
af1
if self.new_dist.upgradeTool is not None:
self.uri = self._expandUri(self.new_dist.upgradeTool)
af2 = apt_pkg.AcquireFile(fetcher,
self.uri,
descr=_("Upgrade tool"))
# reference it here to shut pyflakes up
af2
result = fetcher.run()
if result != fetcher.RESULT_CONTINUE:
logging.warning("fetch result != continue (%s)" % result)
return False
# check that both files are really there and non-null
for f in [os.path.basename(self.new_dist.upgradeToolSig),
os.path.basename(self.new_dist.upgradeTool)]:
if not (os.path.exists(f) and os.path.getsize(f) > 0):
logging.warning("file '%s' missing" % f)
return False
return True
return False
def runDistUpgrader(self):
args = [self.script] + self.run_options
if os.getuid() != 0:
os.execv("/usr/bin/sudo", ["sudo", "-E"] + args)
else:
os.execv(self.script, args)
def cleanup(self):
# cleanup
os.chdir("..")
# del tmpdir
shutil.rmtree(self.tmpdir)
def run(self):
# see if we have release notes
if not self.showReleaseNotes():
return
if not self.fetchDistUpgrader():
self.error(_("Failed to fetch"),
_("Fetching the upgrade failed. There may be a network "
"problem. "))
return
if not self.authenticate():
self.error(_("Authentication failed"),
_("Authenticating the upgrade failed. There may be a "
"problem with the network or with the server. "))
self.cleanup()
return
if not self.extractDistUpgrader():
self.error(_("Failed to extract"),
_("Extracting the upgrade failed. There may be a "
"problem with the network or with the server. "))
return
if not self.verifyDistUprader():
self.error(_("Verification failed"),
_("Verifying the upgrade failed. There may be a "
"problem with the network or with the server. "))
self.cleanup()
return
try:
# check if we can execute, if we run it via sudo we will
# not know otherwise, pkexec will not raise a exception
if not os.access(self.script, os.X_OK):
ex = OSError("Can not execute '%s'" % self.script)
ex.errno = 13
raise ex
self.runDistUpgrader()
except OSError as e:
if e.errno == 13:
self.error(_("Can not run the upgrade"),
_("This usually is caused by a system where /tmp "
"is mounted noexec. Please remount without "
"noexec and run the upgrade again."))
return False
else:
self.error(_("Can not run the upgrade"),
_("The error message is '%s'.") % e.strerror)
return True
if __name__ == "__main__":
d = DistUpgradeFetcherCore(None, None)
# print(d.authenticate('/tmp/Release','/tmp/Release.gpg'))
print("got mirror: '%s'" %
d.mirror_from_sources_list(
"http://archive.ubuntu.com/ubuntu/dists/intrepid-proposed/main/"
"dist-upgrader-all/0.93.34/intrepid.tar.gz",
"http://archive.ubuntu.com/ubuntu"))
|