1 #ifndef DINIT_CONTROL_H
2 #define DINIT_CONTROL_H
6 #include <unordered_map>
12 #include "dinit-log.h"
13 #include "control-cmds.h"
14 #include "service-listener.h"
17 // Control connection for dinit
19 // TODO: Use the input buffer as a circular buffer, instead of chomping data from
20 // the front using a data move.
22 // forward-declaration of callback:
23 static void control_conn_cb(struct ev_loop * loop, ev_io * w, int revents);
27 // Pointer to the control connection that is listening for rollback completion
28 extern ControlConn * 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 ControlConn : private ServiceListener
49 friend void control_conn_cb(struct ev_loop *, ev_io *, int);
53 ServiceSet *service_set;
55 bool bad_conn_close; // close when finished output?
56 bool oom_close; // send final 'out of memory' indicator
58 // The packet length before we need to re-check if the packet is complete.
59 // processPacket() will not be called until the packet reaches this size.
65 template <typename T> using list = std::list<T>;
66 template <typename T> using vector = std::vector<T>;
68 // A mapping between service records and their associated numerical identifier used
70 using handle_t = uint32_t;
71 std::unordered_multimap<ServiceRecord *, handle_t> serviceKeyMap;
72 std::unordered_map<handle_t, ServiceRecord *> keyServiceMap;
74 // Buffer for outgoing packets. Each outgoing back is represented as a vector<char>.
75 list<vector<char>> outbuf;
76 // Current index within the first outgoing packet (all previous bytes have been sent).
77 unsigned outpkt_index = 0;
79 // Queue a packet to be sent
80 // Returns: true if the packet was successfully queued, false if otherwise
81 // (eg if out of memory); in the latter case the connection might
82 // no longer be valid (iff there are no outgoing packets queued).
83 bool queuePacket(vector<char> &&v) noexcept;
84 bool queuePacket(const char *pkt, unsigned size) noexcept;
86 // Process a packet. Can cause the ControlConn to be deleted iff there are no
87 // outgoing packets queued.
89 // std::bad_alloc - if an out-of-memory condition prevents processing
92 // Process a STARTSERVICE/STOPSERVICE packet. May throw std::bad_alloc.
93 void processStartStop(int pktType);
95 // Process a FINDSERVICE/LOADSERVICE packet. May throw std::bad_alloc.
96 void processFindLoad(int pktType);
98 // Notify that data is ready to be read from the socket. Returns true in cases where the
99 // connection was deleted with potentially pending outgoing packets.
100 bool dataReady() noexcept;
102 void sendData() noexcept;
104 // Allocate a new handle for a service; may throw std::bad_alloc
105 handle_t allocateServiceHandle(ServiceRecord *record);
107 ServiceRecord *findServiceForKey(uint32_t key)
110 return keyServiceMap.at(key);
112 catch (std::out_of_range &exc) {
117 // Close connection due to out-of-memory condition.
120 bad_conn_close = true;
122 ev_io_set(&iob, iob.fd, EV_WRITE);
125 // Process service event broadcast.
126 void serviceEvent(ServiceRecord * service, ServiceEvent event) noexcept final override
128 // For each service handle corresponding to the event, send an information packet.
129 auto range = serviceKeyMap.equal_range(service);
130 auto & i = range.first;
131 auto & end = range.second;
134 uint32_t key = i->second;
135 std::vector<char> pkt;
136 constexpr int pktsize = 3 + sizeof(key);
137 pkt.reserve(pktsize);
138 pkt.push_back(DINIT_IP_SERVICEEVENT);
139 pkt.push_back(pktsize);
140 char * p = (char *) &key;
141 for (int j = 0; j < (int)sizeof(key); j++) {
144 pkt.push_back(static_cast<char>(event));
145 queuePacket(std::move(pkt));
149 catch (std::bad_alloc &exc) {
155 ControlConn(struct ev_loop * loop, ServiceSet * service_set, int fd) : loop(loop), service_set(service_set), chklen(0)
157 ev_io_init(&iob, control_conn_cb, fd, EV_READ);
159 ev_io_start(loop, &iob);
161 active_control_conns++;
164 bool rollbackComplete() noexcept;
166 virtual ~ControlConn() noexcept;
170 static void control_conn_cb(struct ev_loop * loop, ev_io * w, int revents)
172 ControlConn *conn = (ControlConn *) w->data;
173 if (revents & EV_READ) {
174 if (conn->dataReady()) {
175 // ControlConn was deleted
179 if (revents & EV_WRITE) {