From c358fc4336450b43861e4687efcb7cd812950c4c Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Mon, 23 Feb 2026 01:00:36 +0100 Subject: [PATCH 1/9] build: Install deb package in aarch64 Dockerfile and explicitly use Dockerfile.amd64 for amd64 build target. --- Makefile | 2 +- docker/Dockerfile.aarch64 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 237877df..9568ce16 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ deb: # Docker Build Targets docker-amd64: @echo "Building Docker (amd64)..." - @docker build --platform linux/amd64 -t linuxcampam:amd64 . + @docker build --platform linux/amd64 -t linuxcampam:amd64 -f docker/Dockerfile.amd64 . @echo "Verifying..." @docker run --rm --platform linux/amd64 --entrypoint /usr/bin/linuxcampam linuxcampam:amd64 help diff --git a/docker/Dockerfile.aarch64 b/docker/Dockerfile.aarch64 index 99914cb7..52eaf4ae 100644 --- a/docker/Dockerfile.aarch64 +++ b/docker/Dockerfile.aarch64 @@ -15,3 +15,4 @@ RUN dpkg-buildpackage -b -uc -us # Verify man pages are in the generated deb RUN dpkg -c ../linuxcampam_*.deb | grep share/man +RUN apt-get update && apt-get install -y ../linuxcampam_*.deb From 37540a85a47a0cca401a1f858f46ade1d51c59a8 Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Mon, 23 Feb 2026 09:51:05 +0100 Subject: [PATCH 2/9] feat: Display `linux-enable-ir-emitter` version in installation completion messages. --- scripts/install_ir_emitter.sh | 15 ++++++++++++--- scripts/setup_config.sh | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/install_ir_emitter.sh b/scripts/install_ir_emitter.sh index 5d88c272..5399e205 100755 --- a/scripts/install_ir_emitter.sh +++ b/scripts/install_ir_emitter.sh @@ -135,7 +135,12 @@ if [ -f "Cargo.toml" ]; then echo " sudo $0 $LATEST_TAG" echo "" - echo "=== Installation Complete ===" + IRE_VERSION=$(linux-enable-ir-emitter -V 2>/dev/null || true) + if [ -n "$IRE_VERSION" ]; then + echo "=== Installation Complete ($IRE_VERSION) ===" + else + echo "=== Installation Complete ===" + fi exit 0 fi @@ -158,5 +163,9 @@ else ninja -C build ninja -C build install fi - -echo "=== Installation Complete ===" +IRE_VERSION=$(linux-enable-ir-emitter -V 2>/dev/null || true) +if [ -n "$IRE_VERSION" ]; then + echo "=== Installation Complete ($IRE_VERSION) ===" +else + echo "=== Installation Complete ===" +fi diff --git a/scripts/setup_config.sh b/scripts/setup_config.sh index 1ee48d1e..e854b4ea 100755 --- a/scripts/setup_config.sh +++ b/scripts/setup_config.sh @@ -71,7 +71,8 @@ if [ -n "$IR_CAM" ]; then chmod +x /tmp/install_ir_emitter.sh /tmp/install_ir_emitter.sh echo "" - echo "[Setup] linux-enable-ir-emitter installed successfully." + IRE_VERSION=$(linux-enable-ir-emitter -V 2>/dev/null || echo "linux-enable-ir-emitter") + echo "[Setup] $IRE_VERSION installed successfully." read -p "[Setup] Would you like to configure the IR emitter now? [Y/n] " -n 1 -r echo if [[ ! $REPLY =~ ^[Nn]$ ]]; then From 189251b3ead9f77169c44e1993195c292f114688 Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Mon, 23 Feb 2026 12:52:03 +0100 Subject: [PATCH 3/9] feat: show active provider in config output and fix ir emitter path missing --- src/service/auth_engine.cpp | 45 ++++++++++++++++++++++++++++--------- src/service/auth_engine.hpp | 3 +++ src/service/config.cpp | 10 +++++++++ src/service/main.cpp | 8 +++++-- src/service/utils.cpp | 25 ++++++++++++++++++++- src/service/utils.hpp | 3 +++ 6 files changed, 81 insertions(+), 13 deletions(-) diff --git a/src/service/auth_engine.cpp b/src/service/auth_engine.cpp index 3749eb53..1b5df2ae 100644 --- a/src/service/auth_engine.cpp +++ b/src/service/auth_engine.cpp @@ -97,15 +97,26 @@ bool AuthEngine::init(const fs::path &config_path) { void AuthEngine::initializeActiveCameras() { if (active_cameras.empty()) { active_cameras.reserve(config.camera_defs.size()); - std::transform(config.camera_defs.begin(), config.camera_defs.end(), - std::back_inserter(active_cameras), [&](const auto &def) { - ActiveCamera ac; - ac.config = def; // Copy config - log_info("Initializing Camera: " + def.id + " (" + - def.type + ") at " + def.path); - ac.cam = camera_factory_(def); - return ac; - }); + std::transform( + config.camera_defs.begin(), config.camera_defs.end(), + std::back_inserter(active_cameras), [&](const auto &def) { + ActiveCamera ac; + ac.config = def; // Copy config + log_info("Initializing Camera: " + def.id + " (" + def.type + + ") at " + def.path); + if (def.type == "ir") { + std::string ir_ver = linuxcampam::getIREmitterVersion( + config.ir_emitter_path.string()); + if (!ir_ver.empty()) { + log_info("IR Emitter Engine: " + config.ir_emitter_path.string() + + " (" + ir_ver + ")"); + } else { + log_info("IR Emitter Engine: Not Installed"); + } + } + ac.cam = camera_factory_(def); + return ac; + }); } } @@ -138,6 +149,7 @@ bool AuthEngine::loadModels() { cv::ocl::setUseOpenCL(true); backend_id = cv::dnn::DNN_BACKEND_OPENCV; target_id = cv::dnn::DNN_TARGET_OPENCL; + active_provider = "OpenCL"; log_info("Selecting OpenCL Backend."); // Log the OpenCL device name for assurance cv::ocl::Device dev = cv::ocl::Device::getDefault(); @@ -147,11 +159,18 @@ bool AuthEngine::loadModels() { "detected. Falling back to CPU."); backend_id = cv::dnn::DNN_BACKEND_OPENCV; target_id = cv::dnn::DNN_TARGET_CPU; + active_provider = "CPU (Fallback)"; } break; } } + // If active_provider is still empty (e.g., config.provider_priority is empty, + // though defaults are set) + if (active_provider.empty()) { + active_provider = "CPU (Default)"; + } + try { log_info("Loading Detector: " + detection_model_path.string()); @@ -1043,5 +1062,11 @@ void AuthEngine::recordAuthAttempt(const std::string &username, bool success) { } std::string AuthEngine::getConfigString() const { - return config.toString(); + std::string config_output = config.toString(); + + config_output += " Active Provider: " + + (active_provider.empty() ? "None" : active_provider) + + "\n\n"; + + return config_output; } diff --git a/src/service/auth_engine.hpp b/src/service/auth_engine.hpp index 53b1e286..fad6f21c 100644 --- a/src/service/auth_engine.hpp +++ b/src/service/auth_engine.hpp @@ -68,6 +68,7 @@ class AuthEngine { // Config visibility [[nodiscard]] std::string getConfigString() const; + [[nodiscard]] const Configuration &getConfig() const { return config; } // Allows to swap out the camera implementation (e.g., using a mock for // testing). @@ -139,4 +140,6 @@ class AuthEngine { mutable std::mutex lockout_mutex_; [[nodiscard]] bool isUserLockedOut(const std::string &username); void recordAuthAttempt(const std::string &username, bool success); + + std::string active_provider; }; diff --git a/src/service/config.cpp b/src/service/config.cpp index 80629dc9..4e3ab0bd 100644 --- a/src/service/config.cpp +++ b/src/service/config.cpp @@ -350,6 +350,16 @@ std::string Configuration::toString() const { if (!cam.enroll_hdr.empty()) { ss << " Enroll HDR: " << cam.enroll_hdr << "\n"; } + if (cam.type == "ir") { + std::string ir_ver = + linuxcampam::getIREmitterVersion(ir_emitter_path.string()); + if (!ir_ver.empty()) { + ss << " IR Emitter Path: " << ir_emitter_path.string() << "\n"; + ss << " IR Emitter Version: " << ir_ver << "\n"; + } else { + ss << " IR Emitter: Not Installed\n"; + } + } ss << "\n"; } diff --git a/src/service/main.cpp b/src/service/main.cpp index 23e78ad0..3917b43e 100644 --- a/src/service/main.cpp +++ b/src/service/main.cpp @@ -87,10 +87,14 @@ void handle_client(int client_fd, AuthEngine &engine) { break; } case Command::GET_VERSION: { + std::string ir_status = linuxcampam::getIREmitterVersion( + engine.getConfig().ir_emitter_path.string()); + std::string ir_append = + ir_status.empty() ? "" : " (IR Emitter: " + ir_status + ")"; #ifdef LINUXCAMPAM_VERSION - response = LINUXCAMPAM_VERSION; + response = std::string(LINUXCAMPAM_VERSION) + ir_append; #else - response = "Unknown"; + response = "Unknown" + ir_append; #endif break; } diff --git a/src/service/utils.cpp b/src/service/utils.cpp index 044afff6..e0b5712d 100644 --- a/src/service/utils.cpp +++ b/src/service/utils.cpp @@ -1,7 +1,9 @@ #include "utils.hpp" -#include "constants.hpp" // For MAX_USERNAME_LENGTH +#include "constants.hpp" + #include +#include #include #include #include @@ -114,6 +116,27 @@ std::vector> enumerateCameras() { return enumerateCameras(backend); } +std::string getIREmitterVersion(const std::string &path) { + std::string version = ""; + if (path.empty()) + return version; + + std::string cmd = path + " -V 2>/dev/null"; + // NOLINTNEXTLINE(cert-env33-c) + FILE *fp = popen(cmd.c_str(), "r"); + if (fp) { + constexpr size_t buf_size = 128; + std::array buf = {}; + if (fgets(buf.data(), static_cast(buf.size()), fp)) { + version = buf.data(); + if (!version.empty() && version.back() == '\n') + version.pop_back(); + } + pclose(fp); + } + return version; +} + bool isValidUsername(std::string_view username) { if (username.empty() || username.length() > linuxcampam::MAX_USERNAME_LENGTH) return false; diff --git a/src/service/utils.hpp b/src/service/utils.hpp index 02d7c152..3fd6d951 100644 --- a/src/service/utils.hpp +++ b/src/service/utils.hpp @@ -88,6 +88,9 @@ enumerateCameras(const ICameraBackend &backend); [[nodiscard]] std::vector> enumerateCameras(); +// Helpers +[[nodiscard]] std::string getIREmitterVersion(const std::string &path); + // Security Utils [[nodiscard]] bool isValidUsername(std::string_view username); From 271ef78b7bb6d740007546a8af07495faf01e377 Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Mon, 23 Feb 2026 13:12:02 +0100 Subject: [PATCH 4/9] refactor: replace active_provider member with getActiveProvider method --- src/service/auth_engine.cpp | 35 ++++++++++++++++++++++++----------- src/service/auth_engine.hpp | 3 +-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/service/auth_engine.cpp b/src/service/auth_engine.cpp index 1b5df2ae..498b43d0 100644 --- a/src/service/auth_engine.cpp +++ b/src/service/auth_engine.cpp @@ -149,7 +149,6 @@ bool AuthEngine::loadModels() { cv::ocl::setUseOpenCL(true); backend_id = cv::dnn::DNN_BACKEND_OPENCV; target_id = cv::dnn::DNN_TARGET_OPENCL; - active_provider = "OpenCL"; log_info("Selecting OpenCL Backend."); // Log the OpenCL device name for assurance cv::ocl::Device dev = cv::ocl::Device::getDefault(); @@ -159,18 +158,11 @@ bool AuthEngine::loadModels() { "detected. Falling back to CPU."); backend_id = cv::dnn::DNN_BACKEND_OPENCV; target_id = cv::dnn::DNN_TARGET_CPU; - active_provider = "CPU (Fallback)"; } break; } } - // If active_provider is still empty (e.g., config.provider_priority is empty, - // though defaults are set) - if (active_provider.empty()) { - active_provider = "CPU (Default)"; - } - try { log_info("Loading Detector: " + detection_model_path.string()); @@ -1061,12 +1053,33 @@ void AuthEngine::recordAuthAttempt(const std::string &username, bool success) { } } +std::string AuthEngine::getActiveProvider() const { + for (const auto &prov : config.provider_priority) { + if (prov == "CUDA") { + /* + if (cv::cuda::getCudaEnabledDeviceCount() > 0) { + return "CUDA"; + } + */ + } else if (prov == "OpenVINO") { + return "OpenVINO"; + } else if (prov == "OpenCL") { + if (cv::ocl::haveOpenCL()) { + return "OpenCL"; + } else { + return "CPU (Fallback)"; + } + } + } + return "CPU (Default)"; +} + std::string AuthEngine::getConfigString() const { std::string config_output = config.toString(); - config_output += " Active Provider: " + - (active_provider.empty() ? "None" : active_provider) + - "\n\n"; + std::string provider = getActiveProvider(); + config_output += + " Active Provider: " + (provider.empty() ? "None" : provider) + "\n\n"; return config_output; } diff --git a/src/service/auth_engine.hpp b/src/service/auth_engine.hpp index fad6f21c..39263a5a 100644 --- a/src/service/auth_engine.hpp +++ b/src/service/auth_engine.hpp @@ -69,6 +69,7 @@ class AuthEngine { // Config visibility [[nodiscard]] std::string getConfigString() const; [[nodiscard]] const Configuration &getConfig() const { return config; } + [[nodiscard]] std::string getActiveProvider() const; // Allows to swap out the camera implementation (e.g., using a mock for // testing). @@ -140,6 +141,4 @@ class AuthEngine { mutable std::mutex lockout_mutex_; [[nodiscard]] bool isUserLockedOut(const std::string &username); void recordAuthAttempt(const std::string &username, bool success); - - std::string active_provider; }; From 4951a925fa9834b4bd5c26bacc92e3e4a6763a8f Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Tue, 24 Feb 2026 01:22:27 +0100 Subject: [PATCH 5/9] feat: Simplify backend provider priority to OpenCL and CPU by removing CUDA and OpenVINO support, and refactor `getIREmitterVersion` to use `std::string_view`. --- README.md | 10 +++++++- man/linuxcampam.conf.5.md | 2 +- src/service/auth_engine.cpp | 48 +++++++++++++------------------------ src/service/config.cpp | 2 +- src/service/utils.cpp | 27 +++++++++++---------- src/service/utils.hpp | 2 +- 6 files changed, 42 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index cbaff1f6..0fe9a208 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,15 @@ > Face unlock for Linux, like Windows Helloâ„¢. Any webcam works; IR recommended for best results. -LinuxCamPAM provides seamless face unlock for Linux `sudo`, `login`, and login/lock screens (GDM, SDDM, LightDM) using OpenCV and AI models (YuNet/SFace). I built it to solve my own need for speed and reliability, supporting hardware acceleration (OpenCL, CUDA) and smart dual-camera configurations (IR + RGB). Virtually any USB webcam works out of the box. +LinuxCamPAM provides seamless face unlock for Linux `sudo`, `login`, and login/lock screens (GDM, SDDM, LightDM) using OpenCV and AI models (YuNet/SFace). I built it to solve my own need for speed and reliability, supporting hardware acceleration (OpenCL) and smart dual-camera configurations (IR + RGB). Virtually any USB webcam works out of the box. + +## Why This Exists Now + +There are existing tools like `howdy`, but they often suffer from being slow, lacking hardware acceleration, or failing completely with modern Python environments and D-Bus integration under Wayland. + +- **Why OpenCV C++**: It runs instantly. No lag when typing `sudo`. +- **Why ONNX + YuNet + SFace**: Replaces the sluggish `dlib` backend for state-of-the-art face detection and recognition. +- **Native Backends**: Native OpenVINO is not enabled in the default static build to maintain a small package size (~15MB vs ~300MB+). OpenCL provides near-native performance for this use case without the bloat. ## Motivation diff --git a/man/linuxcampam.conf.5.md b/man/linuxcampam.conf.5.md index cb891c6b..bc5df7a6 100644 --- a/man/linuxcampam.conf.5.md +++ b/man/linuxcampam.conf.5.md @@ -57,7 +57,7 @@ The file is in standard INI format. Sections and keys are case-insensitive. **provider_priority** = *LIST* : Comma-separated list of hardware backends to try. - (Default: `OpenCL,OpenVINO,CUDA,CPU`). + (Default: `OpenCL,CPU`). **camera_path_ir** = *PATH* : Path to IR camera (e.g., `/dev/video2`). diff --git a/src/service/auth_engine.cpp b/src/service/auth_engine.cpp index 498b43d0..80da82a4 100644 --- a/src/service/auth_engine.cpp +++ b/src/service/auth_engine.cpp @@ -129,22 +129,8 @@ bool AuthEngine::loadModels() { int backend_id = cv::dnn::DNN_BACKEND_OPENCV; int target_id = cv::dnn::DNN_TARGET_CPU; - for (const auto &prov : config.provider_priority) { - if (prov == "CUDA") { - /* - if (cv::cuda::getCudaEnabledDeviceCount() > 0) { - backend_id = cv::dnn::DNN_BACKEND_CUDA; - target_id = cv::dnn::DNN_TARGET_CUDA; - log_info("Selecting CUDA Backend."); - break; - } - */ - } else if (prov == "OpenVINO") { - backend_id = cv::dnn::DNN_BACKEND_INFERENCE_ENGINE; - target_id = cv::dnn::DNN_TARGET_CPU; - log_info("Selecting OpenVINO Backend."); - break; - } else if (prov == "OpenCL") { + for (std::string_view prov : config.provider_priority) { + if (prov == "OpenCL") { if (cv::ocl::haveOpenCL()) { cv::ocl::setUseOpenCL(true); backend_id = cv::dnn::DNN_BACKEND_OPENCV; @@ -153,13 +139,19 @@ bool AuthEngine::loadModels() { // Log the OpenCL device name for assurance cv::ocl::Device dev = cv::ocl::Device::getDefault(); log_info("Hardware Device: " + dev.name() + " " + dev.version()); + break; } else { - log_warn("OpenCL requested but not " - "detected. Falling back to CPU."); - backend_id = cv::dnn::DNN_BACKEND_OPENCV; - target_id = cv::dnn::DNN_TARGET_CPU; + log_warn("OpenCL requested but not detected. Trying next provider " + "fallback."); } + } else if (prov == "CPU") { + backend_id = cv::dnn::DNN_BACKEND_OPENCV; + target_id = cv::dnn::DNN_TARGET_CPU; + log_info("Selecting CPU Backend."); break; + } else { + log_warn("Unsupported or unrecognized backend requested: " + + std::string(prov)); } } @@ -1054,21 +1046,13 @@ void AuthEngine::recordAuthAttempt(const std::string &username, bool success) { } std::string AuthEngine::getActiveProvider() const { - for (const auto &prov : config.provider_priority) { - if (prov == "CUDA") { - /* - if (cv::cuda::getCudaEnabledDeviceCount() > 0) { - return "CUDA"; - } - */ - } else if (prov == "OpenVINO") { - return "OpenVINO"; - } else if (prov == "OpenCL") { + for (std::string_view prov : config.provider_priority) { + if (prov == "OpenCL") { if (cv::ocl::haveOpenCL()) { return "OpenCL"; - } else { - return "CPU (Fallback)"; } + } else if (prov == "CPU") { + return "CPU"; } } return "CPU (Default)"; diff --git a/src/service/config.cpp b/src/service/config.cpp index 4e3ab0bd..3509123d 100644 --- a/src/service/config.cpp +++ b/src/service/config.cpp @@ -268,7 +268,7 @@ void Configuration::parse_ini_into_self( } if (provider_priority.empty()) { Logger::log(LogLevel::DEBUG, "Using defaults for provider_priority"); - provider_priority = {"OpenCL", "OpenVINO", "CUDA", "CPU"}; + provider_priority = {"OpenCL", "CPU"}; } for (const auto &p : provider_priority) { diff --git a/src/service/utils.cpp b/src/service/utils.cpp index e0b5712d..238ba611 100644 --- a/src/service/utils.cpp +++ b/src/service/utils.cpp @@ -116,24 +116,25 @@ std::vector> enumerateCameras() { return enumerateCameras(backend); } -std::string getIREmitterVersion(const std::string &path) { - std::string version = ""; +std::string getIREmitterVersion(std::string_view path) { if (path.empty()) - return version; + return {}; - std::string cmd = path + " -V 2>/dev/null"; + std::string cmd = std::string(path) + " -V 2>/dev/null"; // NOLINTNEXTLINE(cert-env33-c) FILE *fp = popen(cmd.c_str(), "r"); - if (fp) { - constexpr size_t buf_size = 128; - std::array buf = {}; - if (fgets(buf.data(), static_cast(buf.size()), fp)) { - version = buf.data(); - if (!version.empty() && version.back() == '\n') - version.pop_back(); - } - pclose(fp); + if (!fp) + return {}; + + std::string version; + constexpr size_t buf_size = 128; + std::array buf{}; + if (fgets(buf.data(), static_cast(buf.size()), fp)) { + version = buf.data(); + if (!version.empty() && version.back() == '\n') + version.pop_back(); } + pclose(fp); return version; } diff --git a/src/service/utils.hpp b/src/service/utils.hpp index 3fd6d951..2a4758d9 100644 --- a/src/service/utils.hpp +++ b/src/service/utils.hpp @@ -89,7 +89,7 @@ enumerateCameras(const ICameraBackend &backend); enumerateCameras(); // Helpers -[[nodiscard]] std::string getIREmitterVersion(const std::string &path); +[[nodiscard]] std::string getIREmitterVersion(std::string_view path); // Security Utils [[nodiscard]] bool isValidUsername(std::string_view username); From e9b2859f146445daff344a318a763a91baa710de Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Fri, 13 Mar 2026 12:14:50 +0100 Subject: [PATCH 6/9] ci: fix riscv64 docker build segmentation fault --- docker/Dockerfile.riscv64 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/Dockerfile.riscv64 b/docker/Dockerfile.riscv64 index 7c53411c..c8fcc884 100644 --- a/docker/Dockerfile.riscv64 +++ b/docker/Dockerfile.riscv64 @@ -73,11 +73,10 @@ FROM debian:sid-slim ENV DEBIAN_FRONTEND=noninteractive # Install Runtime Dependencies -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get install -y --no-install-recommends \ libpam0g \ libpam-runtime \ v4l-utils \ - systemd \ libgfortran5 \ libgomp1 \ libatomic1 \ From fb4fc0c3671fd4c8bddeacbb10bca992397c63b7 Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Sun, 22 Mar 2026 19:45:53 +0100 Subject: [PATCH 7/9] refactor: introduce `createCommand` helper for defining CLI commands and refactor `init` command to use it. --- .clangd | 4 ++++ CMakeLists.txt | 2 +- src/service/auth_engine.hpp | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 00000000..2742f255 --- /dev/null +++ b/.clangd @@ -0,0 +1,4 @@ +CompileFlags: + CompilationDatabase: "." + Add: [-std=gnu++17] + Compiler: /usr/bin/c++ diff --git a/CMakeLists.txt b/CMakeLists.txt index 29a8352b..1e353723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,7 +167,7 @@ else() googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip URL_HASH SHA256=1f357c27ca988c3f7c6b4bf68a9395005ac6761f034046e9dde0896e3aba00e4 - ${GTEST_DOWNLOAD_ARGS} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) FetchContent_MakeAvailable(googletest) endif() diff --git a/src/service/auth_engine.hpp b/src/service/auth_engine.hpp index 39263a5a..344cb766 100644 --- a/src/service/auth_engine.hpp +++ b/src/service/auth_engine.hpp @@ -4,13 +4,16 @@ #include "icamera.hpp" #include +#include #include #include +#include #include #include #include #include #include +#include #include namespace fs = std::filesystem; From f29b59fa813b69574c0be17314fbc03737bcce61 Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Fri, 27 Mar 2026 19:40:06 +0100 Subject: [PATCH 8/9] fix: Prevent setup script from exiting fatally when no cameras are detected in non-interactive environments and adjust changelog formatting. --- debian/changelog | 68 ++++++++++++++++++++--------------------- scripts/setup_config.sh | 37 +++++++++++++--------- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/debian/changelog b/debian/changelog index 116150d4..db397df2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,78 +1,78 @@ linuxcampam (0.9.7.2-1) unstable; urgency=medium -* Fix: Correctly parse [Auth] and [Capture] configuration sections -* Fix: Ensure documentation consistency regarding permissions and configuration -* Fix: Add preinst script to archive conflicting manual installations to improve update stability -* Hotfix release for v0.9.7 issues + * Fix: Correctly parse [Auth] and [Capture] configuration sections + * Fix: Ensure documentation consistency regarding permissions and configuration + * Fix: Add preinst script to archive conflicting manual installations to improve update stability + * Hotfix release for v0.9.7 issues -- Vladimir Orlinski Fri, 30 Jan 2026 00:20:00 +0100 linuxcampam (0.9.7-1) unstable; urgency=medium -* Bump version to 0.9.7 to match project version -* Refined build process and docker optimization -* Upgrade OpenCV to 4.12.0 (statically linked) -* Update Face Detection models (YuNet 2023mar) + * Bump version to 0.9.7 to match project version + * Refined build process and docker optimization + * Upgrade OpenCV to 4.12.0 (statically linked) + * Update Face Detection models (YuNet 2023mar) -- Vladimir Orlinski Thu, 29 Jan 2026 13:50:00 +0100 linuxcampam (0.9.6-1) unstable; urgency=medium -* Dynamic debug logging via CLI (`linuxcampam debug on/off`) -* Version command for CLI and daemon -* Fix missing systemd service file in debian package -* Fix missing PAM config file in debian package -* Remove bloated -dev dependencies from runtime + * Dynamic debug logging via CLI (`linuxcampam debug on/off`) + * Version command for CLI and daemon + * Fix missing systemd service file in debian package + * Fix missing PAM config file in debian package + * Remove bloated -dev dependencies from runtime -- Vladimir Orlinski Tue, 07 Jan 2026 13:30:00 +0100 linuxcampam (0.9.5-1) unstable; urgency=medium -* GPU sync option to prevent OpenCL hangs on some AMD GPUs -* Fix missing i386/riscv64 debs in releases -* Add .dockerignore for cleaner cross-arch builds -* Auto-clear OpenCL kernel cache on startup to prevent upgrade issues + * GPU sync option to prevent OpenCL hangs on some AMD GPUs + * Fix missing i386/riscv64 debs in releases + * Add .dockerignore for cleaner cross-arch builds + * Auto-clear OpenCL kernel cache on startup to prevent upgrade issues -- Vladimir Orlinski Mon, 06 Jan 2026 15:00:00 +0100 linuxcampam (0.9.4-1) unstable; urgency=medium -* Feat: Implement security rate-limiting and lockout mechanism to prevent brute-force attacks. -* Feat: Add `show-config` CLI command for runtime configuration inspection. -* Security: Add comprehensive regression tests for lockout logic. + * Feat: Implement security rate-limiting and lockout mechanism to prevent brute-force attacks. + * Feat: Add `show-config` CLI command for runtime configuration inspection. + * Security: Add comprehensive regression tests for lockout logic. -- Vladimir Orlinski Sun, 05 Jan 2026 10:56:00 +0100 linuxcampam (0.9.3.3-1) unstable; urgency=medium -* Fix: Include AI models in Docker-based cross-arch builds (i386, riscv64). -* Fix: Preserve versioned .deb filenames in release artifacts. -* Note: Version bumped to clean test the release workflow. + * Fix: Include AI models in Docker-based cross-arch builds (i386, riscv64). + * Fix: Preserve versioned .deb filenames in release artifacts. + * Note: Version bumped to clean test the release workflow. -- Vladimir Orlinski Fri, 03 Jan 2026 21:41:00 +0100 linuxcampam (0.9.3.1-1) unstable; urgency=medium -* Multi-arch support (AARCH64, i386, RISC-V). -* Update Dockerfiles for packaging. -* Secure release workflow. + * Multi-arch support (AARCH64, i386, RISC-V). + * Update Dockerfiles for packaging. + * Secure release workflow. -- Vladimir Orlinski Sat, 03 Jan 2026 02:20:00 +0100 linuxcampam (0.9.2-1) unstable; urgency=medium -* Portable camera detection via V4L2 enumeration (no hardcoded paths). -* Portable GPU detection via sysfs (lspci no longer required). -* Setup fails with clear error if no cameras detected. -* Better IR emitter script guidance for non-Rust systems. + * Portable camera detection via V4L2 enumeration (no hardcoded paths). + * Portable GPU detection via sysfs (lspci no longer required). + * Setup fails with clear error if no cameras detected. + * Better IR emitter script guidance for non-Rust systems. -- Vladimir Orlinski Sat, 28 Dec 2025 23:35:00 +0100 linuxcampam (1.0.0-1) unstable; urgency=medium -* Initial release. -* Bundled stable ONNX models (2022mar/2021dec). -* Feature: Automatic PAM configuration via pam-auth-update. -* Feature: Rusticl OpenCL support enabled by default. + * Initial release. + * Bundled stable ONNX models (2022mar/2021dec). + * Feature: Automatic PAM configuration via pam-auth-update. + * Feature: Rusticl OpenCL support enabled by default. -- Vladimir Orlinski Fri, 19 Dec 2025 20:10:00 +0100 diff --git a/scripts/setup_config.sh b/scripts/setup_config.sh index e854b4ea..528b478c 100755 --- a/scripts/setup_config.sh +++ b/scripts/setup_config.sh @@ -128,20 +128,29 @@ elif [ -n "$RGB_CAM" ]; then else echo "" echo "==============================================" - echo "[Setup] FATAL: No cameras detected!" - echo "==============================================" - echo "" - echo "LinuxCamPAM requires at least one camera to function." - echo "" - echo "Troubleshooting:" - echo " 1. Check if cameras are connected: ls -la /dev/video*" - echo " 2. Check camera details: v4l2-ctl --list-devices" - echo " 3. Verify permissions: groups \$USER | grep video" - echo "" - echo "If you have a camera but it wasn't detected, please" - echo "manually configure it in: $CONFIG_FILE" - echo "" - exit 1 + if [ ! -t 0 ] || [ "$DEBIAN_FRONTEND" = "noninteractive" ]; then + echo "[Setup] WARNING: No cameras detected!" + echo "==============================================" + echo "" + echo "Running non-interactively. Ignoring missing camera to continue setup." + echo "Please manually configure cameras later in: $CONFIG_FILE" + echo "" + else + echo "[Setup] FATAL: No cameras detected!" + echo "==============================================" + echo "" + echo "LinuxCamPAM requires at least one camera to function." + echo "" + echo "Troubleshooting:" + echo " 1. Check if cameras are connected: ls -la /dev/video*" + echo " 2. Check camera details: v4l2-ctl --list-devices" + echo " 3. Verify permissions: groups \$USER | grep video" + echo "" + echo "If you have a camera but it wasn't detected, please" + echo "manually configure it in: $CONFIG_FILE" + echo "" + exit 1 + fi fi echo "[Setup] Checking System RAM and CPU..." From fda08755c8e49bca13127e63b5d65513e881c6e7 Mon Sep 17 00:00:00 2001 From: Vladimir Orlinski Date: Sat, 28 Mar 2026 12:37:16 +0100 Subject: [PATCH 9/9] fix: update package dependencies to include libpam-runtime and remove systemd --- CMakeLists.txt | 2 +- debian/control | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e353723..6ed6a9be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,7 @@ set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") set(CPACK_GENERATOR "DEB") # Runtime dependencies (not -dev packages) for Ubuntu 24.04 # OpenCV is statically linked, so we don't need libopencv-* packages -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libpam0g, libpam-runtime, v4l-utils, systemd, libgfortran5, libatlas3-base, libatomic1") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libpam0g, libpam-runtime, v4l-utils, libgfortran5, libatlas3-base, libatomic1") set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "rocm-opencl-runtime | intel-opencl-icd | nvidia-opencl-icd-340 | nvidia-opencl-icd") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/debian/postinst") diff --git a/debian/control b/debian/control index a5d1d2b9..a1f3403c 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Homepage: https://github.com/Vladush/LinuxCamPAM Package: linuxcampam Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libpam0g, systemd, v4l-utils +Depends: ${shlibs:Depends}, ${misc:Depends}, libpam0g, libpam-runtime, v4l-utils Recommends: rocm-opencl-runtime | intel-opencl-icd | nvidia-opencl-icd-340 | nvidia-opencl-icd Description: Face Authentication PAM Module (LinuxCamPAM) LinuxCamPAM is a Pluggable Authentication Module (PAM) that provides face