testing: fix osx
[m6w6/libmemcached] / test / 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 } else if (arg == "-S" || arg == "--enable-sasl") {
55 sasl = true;
56 }
57 return {};
58 }
59
60 [[nodiscard]]
61 vector<char *> Server::createArgv() {
62 vector<char *> arr;
63
64 pushArg(arr, binary);
65 //pushArg(arr, "-v");
66
67 for (auto it = args.cbegin(); it != args.cend(); ++it) {
68 if (holds_alternative<arg_t>(*it)) {
69 // a single argument
70 auto arg = extractArg(get<arg_t>(*it), binary);
71 handleArg(arr, arg, [&it](const string &arg_) {
72 return extractArg(get<arg_t>(*++it), arg_);
73 });
74 } else {
75 // an argument pair
76 auto &[one, two] = get<arg_pair_t>(*it);
77 auto arg_one = extractArg(one, binary);
78 auto arg_two = extractArg(two, arg_one);
79
80 auto next = handleArg(arr, arg_one, [&arg_two](const string &) {
81 return arg_two;
82 });
83
84 if (!next.has_value()) {
85 pushArg(arr, arg_two);
86 }
87 }
88 }
89
90 arr.push_back(nullptr);
91
92 return arr;
93 }
94
95 optional<Server::ChildProc> Server::start() {
96 if (!pid) {
97 auto argv = createArgv();
98 ForkAndExec fork_and_exec{binary.c_str(), argv.data()};
99
100 pipe = fork_and_exec.createPipe();
101 pid = fork_and_exec();
102
103 for (auto argp : argv) {
104 delete [] argp;
105 }
106
107 if (!pid) {
108 return {};
109 }
110 }
111 return ChildProc{pid, pipe};
112 }
113
114 bool Server::isListening() {
115 MemcachedPtr memc;
116
117 if (holds_alternative<string>(socket_or_port)) {
118 if (memcached_server_add_unix_socket(*memc, get<string>(socket_or_port).c_str())) {
119 return false;
120 }
121 } else {
122 if (memcached_server_add(*memc, "localhost", get<int>(socket_or_port))) {
123 return false;
124 }
125 }
126
127 if (sasl) {
128 memcached_behavior_set(*memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
129 memcached_set_sasl_auth_data(*memc, "memcached", "memcached");
130 }
131
132 Malloced stat(memcached_stat(*memc, nullptr, nullptr));
133 if (!*stat || !stat->pid || stat->pid != pid) {
134 return false;
135 }
136
137 return true;
138 }
139
140 bool Server::stop() {
141 if (!pid) {
142 return true;
143 }
144 if (signalled[SIGTERM]) {
145 return signal(SIGKILL);
146 } else {
147 return signal(SIGTERM);
148 }
149 }
150
151 bool Server::signal(int signo) {
152 if (!pid) {
153 return false;
154 }
155 signalled[signo] += 1;
156 return 0 <= kill(pid, signo);
157 }
158
159 bool Server::check() {
160 return signal(0);
161 }
162
163 bool Server::wait(int flags) {
164 if (pid && pid == waitpid(pid, &status, flags)) {
165 if (drain().length()) {
166 cerr << "Ouput of " << *this << ":\n" << output << endl;
167 output.clear();
168 }
169 pid = 0;
170 if (pipe != -1) {
171 close(pipe);
172 pipe = -1;
173 }
174 return true;
175 }
176 return false;
177 }
178
179 bool Server::tryWait() {
180 return wait(WNOHANG);
181 }
182
183 Server::Server(const Server &s) {
184 binary = s.binary;
185 args = s.args;
186 socket_or_port = s.socket_or_port;
187 }
188
189 Server &Server::operator=(const Server &s) {
190 binary = s.binary;
191 args = s.args;
192 socket_or_port = s.socket_or_port;
193 return *this;
194 }
195
196 pid_t Server::getPid() const {
197 return pid;
198 }
199
200 const string &Server::getBinary() const {
201 return binary;
202 }
203
204 const Server::argv_t &Server::getArgs() const {
205 return args;
206 }
207
208 const socket_or_port_t &Server::getSocketOrPort() const {
209 return socket_or_port;
210 }
211
212 int Server::getPipe() const {
213 return pipe;
214 }
215
216 string &Server::drain() {
217 if (pipe != -1) {
218 again:
219 char read_buf[1<<12];
220 auto read_len = read(pipe, read_buf, sizeof(read_buf));
221
222 if (read_len > 0) {
223 output.append(read_buf, read_len);
224 goto again;
225 }
226 if (read_len == -1) {
227 switch (errno) {
228 case EINTR:
229 goto again;
230 default:
231 perror("Server::drain read()");
232 [[fallthrough]];
233 case EAGAIN:
234 #if EWOULDBLOCK != EAGAIN
235 case EWOULDBLOCK:
236 #endif
237 break;
238 }
239 }
240 }
241 return output;
242 }