#include "control.h"
#include "service.h"
-bool control_conn_t::processPacket()
+bool control_conn_t::process_packet()
{
using std::string;
- // Note that where we call queuePacket, we must generally check the return value. If it
+ // Note that where we call queue_packet, we must generally check the return value. If it
// returns false it has either deleted the connection or marked it for deletion; we
// shouldn't touch instance members after that point.
// Responds with:
// DINIT_RP_CVERSION, (2 byte) minimum compatible version, (2 byte) maximum compatible version
char replyBuf[] = { DINIT_RP_CPVERSION, 0, 0, 0, 0 };
- if (! queuePacket(replyBuf, 1)) return false;
+ if (! queue_packet(replyBuf, 1)) return false;
rbuf.consume(1);
return true;
}
if (pktType == DINIT_CP_FINDSERVICE || pktType == DINIT_CP_LOADSERVICE) {
- return processFindLoad(pktType);
+ return process_find_load(pktType);
}
if (pktType == DINIT_CP_STARTSERVICE || pktType == DINIT_CP_STOPSERVICE
|| pktType == DINIT_CP_WAKESERVICE || pktType == DINIT_CP_RELEASESERVICE) {
- return processStartStop(pktType);
+ return process_start_stop(pktType);
}
if (pktType == DINIT_CP_UNPINSERVICE) {
- return processUnpinService();
+ return process_unpin_service();
}
if (pktType == DINIT_CP_SHUTDOWN) {
// Shutdown/reboot
services->stop_all_services(sd_type);
char ackBuf[] = { DINIT_RP_ACK };
- if (! queuePacket(ackBuf, 1)) return false;
+ if (! queue_packet(ackBuf, 1)) return false;
// Clear the packet from the buffer
rbuf.consume(2);
return true;
}
if (pktType == DINIT_CP_LISTSERVICES) {
- return listServices();
+ return list_services();
}
else {
// Unrecognized: give error response
char outbuf[] = { DINIT_RP_BADREQ };
- if (! queuePacket(outbuf, 1)) return false;
+ if (! queue_packet(outbuf, 1)) return false;
bad_conn_close = true;
iob.set_watches(OUT_EVENTS);
}
return true;
}
-bool control_conn_t::processFindLoad(int pktType)
+bool control_conn_t::process_find_load(int pktType)
{
using std::string;
if (svcSize <= 0 || chklen > 1024) {
// Queue error response / mark connection bad
char badreqRep[] = { DINIT_RP_BADREQ };
- if (! queuePacket(badreqRep, 1)) return false;
+ if (! queue_packet(badreqRep, 1)) return false;
bad_conn_close = true;
iob.set_watches(OUT_EVENTS);
return true;
if (record != nullptr) {
// Allocate a service handle
- handle_t handle = allocateServiceHandle(record);
+ handle_t handle = allocate_service_handle(record);
std::vector<char> rp_buf;
rp_buf.reserve(7);
rp_buf.push_back(DINIT_RP_SERVICERECORD);
rp_buf.push_back(*(((char *) &handle) + i));
}
rp_buf.push_back(static_cast<char>(record->get_target_state()));
- if (! queuePacket(std::move(rp_buf))) return false;
+ if (! queue_packet(std::move(rp_buf))) return false;
}
else {
std::vector<char> rp_buf = { DINIT_RP_NOSERVICE };
- if (! queuePacket(std::move(rp_buf))) return false;
+ if (! queue_packet(std::move(rp_buf))) return false;
}
// Clear the packet from the buffer
return true;
}
-bool control_conn_t::processStartStop(int pktType)
+bool control_conn_t::process_start_stop(int pktType)
{
using std::string;
handle_t handle;
rbuf.extract((char *) &handle, 2, sizeof(handle));
- service_record *service = findServiceForKey(handle);
+ service_record *service = find_service_for_key(handle);
if (service == nullptr) {
// Service handle is bad
char badreqRep[] = { DINIT_RP_BADREQ };
- if (! queuePacket(badreqRep, 1)) return false;
+ if (! queue_packet(badreqRep, 1)) return false;
bad_conn_close = true;
iob.set_watches(OUT_EVENTS);
return true;
char ack_buf[] = { (char)(already_there ? DINIT_RP_ALREADYSS : DINIT_RP_ACK) };
- if (! queuePacket(ack_buf, 1)) return false;
+ if (! queue_packet(ack_buf, 1)) return false;
}
// Clear the packet from the buffer
return true;
}
-bool control_conn_t::processUnpinService()
+bool control_conn_t::process_unpin_service()
{
using std::string;
handle_t handle;
rbuf.extract((char *) &handle, 1, sizeof(handle));
- service_record *service = findServiceForKey(handle);
+ service_record *service = find_service_for_key(handle);
if (service == nullptr) {
// Service handle is bad
char badreqRep[] = { DINIT_RP_BADREQ };
- if (! queuePacket(badreqRep, 1)) return false;
+ if (! queue_packet(badreqRep, 1)) return false;
bad_conn_close = true;
iob.set_watches(OUT_EVENTS);
return true;
service->unpin();
services->process_queues();
char ack_buf[] = { (char) DINIT_RP_ACK };
- if (! queuePacket(ack_buf, 1)) return false;
+ if (! queue_packet(ack_buf, 1)) return false;
}
// Clear the packet from the buffer
return true;
}
-bool control_conn_t::listServices()
+bool control_conn_t::list_services()
{
rbuf.consume(1); // clear request packet
chklen = 0;
try {
- auto slist = services->listServices();
+ auto slist = services->list_services();
for (auto sptr : slist) {
std::vector<char> pkt_buf;
pkt_buf[8+i] = name[i];
}
- if (! queuePacket(std::move(pkt_buf))) return false;
+ if (! queue_packet(std::move(pkt_buf))) return false;
}
char ack_buf[] = { (char) DINIT_RP_LISTDONE };
- if (! queuePacket(ack_buf, 1)) return false;
+ if (! queue_packet(ack_buf, 1)) return false;
return true;
}
catch (std::bad_alloc &exc)
{
- doOomClose();
+ do_oom_close();
return true;
}
}
-control_conn_t::handle_t control_conn_t::allocateServiceHandle(service_record *record)
+control_conn_t::handle_t control_conn_t::allocate_service_handle(service_record *record)
{
bool is_unique = true;
handle_t largest_seen = 0;
return candidate;
}
-
-bool control_conn_t::queuePacket(const char *pkt, unsigned size) noexcept
+bool control_conn_t::queue_packet(const char *pkt, unsigned size) noexcept
{
int in_flag = bad_conn_close ? 0 : IN_EVENTS;
bool was_empty = outbuf.empty();
}
}
-// This queuePacket method is frustratingly similar to the one above, but the subtle differences
+// This queue_packet method is frustratingly similar to the one above, but the subtle differences
// make them extraordinary difficult to combine into a single method.
-bool control_conn_t::queuePacket(std::vector<char> &&pkt) noexcept
+bool control_conn_t::queue_packet(std::vector<char> &&pkt) noexcept
{
int in_flag = bad_conn_close ? 0 : IN_EVENTS;
bool was_empty = outbuf.empty();
}
}
-bool control_conn_t::rollbackComplete() noexcept
+bool control_conn_t::rollback_complete() noexcept
{
char ackBuf[2] = { DINIT_ROLLBACK_COMPLETED, 2 };
- return queuePacket(ackBuf, 2);
+ return queue_packet(ackBuf, 2);
}
-bool control_conn_t::dataReady() noexcept
+bool control_conn_t::data_ready() noexcept
{
int fd = iob.get_watched_fd();
// complete packet?
if (rbuf.get_length() >= chklen) {
try {
- return !processPacket();
+ return !process_packet();
}
catch (std::bad_alloc &baexc) {
- doOomClose();
+ do_oom_close();
return false;
}
}
return false;
}
-bool control_conn_t::sendData() noexcept
+bool control_conn_t::send_data() noexcept
{
if (outbuf.empty() && bad_conn_close) {
if (oom_close) {
control_conn_t::~control_conn_t() noexcept
{
close(iob.get_watched_fd());
- iob.deregister(*loop);
+ iob.deregister(loop);
// Clear service listeners
for (auto p : serviceKeyMap) {
class control_conn_watcher : public eventloop_t::bidi_fd_watcher_impl<control_conn_watcher>
{
- inline rearm receiveEvent(eventloop_t &loop, int fd, int flags) noexcept;
+ inline rearm receive_event(eventloop_t &loop, int fd, int flags) noexcept;
+
+ eventloop_t * event_loop;
public:
+ control_conn_watcher(eventloop_t & event_loop_p) : event_loop(&event_loop_p)
+ {
+ // constructor
+ }
+
rearm read_ready(eventloop_t &loop, int fd) noexcept
{
- return receiveEvent(loop, fd, IN_EVENTS);
+ return receive_event(loop, fd, IN_EVENTS);
}
rearm write_ready(eventloop_t &loop, int fd) noexcept
{
- return receiveEvent(loop, fd, OUT_EVENTS);
+ return receive_event(loop, fd, OUT_EVENTS);
}
-
- eventloop_t * eventLoop;
-
+
void set_watches(int flags)
{
- eventloop_t::bidi_fd_watcher::set_watches(*eventLoop, flags);
- }
-
- void registerWith(eventloop_t &loop, int fd, int flags)
- {
- this->eventLoop = &loop;
- bidi_fd_watcher<eventloop_t>::add_watch(loop, fd, flags);
+ eventloop_t::bidi_fd_watcher::set_watches(*event_loop, flags);
}
};
-inline rearm control_conn_watcher::receiveEvent(eventloop_t &loop, int fd, int flags) noexcept
+inline rearm control_conn_watcher::receive_event(eventloop_t &loop, int fd, int flags) noexcept
{
return control_conn_cb(&loop, this, flags);
}
friend rearm control_conn_cb(eventloop_t *loop, control_conn_watcher *watcher, int revents);
control_conn_watcher iob;
- eventloop_t *loop;
+ eventloop_t &loop;
service_set *services;
bool bad_conn_close = false; // close when finished output?
bool oom_close = false; // send final 'out of memory' indicator
// The packet length before we need to re-check if the packet is complete.
- // processPacket() will not be called until the packet reaches this size.
+ // process_packet() will not be called until the packet reaches this size.
int chklen;
// Receive buffer
// true (with bad_conn_close == true) if the packet was not successfully
// queued (but a suitable error packate has been queued).
// The in/out watch enabled state will also be set appropriately.
- bool queuePacket(vector<char> &&v) noexcept;
- bool queuePacket(const char *pkt, unsigned size) noexcept;
+ bool queue_packet(vector<char> &&v) noexcept;
+ bool queue_packet(const char *pkt, unsigned size) noexcept;
// Process a packet.
// Returns: true (with bad_conn_close == false) if successful
// (connection should be closed).
// Throws:
// std::bad_alloc - if an out-of-memory condition prevents processing
- bool processPacket();
+ bool process_packet();
// Process a STARTSERVICE/STOPSERVICE packet. May throw std::bad_alloc.
- bool processStartStop(int pktType);
+ bool process_start_stop(int pktType);
// Process a FINDSERVICE/LOADSERVICE packet. May throw std::bad_alloc.
- bool processFindLoad(int pktType);
+ bool process_find_load(int pktType);
// Process an UNPINSERVICE packet. May throw std::bad_alloc.
- bool processUnpinService();
+ bool process_unpin_service();
- bool listServices();
+ bool list_services();
// Notify that data is ready to be read from the socket. Returns true if the connection should
// be closed.
- bool dataReady() noexcept;
+ bool data_ready() noexcept;
- bool sendData() noexcept;
+ bool send_data() noexcept;
// Allocate a new handle for a service; may throw std::bad_alloc
- handle_t allocateServiceHandle(service_record *record);
+ handle_t allocate_service_handle(service_record *record);
- service_record *findServiceForKey(uint32_t key)
+ service_record *find_service_for_key(uint32_t key)
{
try {
return keyServiceMap.at(key);
}
// Close connection due to out-of-memory condition.
- void doOomClose()
+ void do_oom_close()
{
bad_conn_close = true;
oom_close = true;
// Process service event broadcast.
// Note that this can potentially be called during packet processing (upon issuing
// service start/stop orders etc).
- void serviceEvent(service_record * service, service_event event) noexcept final override
+ void service_event(service_record * service, service_event_t event) noexcept final override
{
// For each service handle corresponding to the event, send an information packet.
auto range = serviceKeyMap.equal_range(service);
pkt.push_back(*p++);
}
pkt.push_back(static_cast<char>(event));
- queuePacket(std::move(pkt));
+ queue_packet(std::move(pkt));
++i;
}
}
catch (std::bad_alloc &exc) {
- doOomClose();
+ do_oom_close();
}
}
public:
- control_conn_t(eventloop_t * loop, service_set * services_p, int fd)
- : loop(loop), services(services_p), chklen(0)
+ control_conn_t(eventloop_t &loop, service_set * services_p, int fd)
+ : iob(loop), loop(loop), services(services_p), chklen(0)
{
- iob.registerWith(*loop, fd, IN_EVENTS);
+ iob.add_watch(loop, fd, IN_EVENTS);
active_control_conns++;
}
- bool rollbackComplete() noexcept;
+ bool rollback_complete() noexcept;
virtual ~control_conn_t() noexcept;
};
char * cc_addr = (reinterpret_cast<char *>(watcher)) - offsetof(control_conn_t, iob);
control_conn_t *conn = reinterpret_cast<control_conn_t *>(cc_addr);
if (revents & IN_EVENTS) {
- if (conn->dataReady()) {
+ if (conn->data_ready()) {
delete conn;
return rearm::REMOVED;
}
}
if (revents & OUT_EVENTS) {
- if (conn->sendData()) {
+ if (conn->send_data()) {
delete conn;
return rearm::REMOVED;
}
if (newfd != -1) {
try {
- new control_conn_t(loop, services, newfd); // will delete itself when it's finished
+ new control_conn_t(*loop, services, newfd); // will delete itself when it's finished
}
catch (std::exception &exc) {
log(loglevel_t::ERROR, "Accepting control connection: ", exc.what());
return 0;
}
- service_event completionEvent;
- service_event cancelledEvent;
+ service_event_t completionEvent;
+ service_event_t cancelledEvent;
if (do_stop) {
- completionEvent = service_event::STOPPED;
- cancelledEvent = service_event::STOPCANCELLED;
+ completionEvent = service_event_t::STOPPED;
+ cancelledEvent = service_event_t::STOPCANCELLED;
}
else {
- completionEvent = service_event::STARTED;
- cancelledEvent = service_event::STARTCANCELLED;
+ completionEvent = service_event_t::STARTED;
+ cancelledEvent = service_event_t::STARTCANCELLED;
}
// Wait until service started:
if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
handle_t ev_handle;
rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
- service_event event = static_cast<service_event>(rbuffer[2 + sizeof(ev_handle)]);
+ service_event_t event = static_cast<service_event_t>(rbuffer[2 + sizeof(ev_handle)]);
if (ev_handle == handle) {
if (event == completionEvent) {
if (verbose) {
}
return 1;
}
- else if (! do_stop && event == service_event::FAILEDSTART) {
+ else if (! do_stop && event == service_event_t::FAILEDSTART) {
if (verbose) {
cout << "Service failed to start." << endl;
}
};
/* Service events */
-enum class service_event {
+enum class service_event_t {
STARTED, // Service was started (reached STARTED state)
STOPPED, // Service was stopped (reached STOPPED state)
FAILEDSTART, // Service failed to start (possibly due to dependency failing)
// An event occurred on the service being observed.
// Listeners must not be added or removed during event notification.
- virtual void serviceEvent(service_record * service, service_event event) noexcept = 0;
+ virtual void service_event(service_record * service, service_event_t event) noexcept = 0;
};
#endif
}
log_service_stopped(service_name);
- notify_listeners(service_event::STOPPED);
+ notify_listeners(service_event_t::STOPPED);
}
dasynq::rearm service_child_watcher::status_change(eventloop_t &loop, pid_t child, int status) noexcept
// We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING,
// but if so they are waiting (for us), so they too can be instantly returned to
// STARTING state.
- notify_listeners(service_event::STOPCANCELLED);
+ notify_listeners(service_event_t::STOPCANCELLED);
}
else if (! was_active) {
services->service_active(this);
}
}
-void service_record::dependencyStarted() noexcept
+void service_record::dependency_started() noexcept
{
if ((service_state == service_state_t::STARTING || service_state == service_state_t::STARTED)
&& waiting_for_deps) {
waiting_for_deps = false;
- // We overload can_interrupt_start to check whether there is any other
- // process (eg restart timer) that needs to finish before starting.
- if (can_interrupt_start()) {
+ if (! can_proceed_to_start()) {
waiting_for_deps = true;
return;
}
log_service_started(service_name);
service_state = service_state_t::STARTED;
- notify_listeners(service_event::STARTED);
+ notify_listeners(service_event_t::STARTED);
if (onstart_flags.rw_ready) {
open_control_socket();
// Notify any dependents whose desired state is STARTED:
for (auto dept : dependents) {
- dept->get_from()->dependencyStarted();
+ dept->get_from()->dependency_started();
dept->waiting_on = false;
}
}
start_explicit = false;
release();
}
- notify_listeners(service_event::FAILEDSTART);
+ notify_listeners(service_event_t::FAILEDSTART);
// Cancel start of dependents:
for (auto & dept : dependents) {
case dependency_type::SOFT:
if (dept->waiting_on) {
dept->waiting_on = false;
- dept->get_from()->dependencyStarted();
+ dept->get_from()->dependency_started();
}
if (dept->holding_acq) {
dept->holding_acq = false;
fcntl(control_socket[0], F_SETFD, fdflags | FD_CLOEXEC);
try {
- control_conn = new control_conn_t(&eventLoop, services, control_socket[0]);
+ control_conn = new control_conn_t(eventLoop, services, control_socket[0]);
}
catch (std::exception &exc) {
log(loglevel_t::ERROR, service_name, ": can't launch process; out of memory");
}
// We must have had desired_state == STARTED.
- notify_listeners(service_event::STARTCANCELLED);
+ notify_listeners(service_event_t::STARTCANCELLED);
interrupt_start();
*/
struct onstart_flags_t {
- bool rw_ready : 1;
- bool log_ready : 1;
+ bool rw_ready : 1; // file system should be writable once this service starts
+ bool log_ready : 1; // syslog should be available once this service starts
// Not actually "onstart" commands:
bool no_sigterm : 1; // do not send SIGTERM
bool pinned_stopped : 1;
bool pinned_started : 1;
bool waiting_for_deps : 1; // if STARTING, whether we are waiting for dependencies (inc console) to start
+ // if STOPPING, whether we are waiting for dependents to stop
bool waiting_for_execstat : 1; // if we are waiting for exec status after fork()
- bool start_explicit : 1; // whether we are are explictly required to be started
+ bool start_explicit : 1; // whether we are are explicitly required to be started
bool prop_require : 1; // require must be propagated
bool prop_release : 1; // release must be propagated
bool prop_failure : 1; // failure to start must be propagated
bool prop_start : 1;
bool prop_stop : 1;
+
bool restarting : 1; // re-starting after unexpected termination
int required_by = 0; // number of dependents wanting this service to be started
- typedef std::list<service_record *> sr_list;
- typedef sr_list::iterator sr_iter;
-
- // list of soft dependencies
+ // list of dependencies
typedef std::list<service_dep> dep_list;
- // list of soft dependents
+ // list of dependents
typedef std::list<service_dep *> dpt_list;
dep_list depends_on; // services this one depends on
int csfd) noexcept;
// A dependency has reached STARTED state
- void dependencyStarted() noexcept;
+ void dependency_started() noexcept;
void all_deps_started(bool haveConsole = false) noexcept;
return waiting_for_deps;
}
+ // Whether a STARTING service can transition to its STARTED state, once all
+ // dependencies have started.
+ virtual bool can_proceed_to_start() noexcept
+ {
+ return true;
+ }
+
virtual void interrupt_start() noexcept;
// Whether a STOPPING service can immediately transition to STARTED.
|| (service_state == service_state_t::STARTING && waiting_for_deps);
}
- void notify_listeners(service_event event) noexcept
+ void notify_listeners(service_event_t event) noexcept
{
for (auto l : listeners) {
- l->serviceEvent(this, event);
+ l->service_event(this, event);
}
}
return waiting_restart_timer || service_record::can_interrupt_start();
}
+ virtual bool can_proceed_to_start() noexcept override
+ {
+ return ! waiting_restart_timer;
+ }
+
virtual void interrupt_start() noexcept override;
// Kill with SIGKILL
}
// Get the list of all loaded services.
- const std::list<service_record *> &listServices()
+ const std::list<service_record *> &list_services() noexcept
{
return records;
}