1 #ifndef DINIT_CONTROL_H
2 #define DINIT_CONTROL_H
6 #include <unordered_map>
14 #include "dinit-log.h"
15 #include "control-cmds.h"
16 #include "service-listener.h"
19 // Control connection for dinit
22 class control_conn_watcher;
24 // forward-declaration of callback:
25 inline dasynq::rearm control_conn_cb(eventloop_t *loop, control_conn_watcher *watcher, int revents);
27 // Pointer to the control connection that is listening for rollback completion
28 extern control_conn_t * rollback_handler_conn;
30 extern int active_control_conns;
33 // (1 byte) packet type
34 // (N bytes) additional data (service name, etc)
35 // for LOADSERVICE/FINDSERVICE:
36 // (2 bytes) service name length
37 // (M bytes) service name (without nul terminator)
39 // Information packet:
40 // (1 byte) packet type, >= 100
41 // (1 byte) packet length (including all fields)
42 // N bytes: packet data (N = (length - 2))
47 class control_conn_watcher : public eventloop_t::bidi_fd_watcher_impl<control_conn_watcher>
49 inline rearm receive_event(eventloop_t &loop, int fd, int flags) noexcept
51 return control_conn_cb(&loop, this, flags);
54 eventloop_t * event_loop;
57 control_conn_watcher(eventloop_t & event_loop_p) : event_loop(&event_loop_p)
62 rearm read_ready(eventloop_t &loop, int fd) noexcept
64 return receive_event(loop, fd, dasynq::IN_EVENTS);
67 rearm write_ready(eventloop_t &loop, int fd) noexcept
69 return receive_event(loop, fd, dasynq::OUT_EVENTS);
72 void set_watches(int flags)
74 eventloop_t::bidi_fd_watcher::set_watches(*event_loop, flags);
78 class control_conn_t : private service_listener
80 friend rearm control_conn_cb(eventloop_t *loop, control_conn_watcher *watcher, int revents);
82 control_conn_watcher iob;
84 service_set *services;
86 bool bad_conn_close = false; // close when finished output?
87 bool oom_close = false; // send final 'out of memory' indicator
89 // The packet length before we need to re-check if the packet is complete.
90 // process_packet() will not be called until the packet reaches this size.
96 template <typename T> using list = std::list<T>;
97 template <typename T> using vector = std::vector<T>;
99 // A mapping between service records and their associated numerical identifier used
101 using handle_t = uint32_t;
102 std::unordered_multimap<service_record *, handle_t> service_key_map;
103 std::map<handle_t, service_record *> key_service_map;
105 // Buffer for outgoing packets. Each outgoing back is represented as a vector<char>.
106 list<vector<char>> outbuf;
107 // Current index within the first outgoing packet (all previous bytes have been sent).
108 unsigned outpkt_index = 0;
110 // Queue a packet to be sent
111 // Returns: false if the packet could not be queued and a suitable error packet
112 // could not be sent/queued (the connection should be closed);
113 // true (with bad_conn_close == false) if the packet was successfully
115 // true (with bad_conn_close == true) if the packet was not successfully
116 // queued (but a suitable error packate has been queued).
117 // The in/out watch enabled state will also be set appropriately.
118 bool queue_packet(vector<char> &&v) noexcept;
119 bool queue_packet(const char *pkt, unsigned size) noexcept;
122 // Returns: true (with bad_conn_close == false) if successful
123 // true (with bad_conn_close == true) if an error packet was queued
124 // false if an error occurred but no error packet could be queued
125 // (connection should be closed).
127 // std::bad_alloc - if an out-of-memory condition prevents processing
128 bool process_packet();
130 // Process a STARTSERVICE/STOPSERVICE packet. May throw std::bad_alloc.
131 bool process_start_stop(int pktType);
133 // Process a FINDSERVICE/LOADSERVICE packet. May throw std::bad_alloc.
134 bool process_find_load(int pktType);
136 // Process an UNPINSERVICE packet. May throw std::bad_alloc.
137 bool process_unpin_service();
139 // Process an UNLOADSERVICE packet.
140 bool process_unload_service();
142 bool list_services();
144 bool add_service_dep();
146 bool rm_service_dep();
148 // Notify that data is ready to be read from the socket. Returns true if the connection should
150 bool data_ready() noexcept;
152 bool send_data() noexcept;
154 // Allocate a new handle for a service; may throw std::bad_alloc
155 handle_t allocate_service_handle(service_record *record);
157 // Find the service corresponding to a service handle; returns nullptr if not found.
158 service_record *find_service_for_key(handle_t key) noexcept
161 return key_service_map.at(key);
163 catch (std::out_of_range &exc) {
168 // Close connection due to out-of-memory condition.
169 void do_oom_close() noexcept
171 bad_conn_close = true;
173 iob.set_watches(dasynq::OUT_EVENTS);
176 // Process service event broadcast.
177 // Note that this can potentially be called during packet processing (upon issuing
178 // service start/stop orders etc).
179 void service_event(service_record * service, service_event_t event) noexcept final override
181 // For each service handle corresponding to the event, send an information packet.
182 auto range = service_key_map.equal_range(service);
183 auto & i = range.first;
184 auto & end = range.second;
187 uint32_t key = i->second;
188 std::vector<char> pkt;
189 constexpr int pktsize = 3 + sizeof(key);
190 pkt.reserve(pktsize);
191 pkt.push_back(DINIT_IP_SERVICEEVENT);
192 pkt.push_back(pktsize);
193 char * p = (char *) &key;
194 for (int j = 0; j < (int)sizeof(key); j++) {
197 pkt.push_back(static_cast<char>(event));
198 queue_packet(std::move(pkt));
202 catch (std::bad_alloc &exc) {
208 control_conn_t(eventloop_t &loop, service_set * services_p, int fd)
209 : iob(loop), loop(loop), services(services_p), chklen(0)
211 iob.add_watch(loop, fd, dasynq::IN_EVENTS);
212 active_control_conns++;
215 control_conn_t(const control_conn_t &) = delete;
217 bool rollback_complete() noexcept;
219 virtual ~control_conn_t() noexcept;
223 inline dasynq::rearm control_conn_cb(eventloop_t * loop, control_conn_watcher * watcher, int revents)
225 // Get the address of the containing control_connt_t object:
226 _Pragma ("GCC diagnostic push")
227 _Pragma ("GCC diagnostic ignored \"-Winvalid-offsetof\"")
228 char * cc_addr = (reinterpret_cast<char *>(watcher)) - offsetof(control_conn_t, iob);
229 control_conn_t *conn = reinterpret_cast<control_conn_t *>(cc_addr);
230 _Pragma ("GCC diagnostic pop")
232 if (revents & dasynq::IN_EVENTS) {
233 if (conn->data_ready()) {
235 return dasynq::rearm::REMOVED;
238 if (revents & dasynq::OUT_EVENTS) {
239 if (conn->send_data()) {
241 return dasynq::rearm::REMOVED;
245 return dasynq::rearm::NOOP;