bgproc_service::read_pid_file(bp_sys::exit_status *exit_status) noexcept
{
const char *pid_file_c = pid_file.c_str();
- int fd = open(pid_file_c, O_CLOEXEC);
+ int fd = bp_sys::open(pid_file_c, O_CLOEXEC);
if (fd == -1) {
log(loglevel_t::ERROR, get_name(), ": read pid file: ", strerror(errno));
return pid_result_t::FAILED;
if (r < 0) {
// Could not read from PID file
log(loglevel_t::ERROR, get_name(), ": could not read from pidfile; ", strerror(errno));
- close(fd);
+ bp_sys::close(fd);
return pid_result_t::FAILED;
}
- close(fd);
+ bp_sys::close(fd);
pidbuf[r] = 0; // store nul terminator
bool valid_pid = false;
pid_t wait_r = waitpid(pid, exit_status, WNOHANG);
if (wait_r == -1 && errno == ECHILD) {
// We can't track this child - check process exists:
- if (kill(pid, 0) == 0 || errno != ESRCH) {
+ if (bp_sys::kill(pid, 0) == 0 || errno != ESRCH) {
tracking_child = false;
return pid_result_t::OK;
}
#include <list>
#include <utility>
#include <string>
+#include <sstream>
#include "service.h"
#include "proc-service.h"
sset.remove_service(&p);
}
+void test_bgproc_smooth_recover()
+{
+ using namespace std;
+
+ service_set sset;
+
+ string command = "test-command";
+ list<pair<unsigned,unsigned>> command_offsets;
+ command_offsets.emplace_back(0, command.length());
+ std::list<prelim_dep> depends;
+
+ bgproc_service p {&sset, "testproc", std::move(command), command_offsets, depends};
+ init_service_defaults(p);
+ p.set_smooth_recovery(true);
+ p.set_restart_delay(time_val {0, 1000});
+ p.set_pid_file("/run/daemon.pid");
+ sset.add_service(&p);
+
+ p.start(true);
+ sset.process_queues();
+
+ base_process_service_test::exec_succeeded(&p);
+ sset.process_queues();
+
+ // pid_t first_instance = bp_sys::last_forked_pid;
+ pid_t daemon_instance = ++bp_sys::last_forked_pid;
+
+ // Set up the pid file content with the pid of the daemon
+ stringstream str;
+ str << daemon_instance << std::flush;
+ string pid_file_content = str.str();
+ vector<char> pid_file_content_v(pid_file_content.begin(), pid_file_content.end());
+ bp_sys::supply_file_content("/run/daemon.pid", std::move(pid_file_content_v));
+
+ assert(p.get_state() == service_state_t::STARTING);
+
+ base_process_service_test::handle_exit(&p, 0); // exit the launch process
+ sset.process_queues();
+
+ // daemon process has been started now, state should be STARTED
+ assert(p.get_state() == service_state_t::STARTED);
+ assert(daemon_instance == bp_sys::last_forked_pid);
+
+ base_process_service_test::handle_exit(&p, 0); // exit the daemon process
+
+ // since time hasn't been changed, we expect that the process has not yet been re-launched:
+ assert(p.get_state() == service_state_t::STARTED);
+ assert(daemon_instance == bp_sys::last_forked_pid);
+
+ event_loop.advance_time(time_val {0, 1000});
+ sset.process_queues();
+
+ // Now a new process should've been launched:
+ assert(event_loop.active_timers.size() == 0);
+ assert(daemon_instance + 1 == bp_sys::last_forked_pid);
+ assert(p.get_state() == service_state_t::STARTED);
+ assert(event_loop.active_timers.size() == 0);
+
+ base_process_service_test::exec_succeeded(&p);
+ sset.process_queues();
+
+ assert(event_loop.active_timers.size() == 0);
+
+ daemon_instance = ++bp_sys::last_forked_pid;
+ str.str("");
+ str << daemon_instance << std::flush;
+ pid_file_content = str.str();
+ pid_file_content_v = std::vector<char>(pid_file_content.begin(), pid_file_content.end());
+ bp_sys::supply_file_content("/run/daemon.pid", std::move(pid_file_content_v));
+
+ base_process_service_test::handle_exit(&p, 0);
+ sset.process_queues();
+
+ assert(p.get_state() == service_state_t::STARTED);
+
+ assert(event_loop.active_timers.size() == 0);
+
+ // Now run through it again
+
+ base_process_service_test::handle_exit(&p, 0); // exit the daemon process
+
+ // since time hasn't been changed, we expect that the process has not yet been re-launched:
+ assert(p.get_state() == service_state_t::STARTED);
+ assert(daemon_instance == bp_sys::last_forked_pid);
+
+ event_loop.advance_time(time_val {0, 1000});
+ sset.process_queues();
+
+ // Now a new process should've been launched:
+ assert(event_loop.active_timers.size() == 0);
+ assert(daemon_instance + 1 == bp_sys::last_forked_pid);
+ assert(p.get_state() == service_state_t::STARTED);
+ assert(event_loop.active_timers.size() == 0);
+
+ base_process_service_test::exec_succeeded(&p);
+ sset.process_queues();
+
+ assert(event_loop.active_timers.size() == 0);
+
+ daemon_instance = ++bp_sys::last_forked_pid;
+ str.str("");
+ str << daemon_instance << std::flush;
+ pid_file_content = str.str();
+ pid_file_content_v = std::vector<char>(pid_file_content.begin(), pid_file_content.end());
+ bp_sys::supply_file_content("/run/daemon.pid", std::move(pid_file_content_v));
+
+ base_process_service_test::handle_exit(&p, 0);
+ sset.process_queues();
+
+ assert(p.get_state() == service_state_t::STARTED);
+
+ assert(event_loop.active_timers.size() == 0);
+
+ sset.remove_service(&p);
+}
+
// Test stop timeout
void test_scripted_stop_timeout()
{
RUN_TEST(test_proc_stop_timeout, " ");
RUN_TEST(test_proc_smooth_recovery1, "");
RUN_TEST(test_proc_smooth_recovery2, "");
+ RUN_TEST(test_bgproc_smooth_recover, "");
RUN_TEST(test_scripted_stop_timeout, "");
RUN_TEST(test_scripted_start_fail, " ");
RUN_TEST(test_scripted_stop_fail, " ");