Skip to content

Commit

Permalink
Add support for systemd daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
fergeben committed Apr 15, 2020
1 parent ae26640 commit 5253968
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set(SRC_FILES
src/grpc_service.cpp
protos/yess.grpc.pb.cc
protos/yess.pb.cc
src/daemonizer.cpp
)
set(INC_FILES
src/server.hpp
Expand All @@ -45,6 +46,7 @@ set(INC_FILES
src/grpc_service.hpp
protos/yess.grpc.pb.h
protos/yess.pb.h
src/daemonizer.hpp
)
add_library(${PROJECT_NAME}_objects OBJECT ${SRC_FILES} ${INCL_FILES})
add_subdirectory(utils)
Expand All @@ -69,6 +71,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${CONAN_LIBS} yessutils
gRPC::grpc++
gRPC::grpc++_reflection
protobuf::libprotobuf
-lsystemd
)

add_custom_target(
Expand Down
34 changes: 34 additions & 0 deletions src/daemonizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <exception>
#include <iostream>
#include <signal.h>
#include <systemd/sd-daemon.h>

#include "daemonizer.hpp"

namespace yess
{
int Daemonizer::run(int (*cb)(int argc, char **argv), int argc, char **argv)
{
int result = 1;
try {
sd_notify(0, "READY=1");
std::cerr << std::string("Yess daemon has successfully started up.")
<< std::endl
<< std::flush;

// start program
result = cb(argc, argv);

sd_notify(0, "STOPPING=1");
std::cerr << std::string("Yess daemon has been successfully shut down.")
<< std::endl
<< std::flush;
} catch (std::exception &ex) {
sd_notifyf(
0, "STATUS=Failed to start up: %s\n ERRNO=%i", ex.what(), result);
return result;
}
result = 0;
return result;
}
} // namespace yess
16 changes: 16 additions & 0 deletions src/daemonizer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

namespace yess
{
class Daemonizer
{
public:
// static int run(int (*cb)(const int &argc, const char **arbv));
static int run(int (*cb)(int argc, char **argv), int argc, char **argv);
/*
static int run(int (*cb)(const int &argc, const char **arbv),
const int &argc,
const char **argv);
*/
};
} // namespace yess
24 changes: 22 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
#include "daemonizer.hpp"
#include "server.hpp"
#include <iostream>

void sayhello_loop()
{
while (1) {
std::cout << "hello\n";
}
}

int main(int argc, char **argv)
{
yess::Server server(argc, argv);
return server.run();
/*
yess::Server server(argc, argv);
return server.run();
*/
// daemonize(sayhello_loop, "yess");
yess::Daemonizer::run(
[](int argc, char **argv) {
yess::Server server(argc, argv);
return server.run();
},
argc,
argv);
return 0;
}

76 changes: 73 additions & 3 deletions src/server.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <iostream>
#include <stdexcept>
#include <string>
#include <systemd/sd-daemon.h>

#include "argparse.hpp"
#include "db/repositories/sqlite_stream_repo.hpp"
Expand All @@ -12,6 +13,42 @@
using namespace yess;
using namespace db;

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

#include <signal.h>
#include <unistd.h>

namespace
{
// In the GNUC Library, sig_atomic_t is a typedef for int,
// which is atomic on all systems that are supported by the
// GNUC Library
volatile sig_atomic_t do_shutdown = 0;

// std::atomic is safe, as long as it is lock-free
std::atomic<bool> shutdown_requested = false;
static_assert(std::atomic<bool>::is_always_lock_free);
// or, at runtime: assert( shutdown_requested.is_lock_free() );
} // namespace

void my_signal_handler(int /*signum*/)
{
// ok, lock-free atomics
do_shutdown = 1;
shutdown_requested = true;

const char str[] = "received signal\n";
// ok, write is signal-safe
write(STDERR_FILENO, str, sizeof(str) - 1);

// UB, unsafe, internal buffers: std::cout << "received signal\n";
// UB, unsafe, allocates: std::vector<T>(20);
// unsafe, internal buffers: printf("received signal\n");
}

Server::Server(int argc, char **argv)
{
log::init_logger();
Expand Down Expand Up @@ -57,6 +94,10 @@ void Server::parse_args(int argc, char **argv)
exit(0);
}

if (argparser_["--daemon"] == true) {
daemon_ = true;
}

auto log_path = argparser_.get<std::string>("--log-path");
bool file_only = argparser_.get<bool>("--file-only");
if (!log_path.empty()) {
Expand Down Expand Up @@ -92,8 +133,19 @@ void Server::display_version()

std::string Server::conn_str() { return conn_str_; }

#include <thread>

int Server::run()
{
// setup signal handler
{
struct sigaction action;
action.sa_handler = my_signal_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGTERM, &action, NULL);
}

std::string server_address("0.0.0.0:" + std::to_string(port_));
Grpc_service service(conn_str_);

Expand All @@ -109,8 +161,26 @@ int Server::run()
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
log::info("yess listening on {}...", server_address);

// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
if (daemon_) {
// Watchdog thread (for systemd notification)
std::thread w_th([&]() {
// TODO: remove 5s delay for SIGTERM handling
while (!do_shutdown && !shutdown_requested.load()) {
// log::info("Notify systemd");
sd_notify(0, "WATCHDOG=1");
// WatchdogSec=6 (systemd) -> notify every 5 sec
std::this_thread::sleep_for(std::chrono::seconds(5));
}
server->Shutdown();
log::info("Server has been shut down.");
});
w_th.join();
}
std::thread grpc_th([&]() {
// Other thread is responsible for shutting down the server
server->Wait();
});
grpc_th.join();

return 0;
}
2 changes: 2 additions & 0 deletions src/server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class Server
private:
void parse_args(int argc, char **argv);
bool is_config_loaded_ = false;
bool daemon_ = false;
std::string log_path_;
argparse::ArgumentParser argparser_;
void daemon();
};
} // namespace yess
12 changes: 12 additions & 0 deletions yess.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=Yess service

[Service]
Type=simple
ExecStart=/usr/bin/yess --daemon
Restart=on-failure
KillMode=process
WatchdogSec=6

[Install]
WantedBy=multi-user.target

0 comments on commit 5253968

Please sign in to comment.