173 lines
6.1 KiB
Python
173 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tools
|
|
|
|
PACKAGE_BASE_PATH = "/var/cache/pacman/pkg/"
|
|
ARCH = "x86_64"
|
|
|
|
|
|
def is_installed(package):
|
|
"""
|
|
Check whether the specified package is installed or not.
|
|
"""
|
|
try:
|
|
subprocess.check_output(["pacman", "-Qs", package])
|
|
return True
|
|
except subprocess.CalledProcessError:
|
|
return False
|
|
|
|
|
|
def get_file_from_package(file):
|
|
"""
|
|
Get the specified file from the corresponding package archive from pacman
|
|
cache.
|
|
|
|
Returns None and print an error message if unable to find the archive in the
|
|
pacman cache.
|
|
"""
|
|
# /var/cache/pacman/pkg/lvm2-2.02.116-1-x86_64.pkg.tar.xz
|
|
# Find the package
|
|
try:
|
|
pacman_raw = subprocess.check_output(["pacman", "-Qo", file])
|
|
pacman_raw = pacman_raw.decode("utf-8").strip()
|
|
package, version = pacman_raw.split(" ")[-2:]
|
|
except subprocess.CalledProcessError:
|
|
print("Unable to find a package owning file " + file + ".",
|
|
file=sys.stderr)
|
|
return None
|
|
# Remove leading / if present
|
|
file = file.lstrip("/")
|
|
# Get the path to the package
|
|
package_archive = package + "-" + version + "-" + ARCH + ".pkg.tar.xz"
|
|
package_path = PACKAGE_BASE_PATH + package_archive
|
|
# Untar the package file
|
|
try:
|
|
raw = subprocess.check_output(["bsdtar", "-xOf", package_path, file],
|
|
stderr=open(os.devnull, 'w'))
|
|
raw = raw.decode("utf-8")
|
|
except subprocess.CalledProcessError:
|
|
try:
|
|
package_path = package_path.replace(ARCH, "any")
|
|
raw = subprocess.check_output(["bsdtar", "-xOf", package_path, file],
|
|
stderr=open(os.devnull, 'w'))
|
|
raw = raw.decode("utf-8")
|
|
except subprocess.CalledProcessError:
|
|
print("Unable to untar package archive " + package_path + ".",
|
|
file=sys.stderr)
|
|
return None
|
|
return raw
|
|
|
|
|
|
def list_packages_in_group(group):
|
|
"""
|
|
List all the packages member of a given group.
|
|
"""
|
|
pacman_raw = subprocess.check_output(["pacman", "-Sg", group])
|
|
pacman_raw = pacman_raw.decode("utf-8").strip()
|
|
return [i.split(" ")[1] for i in pacman_raw.split("\n")]
|
|
|
|
|
|
|
|
def list_modified_config_files(packages=[]):
|
|
"""
|
|
List all the manually modified configuration files.
|
|
|
|
packages is an optionnal filtering list of packages. Defaults to [] which
|
|
means "all the packages".
|
|
"""
|
|
# TODO: diff
|
|
pacman_raw = subprocess.check_output(["pacman", "-Qii"] + packages)
|
|
pacman_raw = pacman_raw.decode("utf-8").strip()
|
|
modified_list = [i.split("\t")[1]
|
|
for i in pacman_raw.split("\n")
|
|
if i.startswith("MODIFIED")]
|
|
return modified_list
|
|
|
|
|
|
def list_installed_packages():
|
|
"""
|
|
List all the installed packages (native, not AUR).
|
|
|
|
If a group of packages is completely installed, those packages will be
|
|
replaced by the matching group name.
|
|
"""
|
|
# Get a list of all the explicitly installed packages
|
|
pacman_packages_raw = subprocess.check_output(["pacman", "-Qenq"])
|
|
pacman_packages_raw = pacman_packages_raw.decode("utf-8").strip()
|
|
packages_list = pacman_packages_raw.split("\n")
|
|
# Get a list of all the explicitly installed packages, part of a group
|
|
pacman_groups_raw = subprocess.check_output(["pacman", "-Qgenq"])
|
|
pacman_groups_raw = pacman_groups_raw.decode("utf-8").strip()
|
|
# Store the installed packages of each group
|
|
groups_packages = {}
|
|
for i in pacman_groups_raw.split("\n"):
|
|
group, package = i.split(" ")
|
|
if group in groups_packages:
|
|
groups_packages[group] += [package]
|
|
else:
|
|
groups_packages[group] = [package]
|
|
# Check if the whole group has been installed
|
|
for group in groups_packages:
|
|
# List of packages in the group not explicitly installed
|
|
diff = set(list_packages_in_group(group)).difference(
|
|
set(groups_packages[group]))
|
|
# Check that they are installed anyway
|
|
diff_are_all_installed = True
|
|
for package in diff:
|
|
if package not in packages_list:
|
|
diff_are_all_installed = False
|
|
break
|
|
if diff_are_all_installed:
|
|
# If the whole group has been installed, replace its packages in
|
|
# the packages list by itself
|
|
for package in groups_packages[group]:
|
|
try:
|
|
packages_list.remove(package)
|
|
except ValueError:
|
|
pass
|
|
packages_list.append(group)
|
|
return packages_list
|
|
|
|
|
|
def list_installed_aur_packages():
|
|
"""
|
|
List all the explicitly installed AUR packages.
|
|
"""
|
|
pacman_raw = subprocess.check_output(["pacman", "-Qemq"])
|
|
pacman_raw = pacman_raw.decode("utf-8").split("\n")
|
|
return pacman_raw
|
|
|
|
|
|
def list_new_config_files(ignore_binaries=True):
|
|
"""
|
|
List all the config files under /etc that are not directly coming from a
|
|
packet.
|
|
"""
|
|
# Get all the config files under /etc owned by a package
|
|
pacman = subprocess.Popen(["pacman", "-Qq"],
|
|
stdout=subprocess.PIPE)
|
|
pacman_raw = subprocess.check_output(["pacman", "-Ql", "-"],
|
|
stdin=pacman.stdout)
|
|
pacman_raw = pacman_raw.decode("utf-8").strip()
|
|
etc_files = [i.split(" ")[-1] for i in pacman_raw.split("\n")]
|
|
etc_files = [i
|
|
for i in etc_files
|
|
if i.startswith("/etc") and os.path.isfile(i)]
|
|
# Get all the files under /etc, in the filesystem
|
|
fs_etc_files = tools.list_directory("/etc/", lambda x: os.path.isfile(x))
|
|
# Diff the two
|
|
raw_diff = set(fs_etc_files).difference(set(etc_files))
|
|
diff = []
|
|
# Filter etc files
|
|
for i in raw_diff:
|
|
# Do not append if binary file
|
|
if ignore_binaries:
|
|
check_binary_raw = subprocess.check_output(["file", i])
|
|
check_binary_raw = check_binary_raw.decode("utf-8")
|
|
if "text" not in check_binary_raw:
|
|
continue
|
|
diff.append(i)
|
|
return diff
|