2018-11-09 12:43:10 +01:00
|
|
|
#!/usr/bin/python3
|
2018-02-13 23:59:23 +01:00
|
|
|
# Installation script to install howdy
|
2018-04-05 21:12:36 +02:00
|
|
|
# Executed after primary apt install
|
2018-02-13 23:59:23 +01:00
|
|
|
|
2018-12-21 16:51:23 +01:00
|
|
|
|
2018-04-13 00:54:36 +02:00
|
|
|
def col(id):
|
|
|
|
"""Add color escape sequences"""
|
|
|
|
if id == 1: return "\033[32m"
|
|
|
|
if id == 2: return "\033[33m"
|
|
|
|
if id == 3: return "\033[31m"
|
|
|
|
return "\033[0m"
|
|
|
|
|
2018-12-21 16:51:23 +01:00
|
|
|
|
2018-02-13 23:59:23 +01:00
|
|
|
# Import required modules
|
2018-12-09 07:30:25 +01:00
|
|
|
import fileinput
|
2018-02-05 23:43:32 +01:00
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import os
|
2018-02-13 15:00:30 +01:00
|
|
|
import re
|
2018-12-09 07:30:25 +01:00
|
|
|
import tarfile
|
|
|
|
from shutil import rmtree, which
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2018-04-13 00:54:36 +02:00
|
|
|
# Don't run unless we need to configure the install
|
|
|
|
# Will also happen on upgrade but we will catch that later on
|
2018-04-12 19:38:34 +02:00
|
|
|
if "configure" not in sys.argv:
|
|
|
|
sys.exit(0)
|
|
|
|
|
2018-04-13 00:54:36 +02:00
|
|
|
|
2018-02-05 23:43:32 +01:00
|
|
|
def log(text):
|
2018-02-13 23:59:23 +01:00
|
|
|
"""Print a nicely formatted line to stdout"""
|
2018-04-13 01:00:36 +02:00
|
|
|
print("\n>>> " + col(1) + text + col(0) + "\n")
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2018-12-21 16:51:23 +01:00
|
|
|
|
2018-02-05 23:43:32 +01:00
|
|
|
def handleStatus(status):
|
2018-02-13 23:59:23 +01:00
|
|
|
"""Abort if a command fails"""
|
2018-02-08 01:11:47 +01:00
|
|
|
if (status != 0):
|
2018-04-13 00:54:36 +02:00
|
|
|
print(col(3) + "Error while running last command" + col(0))
|
2018-04-05 21:12:36 +02:00
|
|
|
sys.exit(1)
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2018-04-05 21:46:11 +02:00
|
|
|
|
2019-01-02 23:36:09 +01:00
|
|
|
# Create shorthand for subprocess creation
|
2018-12-09 07:30:25 +01:00
|
|
|
sc = subprocess.call
|
2018-04-05 21:46:11 +02:00
|
|
|
|
2018-05-10 15:05:27 +02:00
|
|
|
# We're not in fresh configuration mode so don't continue the setup
|
2018-04-12 19:38:34 +02:00
|
|
|
if not os.path.exists("/tmp/howdy_picked_device"):
|
2018-05-10 15:05:27 +02:00
|
|
|
# Check if we have an older config we can restore
|
|
|
|
if len(sys.argv) > 2:
|
|
|
|
if os.path.exists("/tmp/howdy_config_backup_v" + sys.argv[2] + ".ini"):
|
|
|
|
# Get the config parser
|
|
|
|
import configparser
|
|
|
|
|
|
|
|
# Load th old and new config files
|
|
|
|
oldConf = configparser.ConfigParser()
|
|
|
|
oldConf.read("/tmp/howdy_config_backup_v" + sys.argv[2] + ".ini")
|
|
|
|
newConf = configparser.ConfigParser()
|
|
|
|
newConf.read("/lib/security/howdy/config.ini")
|
|
|
|
|
|
|
|
# Go through every setting in the old config and apply it to the new file
|
|
|
|
for section in oldConf.sections():
|
|
|
|
for (key, value) in oldConf.items(section):
|
2018-12-09 12:47:29 +01:00
|
|
|
|
|
|
|
# MIGRATION 2.3.1 -> 2.4.0
|
2018-11-09 20:26:19 +01:00
|
|
|
# If config is still using the old device_id parameter, convert it to a path
|
2018-11-09 20:02:30 +01:00
|
|
|
if key == "device_id":
|
|
|
|
key = "device_path"
|
|
|
|
value = "/dev/video" + value
|
|
|
|
|
2018-12-09 12:47:29 +01:00
|
|
|
# MIGRATION 2.4.0 -> 2.5.0
|
|
|
|
# Finally correct typo in "timout" config value
|
|
|
|
if key == "timout":
|
|
|
|
key = "timeout"
|
|
|
|
|
2019-03-29 22:21:14 +01:00
|
|
|
# MIGRATION 2.5.0 -> 2.5.1
|
|
|
|
# Remove unsafe automatic dismissal of lock screen
|
|
|
|
if key == "dismiss_lockscreen":
|
2019-03-29 22:51:08 +01:00
|
|
|
if value == "true":
|
2019-03-29 22:21:14 +01:00
|
|
|
print("DEPRECATION: Config falue dismiss_lockscreen is no longer supported because of login loop issues.")
|
|
|
|
continue
|
|
|
|
|
2018-05-10 15:05:27 +02:00
|
|
|
try:
|
|
|
|
newConf.set(section, key, value)
|
|
|
|
# Add a new section where needed
|
2019-01-02 22:57:00 +01:00
|
|
|
except configparser.NoSectionError:
|
2018-05-10 15:05:27 +02:00
|
|
|
newConf.add_section(section)
|
|
|
|
newConf.set(section, key, value)
|
|
|
|
|
|
|
|
# Write it all to file
|
|
|
|
with open("/lib/security/howdy/config.ini", "w") as configfile:
|
|
|
|
newConf.write(configfile)
|
|
|
|
|
2019-01-06 15:36:03 +01:00
|
|
|
# Install dlib data files if needed
|
|
|
|
if not os.path.exists("/lib/security/howdy/dlib-data/shape_predictor_5_face_landmarks.dat"):
|
|
|
|
print("Attempting installation of missing data files")
|
|
|
|
handleStatus(subprocess.call(["./install.sh"], shell=True, cwd="/lib/security/howdy/dlib-data"))
|
|
|
|
|
2018-04-12 19:38:34 +02:00
|
|
|
sys.exit(0)
|
2018-04-05 21:12:36 +02:00
|
|
|
|
2018-04-13 00:54:36 +02:00
|
|
|
# Open the temporary file containing the device ID
|
2018-04-12 19:38:34 +02:00
|
|
|
in_file = open("/tmp/howdy_picked_device", "r")
|
2018-04-13 00:54:36 +02:00
|
|
|
# Load it in, it should be a string
|
2018-04-12 23:45:52 +02:00
|
|
|
picked = in_file.read()
|
2018-04-12 19:38:34 +02:00
|
|
|
in_file.close()
|
2018-04-05 21:12:36 +02:00
|
|
|
|
2018-04-13 00:54:36 +02:00
|
|
|
# Remove the temporary file
|
2018-12-09 07:30:25 +01:00
|
|
|
os.unlink("/tmp/howdy_picked_device")
|
2018-04-05 21:12:36 +02:00
|
|
|
|
2018-04-12 23:45:52 +02:00
|
|
|
log("Upgrading pip to the latest version")
|
|
|
|
|
2018-04-12 19:38:34 +02:00
|
|
|
# Update pip
|
2019-01-03 14:35:07 +01:00
|
|
|
handleStatus(sc(["pip3", "install", "--upgrade", "pip"]))
|
2018-12-09 07:30:25 +01:00
|
|
|
|
2019-10-18 13:24:39 +02:00
|
|
|
|
2020-09-01 16:51:50 +02:00
|
|
|
log("Upgrading numpy to the latest version")
|
2019-10-18 13:24:39 +02:00
|
|
|
|
|
|
|
# Update numpy
|
|
|
|
handleStatus(subprocess.call(["pip3", "install", "--upgrade", "numpy"]))
|
|
|
|
|
2019-01-02 22:57:00 +01:00
|
|
|
log("Downloading and unpacking data files")
|
|
|
|
|
2019-01-02 23:36:09 +01:00
|
|
|
# Run the bash script to download and unpack the .dat files needed
|
2019-01-02 22:57:00 +01:00
|
|
|
handleStatus(subprocess.call(["./install.sh"], shell=True, cwd="/lib/security/howdy/dlib-data"))
|
2018-12-09 07:30:25 +01:00
|
|
|
|
2019-01-02 23:36:09 +01:00
|
|
|
log("Downloading dlib")
|
2018-12-09 07:30:25 +01:00
|
|
|
|
2019-01-03 15:05:53 +01:00
|
|
|
dlib_archive = "/tmp/v19.16.tar.gz"
|
|
|
|
loader = which("wget")
|
|
|
|
LOADER_CMD = None
|
|
|
|
|
|
|
|
# If wget is installed, use that as the downloader
|
|
|
|
if loader:
|
|
|
|
LOADER_CMD = [loader, "--tries", "5", "--output-document"]
|
|
|
|
# Otherwise, fall back on curl
|
2019-01-03 14:35:07 +01:00
|
|
|
else:
|
2019-01-03 15:05:53 +01:00
|
|
|
loader = which("curl")
|
|
|
|
LOADER_CMD = [loader, "--retry", "5", "--location", "--output"]
|
|
|
|
|
|
|
|
# Assemble and execute the download command
|
|
|
|
cmd = LOADER_CMD + [dlib_archive, "https://github.com/davisking/dlib/archive/v19.16.tar.gz"]
|
|
|
|
handleStatus(sc(cmd))
|
|
|
|
|
|
|
|
# The folder containing the dlib source
|
|
|
|
DLIB_DIR = None
|
|
|
|
# A regex of all files to ignore while unpacking the archive
|
|
|
|
excludes = re.compile(
|
|
|
|
"davisking-dlib-\w+/(dlib/(http_client|java|matlab|test/)|"
|
|
|
|
"(docs|examples|python_examples)|"
|
|
|
|
"tools/(archive|convert_dlib_nets_to_caffe|htmlify|imglab|python/test|visual_studio_natvis))"
|
|
|
|
)
|
|
|
|
|
|
|
|
# Open the archive
|
|
|
|
with tarfile.open(dlib_archive) as tf:
|
|
|
|
for item in tf:
|
|
|
|
# Set the destenation dir if unset
|
|
|
|
if not DLIB_DIR:
|
|
|
|
DLIB_DIR = "/tmp/" + item.name
|
|
|
|
|
|
|
|
# extract only files sufficient for building
|
|
|
|
if not excludes.match(item.name):
|
|
|
|
tf.extract(item, "/tmp")
|
|
|
|
|
|
|
|
# Delete the downloaded archive
|
|
|
|
os.unlink(dlib_archive)
|
2018-11-09 18:53:31 +01:00
|
|
|
|
|
|
|
log("Building dlib")
|
|
|
|
|
2019-01-02 22:57:00 +01:00
|
|
|
cmd = ["sudo", "python3", "setup.py", "install"]
|
|
|
|
cuda_used = False
|
2019-01-03 15:05:53 +01:00
|
|
|
|
|
|
|
# Compile and link dlib
|
|
|
|
try:
|
|
|
|
sp = subprocess.Popen(cmd, cwd=DLIB_DIR, stdout=subprocess.PIPE)
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
print("Error while building dlib")
|
|
|
|
raise
|
|
|
|
|
|
|
|
# Go through each line from stdout
|
|
|
|
while sp.poll() is None:
|
|
|
|
line = sp.stdout.readline().decode("utf-8")
|
|
|
|
|
|
|
|
if "DLIB WILL USE CUDA" in line:
|
|
|
|
cuda_used = True
|
|
|
|
|
|
|
|
print(line, end="")
|
|
|
|
|
|
|
|
log("Cleaning up dlib")
|
|
|
|
|
|
|
|
# Remove the no longer needed git clone
|
|
|
|
del sp
|
|
|
|
rmtree(DLIB_DIR)
|
|
|
|
|
|
|
|
print("Temporary dlib files removed")
|
2019-01-02 22:57:00 +01:00
|
|
|
|
|
|
|
log("Installing OpenCV")
|
2018-11-09 18:53:31 +01:00
|
|
|
|
2019-01-02 22:57:00 +01:00
|
|
|
handleStatus(subprocess.call(["pip3", "install", "--no-cache-dir", "opencv-python"]))
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2018-04-07 19:15:00 +02:00
|
|
|
log("Configuring howdy")
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2020-06-22 17:58:09 +02:00
|
|
|
campath = picked.split(";")[0]
|
|
|
|
cert = picked.split(";")[1]
|
|
|
|
|
2018-02-13 23:59:23 +01:00
|
|
|
# Manually change the camera id to the one picked
|
2018-12-09 07:30:25 +01:00
|
|
|
for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace=1):
|
2020-06-22 17:58:09 +02:00
|
|
|
line = line.replace("device_path = none", "device_path = " + campath)
|
2019-01-02 23:36:09 +01:00
|
|
|
line = line.replace("use_cnn = false", "use_cnn = " + str(cuda_used).lower())
|
2020-06-22 17:58:09 +02:00
|
|
|
line = line.replace("certainty = 3.5", "certainty = " + cert)
|
2019-01-02 23:36:09 +01:00
|
|
|
|
|
|
|
print(line, end="")
|
|
|
|
|
2018-11-09 16:33:19 +01:00
|
|
|
print("Camera ID saved")
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2018-02-10 17:03:42 +01:00
|
|
|
# Secure the howdy folder
|
2018-12-09 07:30:25 +01:00
|
|
|
handleStatus(sc(["chmod 744 -R /lib/security/howdy/"], shell=True))
|
2018-02-10 17:03:42 +01:00
|
|
|
|
2018-04-07 17:39:14 +02:00
|
|
|
# Allow anyone to execute the python CLI
|
2019-01-02 23:36:09 +01:00
|
|
|
os.chmod("/lib/security/howdy", 0o755)
|
|
|
|
os.chmod("/lib/security/howdy/cli.py", 0o755)
|
2018-12-09 07:30:25 +01:00
|
|
|
handleStatus(sc(["chmod 755 -R /lib/security/howdy/cli"], shell=True))
|
2018-04-07 19:15:00 +02:00
|
|
|
print("Permissions set")
|
2018-04-07 17:39:14 +02:00
|
|
|
|
|
|
|
# Make the CLI executable as howdy
|
2018-12-09 07:30:25 +01:00
|
|
|
os.symlink("/lib/security/howdy/cli.py", "/usr/local/bin/howdy")
|
|
|
|
os.chmod("/usr/local/bin/howdy", 0o755)
|
2018-04-13 00:54:36 +02:00
|
|
|
print("Howdy command installed")
|
2018-02-10 17:03:42 +01:00
|
|
|
|
2018-02-05 23:43:32 +01:00
|
|
|
log("Adding howdy as PAM module")
|
|
|
|
|
2018-12-05 12:23:07 +01:00
|
|
|
# Activate the pam-config file
|
|
|
|
handleStatus(subprocess.call(["pam-auth-update --package"], shell=True))
|
2018-02-05 23:43:32 +01:00
|
|
|
|
2018-11-09 15:45:59 +01:00
|
|
|
# Sign off
|
2018-02-05 23:43:32 +01:00
|
|
|
print("Installation complete.")
|