def do_describe(packages, verbose=False, die=True): """Display package description(s)""" package_files = [ package for package in packages if package.endswith(".deb") ] package_names = [ package for package in packages if not package.endswith(".deb") ] if package_files: for package_file in package_files: perform.execute("dpkg-deb --info " + package_file) print("=" * 72) sys.stdout.flush() if package_names: packages = package_names else: return if not packages: print("No packages found from those known to be available/installed.") else: packageversions = list() cache = apt.cache.Cache() for package in packages: try: package = cache[package] except KeyError as e: import subprocess command = 'dpkg --print-foreign-architectures'.split() output = subprocess.check_output(command) for arch in output.decode().split(): try: package = cache["{}:{}".format(package, arch)] # to avoid noise, only consider the 1st match break except KeyError: pass if not isinstance(package, apt.package.Package) and die: print(str(e).strip('"')) return 1 packageversion = package.installed if not packageversion: # if package is not installed... packageversion = package.candidate packageversions.append((package.shortname, packageversion.summary, packageversion.description)) packageversions = set(packageversions) if verbose: for packageversion in packageversions: print("{}: {}\n{}\n".format(packageversion[0], packageversion[1], packageversion[2])) else: print("{0:24} {1}".format("Package", "Description")) print("=" * 24 + "-" + "=" * 51) for packageversion in packageversions: print("%-24s %s" % (packageversion[0], packageversion[1]))
def newly_available(verbose=False): """display brand-new packages.. technically new package names""" with open(new_file) as f: packages = f.readlines() if verbose: for package in packages: perform.execute('aptitude show ' + package) else: do_describe([package.strip() for package in packages], die=False)
def update_available(noreport=False): """Generate current list of available packages, backing up the old list """ if not os.path.exists(available_file): f = open(available_file, "w") f.close() temporary_file = tempfile.mkstemp()[1] os.rename(available_file, temporary_file) # sort --unique so packages with more that one architecture are included # only once. This makes the count shown by "update" consistent with the # output of "toupgrade", though not necessarily with the list shown # by "upgrade" (really "apt-get --show-upgraded upgrade"), which might # show amd64 and i386 versions. command = ("apt-cache dumpavail " "| egrep '^(Package|Version):' " "| tr '\n' ' '" "| perl -p -e 's|Package: |\n|g; s|Version: ||g'" "| sort -u -k 1b,1 | tail -n +2 | sed 's| $||' > ") \ + available_file # Use langC in the following since it uses a grep. perform.execute(command, langC=True) # root is not required. os.rename(temporary_file, previous_file) available_packages = open(available_file).readlines() previous_packages = open(previous_file).readlines() diff = len(available_packages) - len(previous_packages) temporary_file = tempfile.mkstemp()[1] # 090425 Use langC=True to work with change from coreutils 6.10 to 7.2 command = "join -v 1 -t' ' {0} {1} | cut -d' ' -f 1 | tee {2} | wc -l" command = command.format(available_file, previous_file, temporary_file) newest = perform.execute(command, pipe=True, langC=True) newest = newest.readlines()[0].strip() if newest != "0": os.rename(temporary_file, new_file) else: os.remove(temporary_file) if not noreport: if diff < 0: direction = str(0 - diff) + " down on" elif diff == 0: direction = "the same as" else: direction = str(diff) + " up on" print("This is " + direction + " the previous count", end=' ') print("with " + newest + " new", end=' ') if newest == "1": print("package.") else: print("packages.")
def count_upgrades(): """Return as a string the number of new upgrades since last update.""" ifile = tempfile.mkstemp()[1] # Use langC in the following since it uses a grep. perform.execute(gen_installed_command_str() + " > " + ifile, langC=True) command = ("join %s %s |" "awk '$2 != $3 {print}' | sort -k 1b,1 | join - %s |" "awk '$4 != $3 {print}' | wc -l | awk '{print $1}' ") % \ (previous_file, available_file, ifile) # 090425 Use langC=True to work with change from coreutils 6.10 to 7.2 count = perform.execute(command, pipe=True, langC=True).read().split()[0] if os.path.exists(ifile): os.remove(ifile) return count
def finish_log(old_log): ts = datetime.strftime(datetime.now(), '%Y-%m-%dT%H:%M:%S') # Generate new list of installed and compare to old lf = open(log_file, "a") new_iter = perform.execute(gen_installed_command_str(), langC=True, pipe=True) old_iter = open(old_log) for o in old_iter: o = o.strip().split(" ") n = new_iter.__next__().strip().split(" ") while o[0] != n[0]: if o[0] < n[0]: lf.write("{0} {1} {2} {3}\n".format(ts, "remove", o[0], o[1])) o = old_iter.__next__().strip().split(" ") elif o[0] > n[0]: lf.write("{0} {1} {2} {3}\n".format(ts, "install", n[0], n[1])) n = new_iter.__next__().strip().split(" ") if o[1] != n[1]: old_version = o[1].split(".") # for a more accurate comparison new_version = n[1].split(".") # same if old_version > new_version: lf.write("{0} {1} {2} {3}\n".format(ts, "downgrade", n[0], n[1])) else: lf.write("{0} {1} {2} {3}\n".format(ts, "upgrade", n[0], n[1])) os.remove(old_log)
def backup_before_upgrade(packages): """Backup packages before a (dist)upgrade. This optional functionality helps recovery in case of trouble caused by the newly-installed packages. The packages are by default stored in a directory named like ~/.wajig/hostname/backups/2010-09-21_09h21.""" date = time.strftime("%Y-%m-%d_%Hh%M", time.localtime()) target = os.path.join(init_dir, "backups", date) if not os.path.exists(target): os.makedirs(target) os.chdir(target) print("The packages will saved in", target) for package in packages: command = "fakeroot -u dpkg-repack " + package perform.execute(command)
def do_status(packages, snapshot=False): """List status of the packages identified""" if not snapshot: print("%-23s %-15s %-15s %-15s %s" % \ ("Package", "Installed", "Previous", "Now", "State")) print("=" * 23 + "-" + "=" * 15 + "-" + "=" * 15 + "-" + "=" * 15 + "-" + "=" * 5) sys.stdout.flush() # Generate a temporary file of installed packages. ifile = tempfile.mkstemp()[1] perform.execute(gen_installed_command_str() + " > " + ifile, langC=True) # Build the command to list the status of installed packages. command = "dpkg --get-selections | sort | join - " + ifile + " | " +\ "join -a 1 - " + previous_file + " | " +\ "awk 'NF==3 {print $0, \"N/A\"; next}{print}' | " +\ "join -a 1 - " + available_file + " | " +\ "awk 'NF==4 {print $0, \"N/A\"; next}{print}' | " if len(packages) > 0: # Use grep, not egrep, otherwise g++ gets lost, for example! command = command + "grep '^\($" for i in packages: command = command + " \|" + i command = command + " \)' |" command = command +\ "awk '{printf(\"%-20s\\t%-15s\\t%-15s\\t%-15s\\t%-2s\\n\", " +\ "$1, $3, $4, $5, $2)}'" if snapshot: fobj = perform.execute(command, pipe=True) for l in fobj: print("=".join(l.split()[0:2])) else: perform.execute(command, langC=True) # Check whether the package is not in the installed list, and if not # list its status appropriately. for i in packages: if perform.execute("egrep '^" + i + " ' " + ifile + " >/dev/null"): # Package is not installed. command = \ "join -a 2 " + previous_file + " " + available_file + " | " +\ "awk 'NF==2 {print $1, \"N/A\", $2; next}{print}' | " +\ "egrep '^" + i + " '" command = command +\ " | awk '{printf(\"%-20s\\t%-15s\\t%-15s\\t%-15s\\n\", " +\ "$1, \"N/A\", $2, $3)}'" perform.execute(command, langC=True) # Tidy up - remove the "installed file" if os.path.exists(ifile): os.remove(ifile)
def display_sys_docs(package, filenames): """This services README and NEWS commands""" docpath = os.path.join("/usr/share/doc", package) if not os.path.exists(docpath): if package_exists(apt.Cache(), package): print("'{}' is not installed".format(package)) return found = False for filename in filenames: path = os.path.join(docpath, filename) cat = "cat" if not os.path.exists(path): path += ".gz" cat = "zcat" if os.path.exists(path): found = True print("{0:=^72}".format(" {0} ".format(filename))) sys.stdout.flush() perform.execute(cat + " " + path) if not found: print("File not found")
def install(package_list): """Some gymnastics to try install local DEB files""" non_existent = list() for package in package_list: if not os.path.exists(package): non_existent.append(package) if non_existent: print("File(s) not found: " + " ".join(non_existent)) return 1 packages = " ".join(package_list) cmd_install = "dpkg --install {}".format(packages) cmd_configure = "dpkg --configure --pending" if perform.execute(cmd_install, root=True): curdir = os.path.dirname(__file__) script = os.path.join(curdir, "debfile-deps.py") for package in package_list: command = "{} {} {}".format(sys.executable, script, package) perform.execute(command, root=True) perform.execute(cmd_configure, root=True)
def do_listnames(pattern=False, pipe=False, teach=False, noop=False): # If user can't access /etc/apt/sources.list then must do this with # sudo or else most packages will not be found. needsudo = not os.access("/etc/apt/sources.list", os.R_OK) if pattern: command = "apt-cache pkgnames | grep -E -- " + pattern \ + " | sort -k 1b,1" else: command = "apt-cache pkgnames | sort -k 1b,1" # Start fix for Bug #292581 - pre-run command to check for no output results = perform.execute(command, root=needsudo, pipe=True, teach=teach, noop=noop) if results: lines = results.readlines() if lines: return perform.execute(command, root=needsudo, pipe=pipe) else: sys.exit(1)
def start_log(old_log): "Write a list of installed packages to a tmp file." perform.execute(gen_installed_command_str() + " > " + old_log, langC=True)
def do_update(simulate=False): if not perform.execute("apt update", root=True): if not simulate: update_available() print("There are {} new upgrades".format(count_upgrades()))
if not os.path.exists(init_dir): os.makedirs(init_dir) # # Temporarily, remove old files from .wajig # After a few versions remove this code. # tmp_dir = os.path.expanduser("~/.wajig") if os.path.exists(tmp_dir + "/Available"): os.rename(tmp_dir + "/Available", init_dir + "/Available") if os.path.exists(tmp_dir + "/Available.prv"): os.rename(tmp_dir + "/Available.prv", init_dir + "/Available.prv") if os.path.exists(tmp_dir + "/Installed"): os.rename(tmp_dir + "/Installed", init_dir + "/Installed") # 100104 Remove any old tmp files. Bug#563573 perform.execute("rm -f " + init_dir + "/tmp*") # TODO 23 Aug 2003 # # Perhaps the only file that wajig needs to cache itself is # Available.prv - the other two can be generated dynamically. # Installed is now like this. Available can be done. # This would mean essentially half the space usage (Installed # is quite small at 30K, while Available and Available.prv # were 280K). This is significant for a user who manages # many hosts. # # If this is the case, then don't worry about creating a host # subdirectory in ~/.wajig. Simply use host in filename, # unless init_dir is used elsewhere?????? Perhaps keep as folder # since tempfiles are created there.