0
0
Fork 0
mirror of https://github.com/boltgolt/howdy.git synced 2024-09-12 09:41:18 +02:00

build: add meson build system

Add Meson as a build system for the whole howdy package,
which allow better flexibility for the paths configuration.
Generate a python module,
which contains the paths used by the other modules
and the header containing the paths for the PAM module.
This commit is contained in:
Sayafdine Said 2023-06-19 23:05:30 +02:00
parent 53a6ea5eb1
commit 2d2480054d
No known key found for this signature in database
GPG key ID: 7567D43648C6E2F4
18 changed files with 189 additions and 40 deletions

3
howdy/src/bin/howdy.in Normal file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env sh
/usr/bin/env python3 "@script_path@" "$@"

View file

@ -28,7 +28,7 @@ except ImportError as err:
import cv2
# Test if at lest 1 of the data files is there and abort if it's not
if not os.path.isfile(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat"):
if not os.path.isfile(paths.dlib_data_dir / "shape_predictor_5_face_landmarks.dat"):
print(_("Data files have not been downloaded, please run the following commands:"))
print("\n\tcd " + paths.dlib_data_dir)
print("\tsudo ./install.sh\n")
@ -36,20 +36,20 @@ if not os.path.isfile(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.da
# Read config from disk
config = configparser.ConfigParser()
config.read(paths.config_dir + "/config.ini")
config.read(paths.config_dir / "config.ini")
use_cnn = config.getboolean("core", "use_cnn", fallback=False)
if use_cnn:
face_detector = dlib.cnn_face_detection_model_v1(paths.dlib_data_dir + "mmod_human_face_detector.dat")
face_detector = dlib.cnn_face_detection_model_v1(paths.dlib_data_dir / "mmod_human_face_detector.dat")
else:
face_detector = dlib.get_frontal_face_detector()
pose_predictor = dlib.shape_predictor(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir + "dlib_face_recognition_resnet_model_v1.dat")
pose_predictor = dlib.shape_predictor(paths.dlib_data_dir / "shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir / "dlib_face_recognition_resnet_model_v1.dat")
user = builtins.howdy_user
# The permanent file to store the encoded model in
enc_file = paths.user_models_dir + user + ".dat"
enc_file = paths.user_models_dir / f"{user}.dat"
# Known encodings
encodings = []

View file

@ -17,7 +17,7 @@ if not os.path.exists(paths.user_models_dir):
sys.exit(1)
# Check if the user has a models file to delete
if not os.path.isfile(paths.user_models_dir + user + ".dat"):
if not os.path.isfile(paths.user_models_dir / f"{user}.dat"):
print(_("{} has no models or they have been cleared already").format(user))
sys.exit(1)
@ -33,5 +33,5 @@ if not builtins.howdy_args.y:
sys.exit(1)
# Delete otherwise
os.remove(paths.user_models_dir + user + ".dat")
os.remove(paths.user_models_dir / f"{user}.dat")
print(_("\nModels cleared"))

View file

@ -20,4 +20,4 @@ elif os.path.isfile("/etc/alternatives/editor"):
editor = "/etc/alternatives/editor"
# Open the editor as a subprocess and fork it
subprocess.call([editor, paths.config_dir + "config.ini"])
subprocess.call([editor, paths.config_dir / "config.ini"])

View file

@ -11,7 +11,7 @@ import paths
from i18n import _
# Get the absolute filepath
config_path = os.path.dirname(paths.config_dir) + "/config.ini"
config_path = paths.config_dir.parent / "config.ini"
# Read config from disk
config = configparser.ConfigParser()

View file

@ -19,7 +19,7 @@ if not os.path.exists(paths.user_models_dir):
sys.exit(1)
# Path to the models file
enc_file = paths.user_models_dir + user + ".dat"
enc_file = paths.user_models_dir / f"{user}.dat"
# Try to load the models file and abort if the user does not have it yet
try:

View file

@ -27,7 +27,7 @@ if not os.path.exists(paths.user_models_dir):
sys.exit(1)
# Path to the models file
enc_file = paths.user_models_dir + user + ".dat"
enc_file = paths.user_models_dir / f"{user}.dat"
# Try to load the models file and abort if the user does not have it yet
try:
@ -71,7 +71,7 @@ if not found:
# Remove the entire file if this encoding is the only one
if len(encodings) == 1:
os.remove(paths.user_models_dir + user + ".dat")
os.remove(paths.user_models_dir / f"{user}.dat")
print(_("Removed last model, howdy disabled for user"))
else:
# A place holder to contain the encodings that will remain

View file

@ -10,7 +10,7 @@ import paths
from i18n import _
# Get the absolute filepath
config_path = os.path.dirname(paths.config_dir) + "/config.ini"
config_path = paths.config_dir / "/config.ini"
# Check if enough arguments have been passed
if len(builtins.howdy_args.arguments) < 2:

View file

@ -12,7 +12,7 @@ from i18n import _
# Read the config
config = configparser.ConfigParser()
config.read(paths.config_dir + "config.ini")
config.read(paths.config_dir / "config.ini")
# Start video capture
video_capture = VideoCapture(config)

View file

@ -20,7 +20,7 @@ path = "/etc/howdy"
# Read config from disk
config = configparser.ConfigParser()
config.read(paths.config_dir + "config.ini")
config.read(paths.config_dir / "config.ini")
if config.get("video", "recording_plugin", fallback="opencv") != "opencv":
print(_("Howdy has been configured to use a recorder which doesn't support the test command yet, aborting"))
@ -60,20 +60,20 @@ use_cnn = config.getboolean('core', 'use_cnn', fallback=False)
if use_cnn:
face_detector = dlib.cnn_face_detection_model_v1(
paths.dlib_data_dir + "mmod_human_face_detector.dat"
paths.dlib_data_dir / "mmod_human_face_detector.dat"
)
else:
face_detector = dlib.get_frontal_face_detector()
pose_predictor = dlib.shape_predictor(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir + "dlib_face_recognition_resnet_model_v1.dat")
pose_predictor = dlib.shape_predictor(paths.dlib_data_dir / "shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir / "dlib_face_recognition_resnet_model_v1.dat")
encodings = []
models = None
try:
user = builtins.howdy_user
models = json.load(open(paths.user_models_dir + user + ".dat"))
models = json.load(open(paths.user_models_dir / f"{user}.dat"))
for model in models:
encodings += model["data"]

View file

@ -49,7 +49,7 @@ def init_detector(lock):
global face_detector, pose_predictor, face_encoder
# Test if at lest 1 of the data files is there and abort if it's not
if not os.path.isfile(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat"):
if not os.path.isfile(paths.dlib_data_dir / "shape_predictor_5_face_landmarks.dat"):
print(_("Data files have not been downloaded, please run the following commands:"))
print("\n\tcd " + paths.dlib_data_dir)
print("\tsudo ./install.sh\n")
@ -58,13 +58,13 @@ def init_detector(lock):
# Use the CNN detector if enabled
if use_cnn:
face_detector = dlib.cnn_face_detection_model_v1(paths.dlib_data_dir + "mmod_human_face_detector.dat")
face_detector = dlib.cnn_face_detection_model_v1(paths.dlib_data_dir / "mmod_human_face_detector.dat")
else:
face_detector = dlib.get_frontal_face_detector()
# Start the others regardless
pose_predictor = dlib.shape_predictor(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir + "dlib_face_recognition_resnet_model_v1.dat")
pose_predictor = dlib.shape_predictor(paths.dlib_data_dir / "shape_predictor_5_face_landmarks.dat")
face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir / "dlib_face_recognition_resnet_model_v1.dat")
# Note the time it took to initialize detectors
timings["ll"] = time.time() - timings["ll"]
@ -127,7 +127,7 @@ face_encoder = None
# Try to load the face model from the models folder
try:
models = json.load(open(paths.user_models_dir + user + ".dat"))
models = json.load(open(paths.user_models_dir / f"{user}.dat"))
for model in models:
encodings += model["data"]
@ -140,7 +140,7 @@ if len(models) < 1:
# Read config from disk
config = configparser.ConfigParser()
config.read(paths.config_dir + "config.ini")
config.read(paths.config_dir / "config.ini")
# Get all config values needed
use_cnn = config.getboolean("core", "use_cnn", fallback=False)

129
howdy/src/meson.build Normal file
View file

@ -0,0 +1,129 @@
project('howdy', 'cpp', license: 'MIT', license_files: '../LICENSE', version: 'beta', meson_version: '>= 1.1.0')
py = import('python').find_installation()
py.dependency()
confdir = join_paths(get_option('sysconfdir'), 'howdy')
dlibdatadir = join_paths(confdir, 'dlib-data')
usermodelsdir = join_paths(confdir, 'models')
logpath = '/var/log/howdy'
py_conf = configuration_data({
'config_dir': confdir,
'dlib_data_dir': dlibdatadir,
'user_models_dir': usermodelsdir,
'log_path': logpath,
})
py_paths = configure_file(
input: 'paths.py.in',
output: 'paths.py',
configuration: py_conf,
)
py_sources = [
'cli/__init__.py',
'cli/add.py',
'cli/clear.py',
'cli/config.py',
'cli/disable.py',
'cli/list.py',
'cli/remove.py',
'cli/set.py',
'cli/snap.py',
'cli/test.py',
'cli.py',
'compare.py',
'i18n.py',
'recorders/__init__.py',
'recorders/ffmpeg_reader.py',
'recorders/pyv4l2_reader.py',
'recorders/v4l2.py',
'recorders/video_capture.py',
'rubberstamps/__init__.py',
'rubberstamps/hotkey.py',
'rubberstamps/nod.py',
'snapshot.py',
py_paths,
]
# Include PAM module
compare_script_path = join_paths(py.get_install_dir(), 'howdy', 'compare.py')
subdir('pam')
py.install_sources(
py_sources,
subdir: 'howdy',
preserve_path: true,
)
install_data('logo.png', install_tag: 'meta')
install_data('config.ini', install_dir: confdir, install_mode: 'rwxr--r--', install_tag: 'config')
install_data('dlib-data/install.sh', install_dir: dlibdatadir, install_mode: 'rwxr--r--')
install_data('dlib-data/Readme.md', install_dir: dlibdatadir, install_mode: 'r--r--r--', install_tag: 'docs')
install_man('../howdy.1')
# if get_option('fetch_dlib_data')
# downloader = find_program('wget')
# bunzip2 = find_program('bunzip2')
# links = [
# 'https://github.com/davisking/dlib-models/raw/master/dlib_face_recognition_resnet_model_v1.dat.bz2',
# 'https://github.com/davisking/dlib-models/raw/master/mmod_human_face_detector.dat.bz2',
# 'https://github.com/davisking/dlib-models/raw/master/shape_predictor_5_face_landmarks.dat.bz2'
# ]
# archived_model_files = [
# 'dlib_face_recognition_resnet_model_v1.dat.bz2',
# 'shape_predictor_5_face_landmarks.dat.bz2',
# 'mmod_human_face_detector.dat.bz2'
# ]
# download = run_command(
# 'download',
# links,
# output: archived_model_files,
# command: [downloader, '-O', '@OUTPUT@', '@INPUT@']
# )
# model_files = [
# 'dlib_face_recognition_resnet_model_v1.dat',
# 'shape_predictor_5_face_landmarks.dat',
# 'mmod_human_face_detector.dat'
# ]
# models = custom_target(
# 'models',
# input: archived_model_files,
# output: model_files,
# command: [bunzip2, '-k', '@INPUT@'],
# )
# install_data(
# model_files,
# install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'dlib_models'),
# )
# endif
py_path = py.get_install_dir()
cli_path = join_paths(py_path, 'howdy', 'cli.py')
conf_data = configuration_data({ 'script_path': cli_path })
bin_name = 'howdy'
bin = configure_file(
input: 'bin/howdy.in',
output: bin_name,
configuration: conf_data
)
install_data(
bin,
install_mode: 'rwxr-xr-x',
install_dir: get_option('bindir'),
)

2
howdy/src/meson.options Normal file
View file

@ -0,0 +1,2 @@
option('pam_dir', type: 'string', value: '/lib/security', description: 'Set the pam_howdy destination directory')
#option('fetch_dlib_data', type: 'boolean', value: false, description: 'Download dlib data files')

View file

@ -41,12 +41,12 @@
#include "enter_device.hh"
#include "main.hh"
#include "optional_task.hh"
#include "paths.hh"
const auto DEFAULT_TIMEOUT =
std::chrono::duration<int, std::chrono::milliseconds::period>(100);
const auto MAX_RETRIES = 5;
const auto PYTHON_EXECUTABLE = "python3";
const auto COMPARE_PROCESS_PATH = "/lib/security/howdy/compare.py";
#define S(msg) gettext(msg)
@ -80,7 +80,8 @@ auto howdy_error(int status,
syslog(LOG_ERR, "Failure, image too dark");
break;
case CompareError::INVALID_DEVICE:
syslog(LOG_ERR, "Failure, not possible to open camera at configured path");
syslog(LOG_ERR,
"Failure, not possible to open camera at configured path");
break;
default:
conv_function(PAM_ERROR_MSG,
@ -321,7 +322,8 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv,
// Wait for the end either of the child or the password input
{
std::unique_lock<std::mutex> lock(mutx);
convar.wait(lock, [&] { return confirmation_type != ConfirmationType::Unset; });
convar.wait(lock,
[&] { return confirmation_type != ConfirmationType::Unset; });
}
// The password has been entered or an error has occurred

View file

@ -1,5 +1,3 @@
project('pam_howdy', 'cpp', version: '0.1.0', default_options: ['cpp_std=c++14'])
inih_cpp = dependency('INIReader', fallback: ['inih', 'INIReader_dep'])
libevdev = dependency('libevdev')
libpam = meson.get_compiler('cpp').find_library('pam')
@ -8,6 +6,16 @@ threads = dependency('threads')
# Translations
subdir('po')
# Paths
paths = { 'compare_script_path': compare_script_path }
paths_h = configure_file(
input: 'paths.hh.in',
output: 'paths.hh',
configuration: paths,
install_dir: get_option('pam_dir')
)
shared_library(
'pam_howdy',
'main.cc',
@ -18,7 +26,10 @@ shared_library(
threads,
libevdev,
],
link_depends: [
paths_h,
],
install: true,
install_dir: '/lib/security',
install_dir: get_option('pam_dir'),
name_prefix: ''
)

View file

@ -0,0 +1 @@
const auto COMPARE_PROCESS_PATH = "@compare_script_path@";

View file

@ -1,12 +1,13 @@
from pathlib import PurePath
# Define the absolute path to the config directory
config_dir = "/etc/howdy/"
config_dir = PurePath("@config_dir@")
# Define the absolute path to the DLib models data directory
dlib_data_dir = config_dir + "/dlib-data/"
dlib_data_dir = PurePath("@dlib_data_dir@")
# Define the absolute path to the Howdy user models directory
user_models_dir = config_dir + "/models/"
user_models_dir = PurePath("@user_models_dir@")
# Define path to any howdy logs
log_path = "/var/log/howdy"
log_path = PurePath("@log_path@")

View file

@ -52,13 +52,13 @@ def generate(frames, text_lines):
# Made sure a snapshot folder exist
if not os.path.exists(paths.log_path):
os.makedirs(paths.log_path)
if not os.path.exists(paths.log_path + "/snapshots"):
os.makedirs(paths.log_path + "/snapshots")
if not os.path.exists(paths.log_path / "snapshots"):
os.makedirs(paths.log_path / "snapshots")
# Generate a filename based on the current time
filename = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S.jpg")
# Write the image to that file
cv2.imwrite(paths.log_path + "/snapshots/" + filename, snap)
cv2.imwrite(paths.log_path / "snapshots" / filename, snap)
# Return the saved file location
return paths.log_path + "/snapshots/" + filename
return paths.log_path / "/snapshots/" / filename