1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/usr/bin/env python
"""Follow which hook is handled by the SELinux ALPM hook"""
import logging
import re
import os.path
import subprocess
import sys
ALPM_HOOK_DIR = '/usr/share/libalpm/hooks/'
FOLLOW_DB = os.path.join(os.path.dirname(__file__), 'hook_tracking_db.txt')
logger = logging.getLogger(__name__)
def run_pacman(args):
"""Run a pacman command and return its output"""
proc = subprocess.Popen(
['pacman'] + args,
env={'LANG': 'C'},
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = proc.stdout.read()
retval = proc.wait()
if retval:
errmsg = proc.communicate()[1].decode('ascii', errors='ignore').strip()
logger.error("pacman error %d: %s", retval, errmsg)
return
return output.decode('ascii', errors='ignore')
def find_package_hooks():
"""Find all available package hooks in Arch Linux repositories
This uses the file database, which needs to be updated with "pacman -Fy"
Yield (repo/package name, hook name) tuples
"""
alpm_hookdir_no_leading_slash = ALPM_HOOK_DIR.lstrip('/')
pkglist = run_pacman(['-Fqo', ALPM_HOOK_DIR]) or ''
for pkgname in pkglist.splitlines():
filelist = run_pacman(['-Fql', pkgname]) or ''
for filename in filelist.splitlines():
if filename.startswith(alpm_hookdir_no_leading_slash):
hookname = filename[len(alpm_hookdir_no_leading_slash):]
if hookname and hookname.endswith('.hook'):
yield (pkgname, hookname)
def get_repo_version(pkgname):
"""Get the version of the package in the repositories"""
# This function can be called with a "repo/pkgname" argument
pkgname = pkgname.split('/')[-1]
pkginfo = run_pacman(['-Si', pkgname]) or ''
for line in pkginfo.splitlines():
line = line.rstrip()
matches = re.match(r'^Version\s*:\s*(\S+)$', line, re.I)
if matches is not None:
return matches.group(1)
logger.error("Unable to find the version of package %s", pkgname)
def read_pkghook_db():
"""Read the package hook tracking database file"""
pkghookdb = {}
with open(FOLLOW_DB, 'r') as fdb:
for line in fdb:
line = line.strip()
if not line or line.startswith('#'):
continue
try:
pkg, ver, hook = line.split()
except ValueError:
# Ignore invalid lines
logger.warning("Invalid line in tracking database: %r", line)
continue
else:
pkghookdb[(pkg, hook)] = ver
return pkghookdb
def main():
current_pkghook_db = read_pkghook_db()
everything_ok = True
for pkg, hook in find_package_hooks():
# Get the versions in the package repositories
last_ver = get_repo_version(pkg)
if last_ver is None:
everything_ok = False
continue
# ... and in the tracking database
cur_ver = current_pkghook_db.get((pkg, hook))
if last_ver == cur_ver:
logger.info("OK %s %s %s", pkg, cur_ver, hook)
continue
everything_ok = False
if cur_ver is None:
logger.info("New hook:")
else:
logger.info("Upgrade needed for %s: %s -> %s",
pkg, cur_ver, last_ver)
logger.info(" DB line: %s %s %s", pkg, last_ver, hook)
hookfile = os.path.join(ALPM_HOOK_DIR, hook)
logger.info(" File: %s", hookfile)
# Try to find the Exec line in the file
if os.path.exists(hookfile):
exec_lines = []
target_lines = []
need_target = False
with open(hookfile, 'r') as fhook:
for line in fhook:
line = line.rstrip()
if line.startswith('Exec'):
exec_lines.append(line)
elif line.startswith('Target'):
target_lines.append(line)
elif line.startswith('NeedsTargets'):
need_target = True
# Show Target lines if NeedsTargets was found
if need_target:
for line in target_lines:
logger.info(" # %s", line)
for line in exec_lines:
logger.info(" # %s", line)
if everything_ok:
logger.info("Remember to run sudo pacman -Fy to update pacman db")
return 0
if __name__ == '__main__':
logging.basicConfig(
format='[%(levelname)s] %(message)s',
level=logging.DEBUG)
sys.exit(main())
|