Simple C++ header only template for creating Linux daemons
A dæmon is a program that runs silently in the background.
Commonly, dæmon processes are created to offer a specific service.
Dæmon processes usually:
- Live for a long time;
- Started at boot time;
- Terminate only during shutdown;
- Have no controlling terminal.
daemonpp has a simple, straightforward api with callbacks to handle your daemon events.
#include "daemon.hpp"
using namespace daemonpp;
using namespace std::chrono_literals;
class my_daemon : public daemon
{
public:
void on_start(const dconfig& cfg) override {
/// Called once after daemon starts automatically with system startup or when you manually call `$ systemctl start my_daemon`
/// Initialize your code here...
dlog::info("my_daemon::on_start(): my_daemon version: " + cfg.get("version") + " started successfully!");
}
void on_update() override {
/// Called every DURATION set in set_update_duration()...
/// Update your code here...
dlog::info("my_daemon::on_update()");
if(++counter >= 3) {
dlog::trace("Stopping my_daemon after 3 updates.");
stop(EXIT_SUCCESS);
}
}
void on_stop() override {
/// Called once before daemon is about to exit with system shutdown or when you manually call `$ systemctl stop my_daemon`
/// Cleanup your code here...
dlog::info("my_daemon::on_stop()");
}
void on_reload(const dconfig& cfg) override {
/// Called once after your daemon's config fil is updated then reloaded with `$ systemctl reload my_daemon`
/// Handle your config updates here...
dlog::info("my_daemon::on_reload(): new daemon version from updated config: " + cfg.get("version"));
}
private:
int counter = 0;
};
int main(int argc, const char* argv[]) {
my_daemon dmn; // create a daemon instance
dmn.set_name("my_daemon"); // set daemon name to identify logs in syslog
dmn.set_update_duration(3s); // set duration to sleep before triggering the on_update callback 3 seconds
dmn.set_cwd("/"); // set daemon's current working directory to root /
dmn.run(argc, argv); // run your daemon
return EXIT_SUCCESS;
}
See examples
Let's assume your daemon project is called my_daemon
- First, clone this repository into a new folder named after your project:
git clone https://github.com/baderouaich/daemonpp my_daemon
Your project structure will be like this:
my_daemon
├── examples/ # example daemon projects to inspire from
├── include/ # include files contains daemonpp single header files
├── systemd/ # .service.in and .conf.in files to be configured by cmake
├── daemonpp.cpp # daemonpp.cpp sample daemon template
├── CMakeLists.txt # CMake project file
├── LICENSE # MIT License file
└── README.md
- Update your CMakeLists.txt file according to your project properties (name, version and description...)
cmake_minimum_required(VERSION 3.10)
project(my_daemon VERSION "0.0.1" DESCRIPTION "My daemon description" LANGUAGES CXX)
^ ^
│_____ update your project name |
|
│_____ update your project description
- Configure your project (to create .service and .conf required daemon files):
mkdir build && cd build
cmake ..
Now your project structure will look like this:
my_daemon
├── examples/
├── include/
├── systemd/
├── my_daemon.conf
├── my_daemon.service
├── my_daemon.cpp
├── CMakeLists.txt
├── LICENSE
└── README.md
That's it! you can now update your daemon code in the my_daemon.cpp file and remove examples/ folder if you don't need.
Also see how to:
sudo make install
sudo systemctl [enable|disable] my_daemon
Start
systemctl start my_daemon
will trigger the on_start() callback, providing your config values.
Stop
systemctl stop my_daemon
will trigger the on_stop() callback.
systemctl restart my_daemon
This is equivalent to
sudo systemctl stop your_daemon && sudo systemctl start your_daemon
if you change your .service or .conf files, and you reinstalled your daemon, you have to reload
your daemon by running:
systemctl reload my_daemon
will trigger the on_reload() callback, providing the new config values.
systemctl status my_daemon
- Stop daemon gracefully
systemctl stop my_daemon
systemctl disable my_daemon
- Delete daemon files
sudo make uninstall
Or
sudo rm -rf /etc/my_daemon/my_daemon.conf /etc/systemd/system/my_daemon.service /usr/bin/my_daemon
Use the built-in dlog static class which uses syslog internally. Then you can see your logs by:
- running
gnome-logs
gui if available. - using cat by running
cat /var/log/syslog | grep your_daemon_name
. - using tail
tail -f /var/log/syslog
to show all logs ortail -f /var/log/syslog | grep your_daemon_name
- using systemctl status by running
systemctl status your_daemon_name
- opening the
/var/log/syslog
file in a text editor and findyour_daemon_name
(not recommended since syslog can be huge).
- re-read configuration file upon SIGHUP
- relay information via event logging, often done using e.g., syslog(3)
- prevent against multiple instances via a lockfile
- allow for easy determination of PID via a pidfile
- include a system initialization script (for /etc/rc.d/, /etc/init.d/, systemd, …) for other Linux distros such as RedHat 4/5/6 or CentOS that use init scripts instead of systemd
- configuration file convention /etc/name.conf
If you face any problems feel free to open an issue at the issue tracker. If you feel the library is missing a feature, please raise a ticket on Github. Pull request are also welcome.