123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- #ifndef DINIT_CONTROL_H
- #define DINIT_CONTROL_H
- #include <list>
- #include <vector>
- #include <unordered_map>
- #include <map>
- #include <limits>
- #include <cstddef>
- #include <unistd.h>
- #include "dinit.h"
- #include "dinit-log.h"
- #include "control-cmds.h"
- #include "service-listener.h"
- #include "cpbuffer.h"
- // Control connection for dinit
- class control_conn_t;
- class control_conn_watcher;
- // forward-declaration of callback:
- inline dasynq::rearm control_conn_cb(eventloop_t *loop, control_conn_watcher *watcher, int revents);
- // Pointer to the control connection that is listening for rollback completion
- extern control_conn_t * rollback_handler_conn;
- extern int active_control_conns;
- // "packet" format:
- // (1 byte) packet type
- // (N bytes) additional data (service name, etc)
- // for LOADSERVICE/FINDSERVICE:
- // (2 bytes) service name length
- // (M bytes) service name (without nul terminator)
- // Information packet:
- // (1 byte) packet type, >= 100
- // (1 byte) packet length (including all fields)
- // N bytes: packet data (N = (length - 2))
- class service_set;
- class service_record;
- class control_conn_watcher : public eventloop_t::bidi_fd_watcher_impl<control_conn_watcher>
- {
- inline rearm receive_event(eventloop_t &loop, int fd, int flags) noexcept
- {
- return control_conn_cb(&loop, this, flags);
- }
- eventloop_t * event_loop;
- public:
- control_conn_watcher(eventloop_t & event_loop_p) : event_loop(&event_loop_p)
- {
- // constructor
- }
- control_conn_watcher(const control_conn_watcher &) = delete;
- void operator=(const control_conn_watcher &) = delete;
- rearm read_ready(eventloop_t &loop, int fd) noexcept
- {
- return receive_event(loop, fd, dasynq::IN_EVENTS);
- }
-
- rearm write_ready(eventloop_t &loop, int fd) noexcept
- {
- return receive_event(loop, fd, dasynq::OUT_EVENTS);
- }
- void set_watches(int flags)
- {
- eventloop_t::bidi_fd_watcher::set_watches(*event_loop, flags);
- }
- };
- class control_conn_t : private service_listener
- {
- friend rearm control_conn_cb(eventloop_t *loop, control_conn_watcher *watcher, int revents);
-
- public:
- // A mapping between service records and their associated numerical identifier used
- // in communction
- using handle_t = uint32_t;
- private:
- control_conn_watcher iob;
- eventloop_t &loop;
- service_set *services;
-
- bool bad_conn_close = false; // close when finished output?
- bool oom_close = false; // send final 'out of memory' indicator
- // The packet length before we need to re-check if the packet is complete.
- // process_packet() will not be called until the packet reaches this size.
- int chklen;
-
- // Receive buffer
- cpbuffer<1024> rbuf;
-
- template <typename T> using list = std::list<T>;
- template <typename T> using vector = std::vector<T>;
-
- std::unordered_multimap<service_record *, handle_t> service_key_map;
- std::map<handle_t, service_record *> key_service_map;
-
- // Buffer for outgoing packets. Each outgoing back is represented as a vector<char>.
- list<vector<char>> outbuf;
- // Current index within the first outgoing packet (all previous bytes have been sent).
- unsigned outpkt_index = 0;
-
- // Queue a packet to be sent
- // Returns: false if the packet could not be queued and a suitable error packet
- // could not be sent/queued (the connection should be closed);
- // true (with bad_conn_close == false) if the packet was successfully
- // queued;
- // true (with bad_conn_close == true) if the packet was not successfully
- // queued (but a suitable error packate has been queued).
- // The in/out watch enabled state will also be set appropriately.
- bool queue_packet(vector<char> &&v) noexcept;
- bool queue_packet(const char *pkt, unsigned size) noexcept;
- // Process a packet.
- // Returns: true (with bad_conn_close == false) if successful
- // true (with bad_conn_close == true) if an error packet was queued
- // false if an error occurred but no error packet could be queued
- // (connection should be closed).
- // Throws:
- // std::bad_alloc - if an out-of-memory condition prevents processing
- bool process_packet();
-
- // Process a STARTSERVICE/STOPSERVICE packet. May throw std::bad_alloc.
- bool process_start_stop(int pktType);
-
- // Process a FINDSERVICE/LOADSERVICE packet. May throw std::bad_alloc.
- bool process_find_load(int pktType);
- // Process an UNPINSERVICE packet. May throw std::bad_alloc.
- bool process_unpin_service();
-
- // Process an UNLOADSERVICE packet.
- bool process_unload_service();
- // List all loaded services and their state.
- bool list_services();
- // Add a dependency between two services.
- bool add_service_dep(bool do_start = false);
- // Remove a dependency between two services.
- bool rm_service_dep();
- // Query service path / load mechanism.
- bool query_load_mech();
- // Notify that data is ready to be read from the socket. Returns true if the connection should
- // be closed.
- bool data_ready() noexcept;
-
- bool send_data() noexcept;
-
- // Allocate a new handle for a service; may throw std::bad_alloc
- handle_t allocate_service_handle(service_record *record);
-
- // Find the service corresponding to a service handle; returns nullptr if not found.
- service_record *find_service_for_key(handle_t key) noexcept
- {
- try {
- return key_service_map.at(key);
- }
- catch (std::out_of_range &exc) {
- return nullptr;
- }
- }
-
- // Close connection due to out-of-memory condition.
- void do_oom_close() noexcept
- {
- bad_conn_close = true;
- oom_close = true;
- iob.set_watches(dasynq::OUT_EVENTS);
- }
-
- // Process service event broadcast.
- // Note that this can potentially be called during packet processing (upon issuing
- // service start/stop orders etc).
- void service_event(service_record * service, service_event_t event) noexcept final override
- {
- // For each service handle corresponding to the event, send an information packet.
- auto range = service_key_map.equal_range(service);
- auto & i = range.first;
- auto & end = range.second;
- try {
- while (i != end) {
- uint32_t key = i->second;
- std::vector<char> pkt;
- constexpr int pktsize = 3 + sizeof(key);
- pkt.reserve(pktsize);
- pkt.push_back(DINIT_IP_SERVICEEVENT);
- pkt.push_back(pktsize);
- char * p = (char *) &key;
- for (int j = 0; j < (int)sizeof(key); j++) {
- pkt.push_back(*p++);
- }
- pkt.push_back(static_cast<char>(event));
- queue_packet(std::move(pkt));
- ++i;
- }
- }
- catch (std::bad_alloc &exc) {
- do_oom_close();
- }
- }
-
- public:
- control_conn_t(eventloop_t &loop, service_set * services_p, int fd)
- : iob(loop), loop(loop), services(services_p), chklen(0)
- {
- iob.add_watch(loop, fd, dasynq::IN_EVENTS);
- active_control_conns++;
- }
-
- control_conn_t(const control_conn_t &) = delete;
- bool rollback_complete() noexcept;
-
- virtual ~control_conn_t() noexcept;
- };
- inline dasynq::rearm control_conn_cb(eventloop_t * loop, control_conn_watcher * watcher, int revents)
- {
- // Get the address of the containing control_connt_t object:
- _Pragma ("GCC diagnostic push")
- _Pragma ("GCC diagnostic ignored \"-Winvalid-offsetof\"")
- uintptr_t cc_addr = reinterpret_cast<uintptr_t>(watcher) - offsetof(control_conn_t, iob);
- control_conn_t *conn = reinterpret_cast<control_conn_t *>(cc_addr);
- _Pragma ("GCC diagnostic pop")
- if (revents & dasynq::IN_EVENTS) {
- if (conn->data_ready()) {
- delete conn;
- return dasynq::rearm::REMOVED;
- }
- }
- if (revents & dasynq::OUT_EVENTS) {
- if (conn->send_data()) {
- delete conn;
- return dasynq::rearm::REMOVED;
- }
- }
-
- return dasynq::rearm::NOOP;
- }
- #endif
|