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 || pktType == DINIT_CP_WAKESERVICE || pktType == DINIT_CP_RELEASESERVICE) {
27 processStartStop(pktType);
30 if (pktType == DINIT_CP_UNPINSERVICE) {
31 processUnpinService();
34 if (pktType == DINIT_CP_SHUTDOWN) {
36 if (rbuf.get_length() < 2) {
41 auto sd_type = static_cast<ShutdownType>(rbuf[1]);
43 service_set->stop_all_services(sd_type);
44 log_to_console = true;
45 char ackBuf[] = { DINIT_RP_ACK };
46 if (! queuePacket(ackBuf, 1)) return;
48 // Clear the packet from the buffer
54 // Unrecognized: give error response
55 char outbuf[] = { DINIT_RP_BADREQ };
56 if (! queuePacket(outbuf, 1)) return;
57 bad_conn_close = true;
58 ev_io_set(&iob, iob.fd, EV_WRITE);
63 void ControlConn::processFindLoad(int pktType)
67 constexpr int pkt_size = 4;
69 if (rbuf.get_length() < pkt_size) {
75 rbuf.extract((char *)&svcSize, 1, 2);
76 chklen = svcSize + 3; // packet type + (2 byte) length + service name
77 if (svcSize <= 0 || chklen > 1024) {
78 // Queue error response / mark connection bad
79 char badreqRep[] = { DINIT_RP_BADREQ };
80 if (! queuePacket(badreqRep, 1)) return;
81 bad_conn_close = true;
82 ev_io_set(&iob, iob.fd, EV_WRITE);
86 if (rbuf.get_length() < chklen) {
87 // packet not complete yet; read more
91 ServiceRecord * record = nullptr;
93 string serviceName = std::move(rbuf.extract_string(3, svcSize));
95 if (pktType == DINIT_CP_LOADSERVICE) {
98 record = service_set->loadService(serviceName);
100 catch (ServiceLoadExc &slexc) {
101 log(LogLevel::ERROR, "Could not load service ", slexc.serviceName, ": ", slexc.excDescription);
106 record = service_set->findService(serviceName.c_str());
109 if (record != nullptr) {
110 // Allocate a service handle
111 handle_t handle = allocateServiceHandle(record);
112 std::vector<char> rp_buf;
114 rp_buf.push_back(DINIT_RP_SERVICERECORD);
115 rp_buf.push_back(static_cast<char>(record->getState()));
116 for (int i = 0; i < (int) sizeof(handle); i++) {
117 rp_buf.push_back(*(((char *) &handle) + i));
119 rp_buf.push_back(static_cast<char>(record->getTargetState()));
120 if (! queuePacket(std::move(rp_buf))) return;
123 std::vector<char> rp_buf = { DINIT_RP_NOSERVICE };
124 if (! queuePacket(std::move(rp_buf))) return;
127 // Clear the packet from the buffer
128 rbuf.consume(chklen);
133 void ControlConn::processStartStop(int pktType)
137 constexpr int pkt_size = 2 + sizeof(handle_t);
139 if (rbuf.get_length() < pkt_size) {
144 // 1 byte: packet type
145 // 1 byte: pin in requested state (0 = no pin, 1 = pin)
146 // 4 bytes: service handle
148 bool do_pin = (rbuf[1] == 1);
150 rbuf.extract((char *) &handle, 2, sizeof(handle));
152 ServiceRecord *service = findServiceForKey(handle);
153 if (service == nullptr) {
154 // Service handle is bad
155 char badreqRep[] = { DINIT_RP_BADREQ };
156 if (! queuePacket(badreqRep, 1)) return;
157 bad_conn_close = true;
158 ev_io_set(&iob, iob.fd, EV_WRITE);
162 bool already_there = false;
164 case DINIT_CP_STARTSERVICE:
165 if (do_pin) service->pinStart();
167 already_there = service->getState() == ServiceState::STARTED;
169 case DINIT_CP_STOPSERVICE:
170 if (do_pin) service->pinStop();
172 already_there = service->getState() == ServiceState::STOPPED;
174 case DINIT_CP_WAKESERVICE:
175 // re-start a stopped service.
176 if (do_pin) service->pinStart();
177 service->start(false);
178 already_there = service->getState() == ServiceState::STARTED;
180 default: /* DINIT_CP_RELEASESERVICE */
181 // remove explicit start from a service, without necessarily stopping it.
186 char ack_buf[] = { (char)(already_there ? DINIT_RP_ALREADYSS : DINIT_RP_ACK) };
188 if (! queuePacket(ack_buf, 1)) return;
191 // Clear the packet from the buffer
192 rbuf.consume(pkt_size);
197 void ControlConn::processUnpinService()
201 constexpr int pkt_size = 1 + sizeof(handle_t);
203 if (rbuf.get_length() < pkt_size) {
208 // 1 byte: packet type
209 // 4 bytes: service handle
212 rbuf.extract((char *) &handle, 1, sizeof(handle));
214 ServiceRecord *service = findServiceForKey(handle);
215 if (service == nullptr) {
216 // Service handle is bad
217 char badreqRep[] = { DINIT_RP_BADREQ };
218 if (! queuePacket(badreqRep, 1)) return;
219 bad_conn_close = true;
220 ev_io_set(&iob, iob.fd, EV_WRITE);
225 char ack_buf[] = { (char) DINIT_RP_ACK };
226 if (! queuePacket(ack_buf, 1)) return;
229 // Clear the packet from the buffer
230 rbuf.consume(pkt_size);
235 ControlConn::handle_t ControlConn::allocateServiceHandle(ServiceRecord *record)
237 bool is_unique = true;
238 handle_t largest_seen = 0;
239 handle_t candidate = 0;
240 for (auto p : keyServiceMap) {
241 if (p.first > largest_seen) largest_seen = p.first;
242 if (p.first == candidate) {
243 if (largest_seen == std::numeric_limits<handle_t>::max()) throw std::bad_alloc();
244 candidate = largest_seen + 1;
246 is_unique &= (p.second != record);
249 keyServiceMap[candidate] = record;
250 serviceKeyMap.insert(std::make_pair(record, candidate));
253 record->addListener(this);
260 bool ControlConn::queuePacket(const char *pkt, unsigned size) noexcept
262 if (bad_conn_close) return false;
264 bool was_empty = outbuf.empty();
267 int wr = write(iob.fd, pkt, size);
269 if (errno == EPIPE) {
273 if (errno != EAGAIN && errno != EWOULDBLOCK) {
280 if ((unsigned)wr == size) {
287 ev_io_set(&iob, iob.fd, EV_READ | EV_WRITE);
290 // Create a vector out of the (remaining part of the) packet:
292 outbuf.emplace_back(pkt, pkt + size);
295 catch (std::bad_alloc &baexc) {
296 // Mark the connection bad, and stop reading further requests
297 bad_conn_close = true;
300 // We can't send out-of-memory response as we already wrote as much as we
301 // could above. Neither can we later send the response since we have currently
302 // sent an incomplete packet. All we can do is close the connection.
306 ev_io_set(&iob, iob.fd, EV_WRITE);
313 bool ControlConn::queuePacket(std::vector<char> &&pkt) noexcept
315 if (bad_conn_close) return false;
317 bool was_empty = outbuf.empty();
321 // We can try sending the packet immediately:
322 int wr = write(iob.fd, pkt.data(), pkt.size());
324 if (errno == EPIPE) {
328 if (errno != EAGAIN && errno != EWOULDBLOCK) {
335 if ((unsigned)wr == pkt.size()) {
341 ev_io_set(&iob, iob.fd, EV_READ | EV_WRITE);
345 outbuf.emplace_back(pkt);
348 catch (std::bad_alloc &baexc) {
349 // Mark the connection bad, and stop reading further requests
350 bad_conn_close = true;
353 // We can't send out-of-memory response as we already wrote as much as we
354 // could above. Neither can we later send the response since we have currently
355 // sent an incomplete packet. All we can do is close the connection.
359 ev_io_set(&iob, iob.fd, EV_WRITE);
365 bool ControlConn::rollbackComplete() noexcept
367 char ackBuf[2] = { DINIT_ROLLBACK_COMPLETED, 2 };
368 return queuePacket(ackBuf, 2);
371 bool ControlConn::dataReady() noexcept
375 int r = rbuf.fill(fd);
377 // Note file descriptor is non-blocking
379 if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
393 if (rbuf.get_length() >= chklen) {
397 catch (std::bad_alloc &baexc) {
402 if (rbuf.get_length() == 1024) {
405 // TODO error response?
406 bad_conn_close = true;
407 ev_io_set(&iob, iob.fd, EV_WRITE);
413 void ControlConn::sendData() noexcept
415 if (outbuf.empty() && bad_conn_close) {
418 char oomBuf[] = { DINIT_RP_OOM };
419 write(iob.fd, oomBuf, 1);
425 vector<char> & pkt = outbuf.front();
426 char *data = pkt.data();
427 int written = write(iob.fd, data + outpkt_index, pkt.size() - outpkt_index);
429 if (errno == EPIPE) {
433 else if (errno == EAGAIN || errno == EWOULDBLOCK) {
434 // spurious readiness notification?
437 log(LogLevel::ERROR, "Error writing to control connection: ", strerror(errno));
443 outpkt_index += written;
444 if (outpkt_index == pkt.size()) {
445 // We've finished this packet, move on to the next:
448 if (outbuf.empty() && ! oom_close) {
449 if (! bad_conn_close) {
450 ev_io_set(&iob, iob.fd, EV_READ);
459 ControlConn::~ControlConn() noexcept
462 ev_io_stop(loop, &iob);
464 // Clear service listeners
465 for (auto p : serviceKeyMap) {
466 p.first->removeListener(this);
469 active_control_conns--;