diff --git a/Makefile.am b/Makefile.am
index 1852983..b7aa1a3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,7 @@
## along with this program. If not, see .
##
## Authors: Daniel Kopecek
+## Jiri Vymazal
##
SUBDIRS=src/Tests/
diff --git a/doc/usbguard-daemon.8 b/doc/usbguard-daemon.8
index 18c72fe..dfdb285 100644
--- a/doc/usbguard-daemon.8
+++ b/doc/usbguard-daemon.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pandoc 1.17.0.3
+.\" Automatically generated by Pandoc 1.19.1
.\"
.TH "USBGUARD\-DAEMON" "8" "June 2016" "" ""
.hy
@@ -25,6 +25,11 @@ Enable debugging messages in the log.
.RS
.RE
.TP
+.B \f[B]\-f\f[]
+Enable classical daemon behavior (fork at start, sysV compliant).
+.RS
+.RE
+.TP
.B \f[B]\-s\f[]
Log to syslog.
.RS
@@ -41,7 +46,8 @@ Log to a file at \f[I]path\f[].
.RE
.TP
.B \f[B]\-p\f[] <\f[I]path\f[]>
-Write PID to a file at \f[I]path\f[].
+Write PID to a file at \f[I]path\f[] (default:
+\f[I]/var/run/usbguard.pid\f[]).
.RS
.RE
.TP
diff --git a/doc/usbguard-daemon.8.md b/doc/usbguard-daemon.8.md
index 3e2fcaf..581613d 100644
--- a/doc/usbguard-daemon.8.md
+++ b/doc/usbguard-daemon.8.md
@@ -19,6 +19,9 @@ The **usbguard-daemon** is the main component of the USBGuard software framework
**-d**
: Enable debugging messages in the log.
+**-f**
+: Enable classical daemon behavior (fork at start, sysV compliant).
+
**-s**
: Log to syslog.
@@ -29,7 +32,7 @@ The **usbguard-daemon** is the main component of the USBGuard software framework
: Log to a file at *path*.
**-p** <*path*>
-: Write PID to a file at *path*.
+: Write PID to a file at *path* (default: */var/run/usbguard.pid*).
**-c** <*path*>
: Load configuration from a file at *path* (default: */etc/usbguard/usbguard-daemon.conf*).
diff --git a/src/Common/Utility.cpp b/src/Common/Utility.cpp
index f84d2a8..237acfb 100644
--- a/src/Common/Utility.cpp
+++ b/src/Common/Utility.cpp
@@ -42,56 +42,6 @@
namespace usbguard
{
- void daemonize()
- {
- const ::pid_t pid = fork();
-
- switch(pid) {
- case 0: /* child */
- break;
- case -1: /* error */
- ::exit(EXIT_FAILURE);
- default: /* parent */
- ::exit(EXIT_SUCCESS);
- }
- //
- // Decouple from parent environment
- // - chdir to /
- // - create new process session
- // - reset umask
- // - cleanup file descriptors
- // - ???
- // - consider using libdaemon
- //
- if (::chdir("/") != 0) {
- ::exit(EXIT_FAILURE);
- }
- const ::pid_t sid = ::setsid();
- if (sid != 0) {
- ::exit(EXIT_FAILURE);
- }
- ::umask(::umask(077)|022);
- struct rlimit rlim;
- if (::getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
- ::exit(EXIT_FAILURE);
- }
- const int maxfd = (rlim.rlim_max == RLIM_INFINITY ? 1024 : rlim.rlim_max);
- for (int fd = 0; fd < maxfd; ++fd) {
- ::close(fd);
- }
- return;
- }
-
- bool writePID(const std::string& filepath)
- {
- std::ofstream pidstream(filepath, std::ios_base::trunc);
- if (!pidstream) {
- return false;
- }
- pidstream << numberToString(getpid()) << std::endl;
- return true;
- }
-
static void runCommandExecChild(const std::string& path, const std::vector& args)
{
struct rlimit rlim;
diff --git a/src/Common/Utility.hpp b/src/Common/Utility.hpp
index f722b22..54e1ea1 100644
--- a/src/Common/Utility.hpp
+++ b/src/Common/Utility.hpp
@@ -41,25 +41,6 @@
namespace usbguard
{
/**
- * Create a background process.
- *
- * Performs the following actions:
- * 1) fork a new process (parent process exists with 0)
- * 2) chdir to /
- * 3) creates a new process session
- * 4) resets umask
- * 5) closes all file descriptors
- * 6) Reinitialize logging for the child
- */
- void daemonize(void);
-
- /**
- * Writes the current PID to a file at filepath.
- * Returns true on success, otherwise returns false.
- */
- bool writePID(const std::string& filepath);
-
- /**
* Wrappers for the __builtin_expect function.
*/
#if defined(__GNUC__)
diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp
index b317c85..2a9a37c 100644
--- a/src/Daemon/Daemon.cpp
+++ b/src/Daemon/Daemon.cpp
@@ -15,6 +15,7 @@
// along with this program. If not, see .
//
// Authors: Daniel Kopecek
+// Jiri Vymazal
//
#ifdef HAVE_BUILD_CONFIG_H
#include
@@ -27,6 +28,8 @@
#include "usbguard/RuleParser.hpp"
#include "usbguard/Audit.hpp"
+#include
+
#include
#include
#include
@@ -112,6 +115,8 @@ namespace usbguard
_device_rules_with_port = false;
_restore_controller_device_state = false;
+
+ pid_fd = -1;
}
Daemon::~Daemon()
@@ -402,6 +407,10 @@ namespace usbguard
}
} while(!exit_loop);
+ if (pid_fd != -1) {
+ lockf(pid_fd, F_ULOCK, 0);
+ close(pid_fd);
+ }
IPCServer::stop();
_dm->stop();
USBGUARD_LOG(Trace) << "Leaving main loop.";
@@ -411,6 +420,73 @@ namespace usbguard
{
}
+ void Daemon::daemonize(const std::string &pid_file)
+ {
+ USBGUARD_LOG(Trace) << "Starting daemonization";
+
+ pid_t pid = 0;
+ pid_t original_pid = getpid();
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ sigprocmask(SIG_BLOCK, &mask, nullptr);
+ USBGUARD_SYSCALL_THROW("Daemonize", (pid = fork()) < 0);
+ if (pid > 0) {
+ constexpr int timeout_val = 5;
+ struct timespec timeout {timeout_val,0};
+ const time_t start = time(nullptr);
+ siginfo_t info;
+ do {
+ const int signum = sigtimedwait(&mask, &info, &timeout);
+ if (signum == SIGUSR1 && info.si_signo == SIGUSR1 && info.si_pid == pid) {
+ USBGUARD_LOG(Trace) << "Finished daemonization";
+ exit(EXIT_SUCCESS);
+ }
+ if (signum == -1 && errno == EAGAIN) {
+ break; /* timed out */
+ }
+ timeout.tv_sec = timeout_val - difftime(time(nullptr), start); /* avoid potentially endless loop */
+ } while(true);
+ throw Exception("Deamonize", "signal", "Waiting on pid file write timeout!");
+ }
+
+ /* Now we are forked */
+ USBGUARD_SYSCALL_THROW("Daemonize", setsid() < 0);
+ signal(SIGCHLD, SIG_IGN);
+
+ USBGUARD_SYSCALL_THROW("Daemonize", (pid_fd = open(pid_file.c_str(), O_RDWR|O_CREAT, 0640)) < 0);
+ USBGUARD_SYSCALL_THROW("Daemonize", (lockf(pid_fd, F_TLOCK, 0)) < 0);
+ USBGUARD_SYSCALL_THROW("Daemonize", (pid = fork()) < 0);
+ if (pid > 0) {
+ try {
+ std::string pid_str = std::to_string(pid);
+ USBGUARD_SYSCALL_THROW("Daemonize", write(pid_fd, pid_str.c_str(), pid_str.size()) != static_cast(pid_str.size()));
+ kill(original_pid, SIGUSR1);
+ exit(EXIT_SUCCESS);
+ }
+ catch(...) {
+ kill(pid, SIGKILL);
+ throw;
+ }
+ }
+
+ /* Now we are forked 2nd time */
+ umask(0047); /* no need for world-accessible or executable files */
+ chdir("/");
+ const std::array std_fds {{STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}};
+ int fd_null;
+ USBGUARD_SYSCALL_THROW("Daemonize", (fd_null = open("/dev/null", O_RDWR)) < 0);
+ /* We do not need to close all fds because there is only logging open at this point */
+ for (auto fd : std_fds) {
+ USBGUARD_SYSCALL_THROW("Daemonize", close(fd));
+ USBGUARD_SYSCALL_THROW("Daemonize", (dup2(fd_null, fd)) < 0);
+ }
+ close(fd_null);
+
+ USBGUARD_SYSCALL_THROW("Daemonize", (lockf(pid_fd, F_LOCK, 0)) < 0);
+ }
+
uint32_t Daemon::assignID()
{
return _ruleset.assignID();
diff --git a/src/Daemon/Daemon.hpp b/src/Daemon/Daemon.hpp
index cfd02d9..065deaf 100644
--- a/src/Daemon/Daemon.hpp
+++ b/src/Daemon/Daemon.hpp
@@ -15,6 +15,7 @@
// along with this program. If not, see .
//
// Authors: Daniel Kopecek
+// Jiri Vymazal
//
#pragma once
#ifdef HAVE_BUILD_CONFIG_H
@@ -72,6 +73,8 @@ namespace usbguard
void run();
/* Stop the daemon */
void quit();
+ /* Handle process daemonization */
+ void daemonize(const std::string& pid_file);
uint32_t assignID();
uint32_t upsertRule(const std::string& match_spec, const std::string& rule_spec, bool parent_insensitive = false);
@@ -112,6 +115,8 @@ namespace usbguard
ConfigFile _config;
RuleSet _ruleset;
+ int pid_fd;
+
std::string _device_manager_backend;
std::shared_ptr _dm;
diff --git a/src/Daemon/main.cpp b/src/Daemon/main.cpp
index 869c2e2..4b9b351 100644
--- a/src/Daemon/main.cpp
+++ b/src/Daemon/main.cpp
@@ -15,6 +15,7 @@
// along with this program. If not, see .
//
// Authors: Daniel Kopecek
+// Jiri Vymazal
//
#ifdef HAVE_BUILD_CONFIG_H
#include
@@ -37,9 +38,13 @@
static void setupCapabilities(void);
#endif
+#ifndef USBGUARD_PID_FILE
+#define USBGUARD_PID_FILE "/var/run/usbguard.pid"
+#endif
+
using namespace usbguard;
-const char * const G_optstring = "dskl:p:c:hWC";
+const char * const G_optstring = "dfskl:p:c:hWC";
static void printUsage(std::ostream& stream, const char *arg0)
{
@@ -47,6 +52,7 @@ static void printUsage(std::ostream& stream, const char *arg0)
stream << "Usage: " << filenameFromPath(std::string(arg0), true) << " [OPTIONS]" << std::endl;
stream << std::endl;
stream << " -d Enable debugging messages in the log." << std::endl;
+ stream << " -f Enable classical daemon forking behavior." << std::endl;
stream << " -s Log to syslog." << std::endl;
stream << " -k Log to console." << std::endl;
stream << " -l Log to a file at `path'." << std::endl;
@@ -68,8 +74,9 @@ int main(int argc, char *argv[])
bool log_file = false;
bool use_seccomp_whitelist = false;
bool drop_capabilities = false;
+ bool daemonize = false;
std::string log_file_path;
- std::string pid_file;
+ std::string pid_file = USBGUARD_PID_FILE;
std::string conf_file = "/etc/usbguard/usbguard-daemon.conf";
int opt;
@@ -79,6 +86,9 @@ int main(int argc, char *argv[])
case 'd':
debug_mode = true;
break;
+ case 'f':
+ daemonize = true;
+ break;
case 's':
log_syslog = true;
break;
@@ -144,6 +154,13 @@ int main(int argc, char *argv[])
if (!conf_file.empty()) {
daemon.loadConfiguration(conf_file);
}
+ if (daemonize) {
+ if (log_console && !log_syslog && !log_file) {
+ USBGUARD_LOG(Warning) << "You have selected to fork and log only to \
+ console, nothing will be logged after forking!";
+ }
+ daemon.daemonize(pid_file);
+ }
daemon.run();
ret = EXIT_SUCCESS;
}
diff --git a/src/Tests/Makefile.am b/src/Tests/Makefile.am
index 7d93474..a952d18 100644
--- a/src/Tests/Makefile.am
+++ b/src/Tests/Makefile.am
@@ -40,6 +40,7 @@ EXTRA_DIST=\
$(top_srcdir)/src/Tests/UseCase/001_cli_policy.sh \
$(top_srcdir)/src/Tests/UseCase/002_cli_devices.sh \
$(top_srcdir)/src/Tests/UseCase/003_cli_devices_dummy.sh \
+ $(top_srcdir)/src/Tests/UseCase/004_daemonize.sh \
$(top_srcdir)/src/Tests/UseCase/DummyDevices
LOG_DRIVER=\
@@ -62,7 +63,8 @@ TESTS=\
UseCase/000_executable.sh \
UseCase/001_cli_policy.sh \
UseCase/002_cli_devices.sh \
- UseCase/003_cli_devices_dummy.sh
+ UseCase/003_cli_devices_dummy.sh \
+ UseCase/004_daemonize.sh
check_PROGRAMS=\
test-unit \
diff --git a/src/Tests/UseCase/004_daemonize.sh b/src/Tests/UseCase/004_daemonize.sh
new file mode 100755
index 0000000..d59dad1
--- /dev/null
+++ b/src/Tests/UseCase/004_daemonize.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+#
+#
+# Copyright (C) 2016 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Authors: Jiri Vymazal
+#
+# Test whether the binaries are executable as expected (no linker errors, etc.)
+#
+source "${USBGUARD_TESTLIB_BASH}" || exit 129
+
+# TODO? Move to testlib
+export USBGUARD_TESTLIB_TMPDIR="$(mktemp -d --tmpdir usbguard-test.XXXXXX)"
+
+export config_path="${USBGUARD_TESTLIB_TMPDIR}/daemon.conf"
+export pidfile_path="${USBGUARD_TESTLIB_TMPDIR}/usbguard.pid"
+export logfile="${USBGUARD_TESTLIB_TMPDIR}/daemon.log"
+
+function test_cli_daemonize()
+{
+ sleep 5
+
+ if [ ! -f "$pidfile_path" ]; then
+ echo "Test error: PID file for usbguard not present"
+ exit 1
+ fi
+
+ if [ ! `pgrep usbguard` == `cat $pidfile_path` ]; then
+ echo "Test error: PID of usbguard daemon not present in PID file"
+ exit 1
+ fi
+}
+
+cat > "$config_path" <