6 // Client library for Dinit clients
9 using handle_t = uint32_t;
10 using cpbuffer_t = cpbuffer<1024>;
12 class cp_read_exception
16 cp_read_exception(int err) : errcode(err) { }
19 class cp_write_exception
23 cp_write_exception(int err) : errcode(err) { }
26 class cp_old_client_exception
31 class cp_old_server_exception
38 // static_membuf: a buffer of a fixed size (N) with one additional value (of type T). Don't use this
39 // directly, construct via membuf.
40 template <int N> class static_membuf
43 static constexpr int size() { return N; }
50 static_membuf(const T &val)
52 static_assert(sizeof(T) == N, "must initialise with object of correct size");
56 template <int M, typename T>
57 static_membuf(char (&prevbuf)[M], const T &val)
59 static_assert(M + sizeof(T) == N, "size is not correct");
60 memcpy(buf, prevbuf, M);
61 memcpy(buf + M, &val, sizeof(val));
64 const char *data() const { return buf; }
66 template <typename U> static_membuf<N+sizeof(U)> append(const U &u)
68 return static_membuf<N+sizeof(U)>{buf, u};
71 void output(char *out)
73 memcpy(out, buf, size());
77 // "membuf" class provides a compile-time allocated buffer that we can add items to one-by-one. This is
78 // much safer than working with raw buffers and calculating offsets and sizes by hand (and with a decent
79 // compiler the end result is just as efficient).
82 // auto m = membuf().append(value1).append(value2).append(value3);
84 // m.size() - returns total size of the buffer (sizeof(value1)+...)
85 // m.data() - returns a 'const char *' to the buffer contents
90 template <typename U> static_membuf<sizeof(U)> append(const U &u)
92 return static_membuf<sizeof(U)>(u);
96 // Fill a circular buffer from a file descriptor, until it contains at least _rlength_ bytes.
97 // Throws cp_read_exception if the requested number of bytes cannot be read, with:
98 // errcode = 0 if end of stream (remote end closed)
99 // errcode = errno if another error occurred
100 // Note that EINTR is ignored (i.e. the read will be re-tried).
101 inline void fill_buffer_to(cpbuffer_t &buf, int fd, int rlength)
104 int r = buf.fill_to(fd, rlength);
106 if (errno != EINTR) {
107 throw cp_read_exception(errno);
111 throw cp_read_exception(0);
120 // Wait for a reply packet, skipping over any information packets that are received in the meantime.
121 inline void wait_for_reply(cpbuffer_t &rbuffer, int fd)
123 fill_buffer_to(rbuffer, fd, 1);
125 while (rbuffer[0] >= 100) {
126 // Information packet; discard.
127 fill_buffer_to(rbuffer, fd, 2);
128 int pktlen = (unsigned char) rbuffer[1];
130 rbuffer.consume(1); // Consume one byte so we'll read one byte of the next packet
131 fill_buffer_to(rbuffer, fd, pktlen);
132 rbuffer.consume(pktlen - 1);
136 // Wait for an info packet. If any other reply packet comes, throw a cp_read_exception.
137 inline void wait_for_info(cpbuffer_t &rbuffer, int fd)
139 fill_buffer_to(rbuffer, fd, 2);
141 if (rbuffer[0] < 100) {
142 throw cp_read_exception(0);
145 int pktlen = (unsigned char) rbuffer[1];
146 fill_buffer_to(rbuffer, fd, pktlen);
149 // Write *all* the requested buffer and re-try if necessary until
150 // the buffer is written or an unrecoverable error occurs.
151 inline int write_all(int fd, const void *buf, size_t count)
153 const char *cbuf = static_cast<const char *>(buf);
156 int r = write(fd, cbuf, count);
158 if (errno == EINTR) continue;
168 // Write all the requested buffer, and throw an exception on failure.
169 inline void write_all_x(int fd, const void *buf, size_t count)
171 if (write_all(fd, buf, count) == -1) {
172 throw cp_write_exception(errno);
176 // Write all the requested buffer (eg membuf) and throw an exception on failure.
177 template <typename Buf> inline void write_all_x(int fd, const Buf &b)
179 write_all_x(fd, b.data(), b.size());
182 // Check the protocol version is compatible with the client.
183 // minversion - minimum protocol version that client can speak
184 // version - maximum protocol version that client can speak
185 // rbuffer, fd - communication buffer and socket
186 // returns: the actual protocol version
187 // throws an exception on protocol mismatch or error.
188 inline uint16_t check_protocol_version(int minversion, int version, cpbuffer_t &rbuffer, int fd)
190 constexpr int bufsize = 1;
191 char buf[bufsize] = { DINIT_CP_QUERYVERSION };
192 write_all_x(fd, buf, bufsize);
194 wait_for_reply(rbuffer, fd);
195 if (rbuffer[0] != DINIT_RP_CPVERSION) {
196 throw cp_read_exception{0};
199 // DINIT_RP_CVERSION, (2 byte) minimum compatible version, (2 byte) actual version
200 constexpr int rbufsize = 1 + 2 * sizeof(uint16_t);
201 fill_buffer_to(rbuffer, fd, rbufsize);
202 uint16_t rminversion;
205 rbuffer.extract(reinterpret_cast<char *>(&rminversion), 1, sizeof(uint16_t));
206 rbuffer.extract(reinterpret_cast<char *>(&cpversion), 1 + sizeof(uint16_t), sizeof(uint16_t));
207 rbuffer.consume(rbufsize);
209 if (rminversion > version) {
211 throw cp_old_client_exception();
213 if (cpversion < minversion) {
215 throw cp_old_server_exception();