4 void ControlConn::processPacket()
8 // Note that where we call queuePacket, we must generally check the return value. If it
9 // returns false it has either deleted the connection or marked it for deletion; we
10 // shouldn't touch instance members after that point.
12 int pktType = rbuf[0];
13 if (pktType == DINIT_CP_QUERYVERSION) {
15 // DINIT_RP_CVERSION, (2 byte) minimum compatible version, (2 byte) maximum compatible version
16 char replyBuf[] = { DINIT_RP_CPVERSION, 0, 0, 0, 0 };
17 if (! queuePacket(replyBuf, 1)) return;
21 if (pktType == DINIT_CP_FINDSERVICE || pktType == DINIT_CP_LOADSERVICE) {
22 processFindLoad(pktType);
25 if (pktType == DINIT_CP_STARTSERVICE || pktType == DINIT_CP_STOPSERVICE) {
26 processStartStop(pktType);
29 else if (pktType == DINIT_CP_SHUTDOWN) {
31 if (rbuf.get_length() < 2) {
36 auto sd_type = static_cast<ShutdownType>(rbuf[1]);
38 service_set->stop_all_services(sd_type);
39 log_to_console = true;
40 char ackBuf[] = { DINIT_RP_ACK };
41 if (! queuePacket(ackBuf, 1)) return;
43 // Clear the packet from the buffer
49 // Unrecognized: give error response
50 char outbuf[] = { DINIT_RP_BADREQ };
51 if (! queuePacket(outbuf, 1)) return;
52 bad_conn_close = true;
53 ev_io_set(&iob, iob.fd, EV_WRITE);
58 void ControlConn::processFindLoad(int pktType)
62 constexpr int pkt_size = 4;
64 if (rbuf.get_length() < pkt_size) {
70 rbuf.extract((char *)&svcSize, 1, 2);
71 chklen = svcSize + 3; // packet type + (2 byte) length + service name
72 if (svcSize <= 0 || chklen > 1024) {
73 // Queue error response / mark connection bad
74 char badreqRep[] = { DINIT_RP_BADREQ };
75 if (! queuePacket(badreqRep, 1)) return;
76 bad_conn_close = true;
77 ev_io_set(&iob, iob.fd, EV_WRITE);
81 if (rbuf.get_length() < chklen) {
82 // packet not complete yet; read more
86 ServiceRecord * record = nullptr;
88 string serviceName = std::move(rbuf.extract_string(3, svcSize));
90 if (pktType == DINIT_CP_LOADSERVICE) {
93 record = service_set->loadService(serviceName);
95 catch (ServiceLoadExc &slexc) {
96 log(LogLevel::ERROR, "Could not load service ", slexc.serviceName, ": ", slexc.excDescription);
101 record = service_set->findService(serviceName.c_str());
104 if (record != nullptr) {
105 // Allocate a service handle
106 handle_t handle = allocateServiceHandle(record);
107 std::vector<char> rp_buf;
109 rp_buf.push_back(DINIT_RP_SERVICERECORD);
110 rp_buf.push_back(static_cast<char>(record->getState()));
111 for (int i = 0; i < (int) sizeof(handle); i++) {
112 rp_buf.push_back(*(((char *) &handle) + i));
114 rp_buf.push_back(static_cast<char>(record->getTargetState()));
115 if (! queuePacket(std::move(rp_buf))) return;
118 std::vector<char> rp_buf = { DINIT_RP_NOSERVICE };
119 if (! queuePacket(std::move(rp_buf))) return;
122 // Clear the packet from the buffer
123 rbuf.consume(chklen);
128 void ControlConn::processStartStop(int pktType)
132 constexpr int pkt_size = 2 + sizeof(handle_t);
134 if (rbuf.get_length() < pkt_size) {
139 // 1 byte: packet type
140 // 1 byte: pin in requested state (0 = no pin, 1 = pin)
141 // 4 bytes: service handle
143 bool do_pin = (rbuf[1] == 1);
145 rbuf.extract((char *) &handle, 2, sizeof(handle));
147 ServiceRecord *service = findServiceForKey(handle);
148 if (service == nullptr) {
149 // Service handle is bad
150 char badreqRep[] = { DINIT_RP_BADREQ };
151 if (! queuePacket(badreqRep, 1)) return;
152 bad_conn_close = true;
153 ev_io_set(&iob, iob.fd, EV_WRITE);
157 if (pktType == DINIT_CP_STARTSERVICE) {
174 char ack_buf[] = { DINIT_RP_ACK };
175 if (! queuePacket(ack_buf, 1)) return;
178 // Clear the packet from the buffer
179 rbuf.consume(pkt_size);
184 ControlConn::handle_t ControlConn::allocateServiceHandle(ServiceRecord *record)
186 bool is_unique = true;
187 handle_t largest_seen = 0;
188 handle_t candidate = 0;
189 for (auto p : keyServiceMap) {
190 if (p.first > largest_seen) largest_seen = p.first;
191 if (p.first == candidate) {
192 if (largest_seen == std::numeric_limits<handle_t>::max()) throw std::bad_alloc();
193 candidate = largest_seen + 1;
195 is_unique &= (p.second != record);
198 keyServiceMap[candidate] = record;
199 serviceKeyMap.insert(std::make_pair(record, candidate));
202 record->addListener(this);
209 bool ControlConn::queuePacket(const char *pkt, unsigned size) noexcept
211 if (bad_conn_close) return false;
213 bool was_empty = outbuf.empty();
216 int wr = write(iob.fd, pkt, size);
218 if (errno == EPIPE) {
222 if (errno != EAGAIN && errno != EWOULDBLOCK) {
229 if ((unsigned)wr == size) {
236 ev_io_set(&iob, iob.fd, EV_READ | EV_WRITE);
239 // Create a vector out of the (remaining part of the) packet:
241 outbuf.emplace_back(pkt, pkt + size);
244 catch (std::bad_alloc &baexc) {
245 // Mark the connection bad, and stop reading further requests
246 bad_conn_close = true;
249 // We can't send out-of-memory response as we already wrote as much as we
250 // could above. Neither can we later send the response since we have currently
251 // sent an incomplete packet. All we can do is close the connection.
255 ev_io_set(&iob, iob.fd, EV_WRITE);
262 bool ControlConn::queuePacket(std::vector<char> &&pkt) noexcept
264 if (bad_conn_close) return false;
266 bool was_empty = outbuf.empty();
270 // We can try sending the packet immediately:
271 int wr = write(iob.fd, pkt.data(), pkt.size());
273 if (errno == EPIPE) {
277 if (errno != EAGAIN && errno != EWOULDBLOCK) {
284 if ((unsigned)wr == pkt.size()) {
290 ev_io_set(&iob, iob.fd, EV_READ | EV_WRITE);
294 outbuf.emplace_back(pkt);
297 catch (std::bad_alloc &baexc) {
298 // Mark the connection bad, and stop reading further requests
299 bad_conn_close = true;
302 // We can't send out-of-memory response as we already wrote as much as we
303 // could above. Neither can we later send the response since we have currently
304 // sent an incomplete packet. All we can do is close the connection.
308 ev_io_set(&iob, iob.fd, EV_WRITE);
314 bool ControlConn::rollbackComplete() noexcept
316 char ackBuf[2] = { DINIT_ROLLBACK_COMPLETED, 2 };
317 return queuePacket(ackBuf, 2);
320 bool ControlConn::dataReady() noexcept
324 int r = rbuf.fill(fd);
326 // Note file descriptor is non-blocking
328 if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
342 if (rbuf.get_length() >= chklen) {
346 catch (std::bad_alloc &baexc) {
351 if (rbuf.get_length() == 1024) {
354 // TODO error response?
355 bad_conn_close = true;
356 ev_io_set(&iob, iob.fd, EV_WRITE);
362 void ControlConn::sendData() noexcept
364 if (outbuf.empty() && bad_conn_close) {
367 char oomBuf[] = { DINIT_RP_OOM };
368 write(iob.fd, oomBuf, 1);
374 vector<char> & pkt = outbuf.front();
375 char *data = pkt.data();
376 int written = write(iob.fd, data + outpkt_index, pkt.size() - outpkt_index);
378 if (errno == EPIPE) {
382 else if (errno == EAGAIN || errno == EWOULDBLOCK) {
383 // spurious readiness notification?
386 log(LogLevel::ERROR, "Error writing to control connection: ", strerror(errno));
392 outpkt_index += written;
393 if (outpkt_index == pkt.size()) {
394 // We've finished this packet, move on to the next:
397 if (outbuf.empty() && ! oom_close) {
398 if (! bad_conn_close) {
399 ev_io_set(&iob, iob.fd, EV_READ);
408 ControlConn::~ControlConn() noexcept
411 ev_io_stop(loop, &iob);
413 // Clear service listeners
414 for (auto p : serviceKeyMap) {
415 p.first->removeListener(this);
418 active_control_conns--;