-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathmissing-libs.py
113 lines (95 loc) · 2.89 KB
/
missing-libs.py
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
#!/usr/bin/env python
# file: missing-libs.py
# vim:fileencoding=utf-8:ft=python
#
# Copyright © 2016-2018 R.F. Smith <[email protected]>.
# SPDX-License-Identifier: MIT
# Created: 2016-01-17T15:48:11+01:00
# Last modified: 2021-02-28T17:00:10+0100
"""Check executables in the given directory for missing shared libraries."""
import argparse
import concurrent.futures as cf
import logging
import sys
import os
import subprocess as sp
from enum import Enum
__version__ = "2020.04.01"
class Ftype(Enum):
"""Enum for limited file type information."""
script = 1
executable = 2
other = 3
unaccessible = 4
def main():
"""
Entry point for missing-libs.py.
"""
args = setup()
programs = (
e.path
for d in args.dirs
for e in os.scandir(d)
if get_type(e.path) == Ftype.executable
)
with cf.ThreadPoolExecutor(max_workers=os.cpu_count()) as tp:
for path, missing in tp.map(check_missing_libs, programs):
for lib in missing:
print(path, lib)
def setup():
"""Process the command-line arguments."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("-v", "--version", action="version", version=__version__)
parser.add_argument(
"--log",
default="warning",
choices=["debug", "info", "warning", "error"],
help="logging level (defaults to 'warning')",
)
parser.add_argument("dirs", nargs="*", help="one or more directory to process")
args = parser.parse_args(sys.argv[1:])
logging.basicConfig(
level=getattr(logging, args.log.upper(), None),
format="%(levelname)s: %(message)s",
)
logging.debug(f"Command line arguments = {sys.argv}")
logging.debug(f"Parsed arguments = {args}")
return args
def get_type(path):
"""
Determine the Ftype of a file.
Returns:
The Ftype for the given path.
"""
try:
with open(path, "rb") as p:
data = p.read(2)
except OSError:
logging.warning(f"cannot access {path}")
return Ftype.unaccessible
if data == b"#!":
return Ftype.script
elif data == b"\x7f\x45":
return Ftype.executable
else:
return Ftype.other
def check_missing_libs(path):
"""
Check if a program has missing libraries, using ldd.
Arguments:
path: String containing the path of a file to query.
Returns:
A tuple of the path and a list of missing libraries.
"""
logging.info(f"checking {path}")
p = sp.run(["ldd", path], stdout=sp.PIPE, stderr=sp.PIPE, universal_newlines=True)
if "not a dynamic executable" in p.stderr:
logging.info(f'"{path}" is not a dynamic executable')
rv = []
else:
rv = [
ln for ln in p.stdout.splitlines() if "missing" in ln or "not found" in ln
]
return (path, rv)
if __name__ == "__main__":
main()