libmemcached/sasl: ensure mech list is zero terminated
[awesomized/libmemcached] / testing / lib / Server.cpp
1 #include "Server.hpp"
2 #include "Connection.hpp"
3 #include "ForkAndExec.hpp"
4
5 #include <sys/wait.h>
6 #include <unistd.h>
7
8 Server::Server(string binary_, Server::argv_t args_)
9 : binary{move(binary_)}
10 , args{move(args_)}
11 {}
12
13 Server::~Server() {
14 stop();
15 wait();
16 if (pipe != -1) {
17 close(pipe);
18 }
19 if (holds_alternative<string>(socket_or_port)) {
20 unlink(get<string>(socket_or_port).c_str());
21 }
22 }
23
24 static inline string extractArg(const Server::arg_t &arg_cont, const string &func_arg) {
25 if (holds_alternative<string>(arg_cont)) {
26 return get<string>(arg_cont);
27 } else {
28 return get<Server::arg_func_t>(arg_cont)(func_arg);
29 }
30 }
31
32 static inline void pushArg(vector<char *> &arr, const string &arg) {
33 auto len = arg.size();
34 auto str = arg.data(), end = str + len + 1;
35 auto ptr = new char[len + 1];
36 copy(str, end, ptr);
37 arr.push_back(ptr);
38 }
39
40 optional<string> Server::handleArg(vector<char *> &arr, const string &arg, const arg_func_t &next_arg) {
41 pushArg(arr, arg);
42 if (arg == "-p" || arg == "--port") {
43 auto port = next_arg(arg);
44 pushArg(arr, port);
45 pushArg(arr, "-U");
46 pushArg(arr, port);
47 socket_or_port = stoi(port);
48 return port;
49 } else if (arg == "-s" || arg == "--unix-socket") {
50 auto sock = next_arg(arg);
51 pushArg(arr, sock);
52 socket_or_port = sock;
53 return sock;
54 }
55 return {};
56 }
57
58 [[nodiscard]]
59 vector<char *> Server::createArgv() {
60 vector<char *> arr;
61
62 pushArg(arr, binary);
63 //pushArg(arr, "-v");
64
65 for (auto it = args.cbegin(); it != args.cend(); ++it) {
66 if (holds_alternative<arg_t>(*it)) {
67 // a single argument
68 auto arg = extractArg(get<arg_t>(*it), binary);
69 handleArg(arr, arg, [&it](const string &arg_) {
70 return extractArg(get<arg_t>(*++it), arg_);
71 });
72 } else {
73 // an argument pair
74 auto &[one, two] = get<arg_pair_t>(*it);
75 auto arg_one = extractArg(one, binary);
76 auto arg_two = extractArg(two, arg_one);
77
78 auto next = handleArg(arr, arg_one, [&arg_two](const string &) {
79 return arg_two;
80 });
81
82 if (!next.has_value()) {
83 pushArg(arr, arg_two);
84 }
85 }
86 }
87
88 arr.push_back(nullptr);
89
90 return arr;
91 }
92
93 optional<Server::ChildProc> Server::start() {
94 if (!pid) {
95 auto argv = createArgv();
96 ForkAndExec fork_and_exec{binary.c_str(), argv.data()};
97
98 pipe = fork_and_exec.createPipe();
99 pid = fork_and_exec();
100
101 for (auto argp : argv) {
102 delete [] argp;
103 }
104
105 if (!pid) {
106 return {};
107 }
108 }
109 return ChildProc{pid, pipe};
110 }
111
112 bool Server::isListening() {
113 MemcachedPtr memc;
114
115 if (holds_alternative<string>(socket_or_port)) {
116 if (memcached_server_add_unix_socket(*memc, get<string>(socket_or_port).c_str())) {
117 return false;
118 }
119 } else {
120 if (memcached_server_add(*memc, "localhost", get<int>(socket_or_port))) {
121 return false;
122 }
123 }
124
125 Malloced stat(memcached_stat(*memc, nullptr, nullptr));
126 if (!*stat || !stat->pid || stat->pid != pid) {
127 return false;
128 }
129
130 return true;
131 }
132
133 bool Server::stop() {
134 if (!pid) {
135 return true;
136 }
137 if (signalled[SIGTERM]) {
138 return signal(SIGKILL);
139 } else {
140 return signal(SIGTERM);
141 }
142 }
143
144 bool Server::signal(int signo) {
145 if (!pid) {
146 return false;
147 }
148 signalled[signo] += 1;
149 return 0 <= kill(pid, signo);
150 }
151
152 bool Server::check() {
153 return signal(0);
154 }
155
156 bool Server::wait(int flags) {
157 if (pid && pid == waitpid(pid, &status, flags)) {
158 if (drain().length()) {
159 cerr << "Ouput of " << *this << ":\n" << output << endl;
160 output.clear();
161 }
162 pid = 0;
163 if (pipe != -1) {
164 close(pipe);
165 pipe = -1;
166 }
167 return true;
168 }
169 return false;
170 }
171
172 bool Server::tryWait() {
173 return wait(WNOHANG);
174 }
175
176 Server::Server(const Server &s) {
177 binary = s.binary;
178 args = s.args;
179 socket_or_port = s.socket_or_port;
180 }
181
182 Server &Server::operator=(const Server &s) {
183 binary = s.binary;
184 args = s.args;
185 socket_or_port = s.socket_or_port;
186 return *this;
187 }
188
189 pid_t Server::getPid() const {
190 return pid;
191 }
192
193 const string &Server::getBinary() const {
194 return binary;
195 }
196
197 const Server::argv_t &Server::getArgs() const {
198 return args;
199 }
200
201 const socket_or_port_t &Server::getSocketOrPort() const {
202 return socket_or_port;
203 }
204
205 int Server::getPipe() const {
206 return pipe;
207 }
208
209 string &Server::drain() {
210 if (pipe != -1) {
211 again:
212 char read_buf[1<<12];
213 auto read_len = read(pipe, read_buf, sizeof(read_buf));
214
215 if (read_len > 0) {
216 output.append(read_buf, read_len);
217 goto again;
218 }
219 if (read_len == -1) {
220 switch (errno) {
221 case EINTR:
222 goto again;
223 default:
224 perror("Server::drain read()");
225 [[fallthrough]];
226 case EAGAIN:
227 #if EWOULDBLOCK != EAGAIN
228 case EWOULDBLOCK:
229 #endif
230 break;
231 }
232 }
233 }
234 return output;
235 }