3 // Given a string and a list of pairs of (start,end) indices for each argument in that string,
4 // store a null terminator for the argument. Return a `char *` vector containing the beginning
5 // of each argument and a trailing nullptr. (The returned array is invalidated if the string is later modified).
6 static std::vector<const char *> separate_args(std::string &s, std::list<std::pair<unsigned,unsigned>> &arg_indices)
8 std::vector<const char *> r;
9 r.reserve(arg_indices.size() + 1);
11 // First store nul terminator for each part:
12 for (auto index_pair : arg_indices) {
13 if (index_pair.second < s.length()) {
14 s[index_pair.second] = 0;
18 // Now we can get the C string (c_str) and store offsets into it:
19 const char * cstr = s.c_str();
20 for (auto index_pair : arg_indices) {
21 r.push_back(cstr + index_pair.first);
27 class base_process_service;
29 // A timer for process restarting. Used to ensure a minimum delay between process restarts (and
30 // also for timing service stop before the SIGKILL hammer is used).
31 class process_restart_timer : public eventloop_t::timer_impl<process_restart_timer>
34 base_process_service * service;
36 process_restart_timer(base_process_service *service_p)
41 dasynq::rearm timer_expiry(eventloop_t &, int expiry_count);
44 class base_process_service : public service_record
46 friend class service_child_watcher;
47 friend class exec_status_pipe_watcher;
48 friend class process_restart_timer;
52 void do_restart() noexcept;
55 string program_name; // storage for program/script and arguments
56 std::vector<const char *> exec_arg_parts; // pointer to each argument/part of the program_name, and nullptr
58 string stop_command; // storage for stop program/script and arguments
59 std::vector<const char *> stop_arg_parts; // pointer to each argument/part of the stop_command, and nullptr
61 service_child_watcher child_listener;
62 exec_status_pipe_watcher child_status_listener;
63 process_restart_timer restart_timer;
64 time_val last_start_time;
66 // Restart interval time and restart count are used to track the number of automatic restarts
67 // over an interval. Too many restarts over an interval will inhibit further restarts.
68 time_val restart_interval_time; // current restart interval
69 int restart_interval_count; // count of restarts within current interval
71 time_val restart_interval; // maximum restart interval
72 int max_restart_interval_count; // number of restarts allowed over maximum interval
73 time_val restart_delay; // delay between restarts
75 // Time allowed for service stop, after which SIGKILL is sent. 0 to disable.
76 time_val stop_timeout = {10, 0}; // default of 10 seconds
78 // Time allowed for service start, after which SIGINT is sent (and then SIGKILL after
79 // <stop_timeout>). 0 to disable.
80 time_val start_timeout = {60, 0}; // default of 1 minute
82 bool waiting_restart_timer : 1;
83 bool stop_timer_armed : 1;
84 bool reserved_child_watch : 1;
85 bool tracking_child : 1; // whether we expect to see child process status
86 bool start_is_interruptible : 1; // whether we can interrupt start
88 // Launch the process with the given arguments, return true on success
89 bool start_ps_process(const std::vector<const char *> &args, bool on_console) noexcept;
91 // Restart the process (due to start failure or unexpected termination). Restarts will be
93 bool restart_ps_process() noexcept;
95 // Perform smooth recovery process
96 void do_smooth_recovery() noexcept;
98 // Start the process, return true on success
99 virtual bool bring_up() noexcept override;
101 virtual void bring_down() noexcept override;
103 // Called when the process exits. The exit_status is the status value yielded by
104 // the "wait" system call.
105 virtual void handle_exit_status(int exit_status) noexcept = 0;
107 // Called if an exec fails.
108 virtual void exec_failed(int errcode) noexcept = 0;
110 virtual bool can_interrupt_start() noexcept override
112 return waiting_restart_timer || start_is_interruptible || service_record::can_interrupt_start();
115 virtual bool can_proceed_to_start() noexcept override
117 return ! waiting_restart_timer;
120 virtual bool interrupt_start() noexcept override;
123 void kill_with_fire() noexcept;
125 // Signal the process group of the service process
126 void kill_pg(int signo) noexcept;
129 base_process_service(service_set *sset, string name, service_type_t record_type_p, string &&command,
130 std::list<std::pair<unsigned,unsigned>> &command_offsets,
131 const std::list<prelim_dep> &deplist_p);
133 ~base_process_service() noexcept
137 // Set the stop command and arguments (may throw std::bad_alloc)
138 void set_stop_command(std::string command, std::list<std::pair<unsigned,unsigned>> &stop_command_offsets)
140 stop_command = command;
141 stop_arg_parts = separate_args(stop_command, stop_command_offsets);
144 void set_restart_interval(timespec interval, int max_restarts) noexcept
146 restart_interval = interval;
147 max_restart_interval_count = max_restarts;
150 void set_restart_delay(timespec delay) noexcept
152 restart_delay = delay;
155 void set_stop_timeout(timespec timeout) noexcept
157 stop_timeout = timeout;
160 void set_start_timeout(timespec timeout) noexcept
162 start_timeout = timeout;
165 void set_start_interruptible(bool value) noexcept
167 start_is_interruptible = value;
171 class process_service : public base_process_service
173 virtual void handle_exit_status(int exit_status) noexcept override;
174 virtual void exec_failed(int errcode) noexcept override;
175 virtual void bring_down() noexcept override;
178 process_service(service_set *sset, string name, string &&command,
179 std::list<std::pair<unsigned,unsigned>> &command_offsets,
180 std::list<prelim_dep> depends_p)
181 : base_process_service(sset, name, service_type_t::PROCESS, std::move(command), command_offsets,
186 ~process_service() noexcept
191 class bgproc_service : public base_process_service
193 virtual void handle_exit_status(int exit_status) noexcept override;
194 virtual void exec_failed(int errcode) noexcept override;
196 enum class pid_result_t {
198 FAILED, // failed to read pid or read invalid pid
199 TERMINATED // read pid successfully, but the process already terminated
202 // Read the pid-file, return false on failure
203 pid_result_t read_pid_file(int *exit_status) noexcept;
206 bgproc_service(service_set *sset, string name, string &&command,
207 std::list<std::pair<unsigned,unsigned>> &command_offsets,
208 std::list<prelim_dep> depends_p)
209 : base_process_service(sset, name, service_type_t::BGPROCESS, std::move(command), command_offsets,
214 ~bgproc_service() noexcept
219 class scripted_service : public base_process_service
221 virtual void handle_exit_status(int exit_status) noexcept override;
222 virtual void exec_failed(int errcode) noexcept override;
223 virtual void bring_down() noexcept override;
225 virtual bool interrupt_start() noexcept override
227 // if base::interrupt_start() returns false, then start hasn't been fully interrupted, but an
228 // interrupt has been issued:
229 interrupting_start = ! base_process_service::interrupt_start();
230 return ! interrupting_start;
233 bool interrupting_start : 1; // running start script (true) or stop script (false)
236 scripted_service(service_set *sset, string name, string &&command,
237 std::list<std::pair<unsigned,unsigned>> &command_offsets,
238 std::list<prelim_dep> depends_p)
239 : base_process_service(sset, name, service_type_t::SCRIPTED, std::move(command), command_offsets,
240 depends_p), interrupting_start(false)
244 ~scripted_service() noexcept