break;
}
case DINIT_CP_WAKESERVICE:
- // re-start a stopped service (do not mark as required)
+ {
+ // re-attach a service to its (started) dependents, causing it to start.
if (services->is_shutting_down()) {
ack_buf[0] = DINIT_RP_NAK;
break;
}
+ bool found_dpt = false;
+ for (auto dpt : service->get_dependents()) {
+ auto from = dpt->get_from();
+ auto from_state = from->get_state();
+ if (from_state == service_state_t::STARTED || from_state == service_state_t::STARTING) {
+ found_dpt = true;
+ if (! dpt->holding_acq) {
+ dpt->get_from()->start_dep(*dpt);
+ }
+ }
+ }
+ if (! found_dpt) {
+ ack_buf[0] = DINIT_RP_NAK;
+ }
+
if (do_pin) service->pin_start();
- service->start(false);
services->process_queues();
if (service->get_state() == service_state_t::STARTED) ack_buf[0] = DINIT_RP_ALREADYSS;
break;
+ }
case DINIT_CP_RELEASESERVICE:
// remove required mark, stop if not required by dependents
if (do_pin) service->pin_stop();
delete cc;
}
+void cptest_wake()
+{
+ service_set sset;
+
+ const char * const service_name1 = "test-service-1";
+ const char * const service_name2 = "test-service-2";
+
+ service_record *s1 = new service_record(&sset, service_name1, service_type_t::INTERNAL, {});
+ sset.add_service(s1);
+ service_record *s2 = new service_record(&sset, service_name2, service_type_t::INTERNAL,
+ {{ s1, dependency_type::WAITS_FOR }});
+ sset.add_service(s2);
+
+ s2->start();
+ sset.process_queues();
+
+ s1->stop(true);
+ sset.process_queues();
+
+ assert(s1->get_state() == service_state_t::STOPPED);
+ assert(s2->get_state() == service_state_t::STARTED);
+
+ int fd = bp_sys::allocfd();
+ auto *cc = new control_conn_t(event_loop, &sset, fd);
+
+ // Get a service handle:
+ std::vector<char> cmd = { DINIT_CP_FINDSERVICE };
+ uint16_t name_len = strlen(service_name1);
+ char *name_len_cptr = reinterpret_cast<char *>(&name_len);
+ cmd.insert(cmd.end(), name_len_cptr, name_len_cptr + sizeof(name_len));
+ cmd.insert(cmd.end(), service_name1, service_name1 + name_len);
+
+ bp_sys::supply_read_data(fd, std::move(cmd));
+
+ event_loop.regd_bidi_watchers[fd]->read_ready(event_loop, fd);
+
+ std::vector<char> wdata;
+ bp_sys::extract_written_data(fd, wdata);
+
+ assert(wdata.size() == 3 + sizeof(control_conn_t::handle_t));
+ assert(wdata[0] == DINIT_RP_SERVICERECORD);
+ service_state_t s = static_cast<service_state_t>(wdata[1]);
+ assert(s == service_state_t::STOPPED);
+ service_state_t ts = static_cast<service_state_t>(wdata[6]);
+ assert(ts == service_state_t::STOPPED);
+
+ control_conn_t::handle_t h1;
+ std::copy(wdata.data() + 2, wdata.data() + 2 + sizeof(h1), reinterpret_cast<char *>(&h1));
+
+ // Wake s1:
+ cmd = { DINIT_CP_WAKESERVICE, 0 /* don't pin */ };
+ char * h_cp = reinterpret_cast<char *>(&h1);
+ cmd.insert(cmd.end(), h_cp, h_cp + sizeof(h1));
+ bp_sys::supply_read_data(fd, std::move(cmd));
+
+ event_loop.regd_bidi_watchers[fd]->read_ready(event_loop, fd);
+ bp_sys::extract_written_data(fd, wdata);
+
+ assert(wdata.size() == 1 + 7 /* ACK reply + info packet */);
+ assert(wdata[0] == DINIT_IP_SERVICEEVENT);
+ // packetsize, key (handle), event
+ assert(wdata[1] == 7);
+ control_conn_t::handle_t ip_h;
+ std::copy(wdata.data() + 2, wdata.data() + 2 + sizeof(ip_h), reinterpret_cast<char *>(&ip_h));
+ assert(ip_h == h1);
+ assert(wdata[6] == static_cast<int>(service_event_t::STARTED));
+
+ // and then the ack (already started):
+ assert(wdata[7] == DINIT_RP_ALREADYSS);
+
+ // now stop s2 (and therefore s1):
+ s2->stop(true);
+ sset.process_queues();
+ assert(s1->get_state() == service_state_t::STOPPED);
+ assert(s2->get_state() == service_state_t::STOPPED);
+
+ // Clear any info packets:
+ bp_sys::extract_written_data(fd, wdata);
+
+ // Trying to wake s1 should now fail:
+ cmd = { DINIT_CP_WAKESERVICE, 0 /* don't pin */ };
+ cmd.insert(cmd.end(), h_cp, h_cp + sizeof(h1));
+ bp_sys::supply_read_data(fd, std::move(cmd));
+
+ event_loop.regd_bidi_watchers[fd]->read_ready(event_loop, fd);
+ bp_sys::extract_written_data(fd, wdata);
+
+ assert(wdata.size() == 1);
+ assert(wdata[0] == DINIT_RP_NAK);
+
+ delete cc;
+}
+
#define RUN_TEST(name, spacing) \
std::cout << #name "..." spacing << std::flush; \
RUN_TEST(cptest_addrmdeps, " ");
RUN_TEST(cptest_enableservice, " ");
RUN_TEST(cptest_restart, " ");
+ RUN_TEST(cptest_wake, " ");
return 0;
}