fix for pid == -1
[awesomized/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 == -1) {
134 return false;
135 }
136 if (stat->pid != pid) {
137 cerr << "Another server is listening on " << socket_or_port
138 << " (expected pid " << pid << " found pid " << stat->pid << ")\n";
139 return false;
140 }
141
142 return true;
143 }
144
145 bool Server::stop() {
146 if (!pid) {
147 return true;
148 }
149 if (signalled[SIGTERM]) {
150 return signal(SIGKILL);
151 } else {
152 return signal(SIGTERM);
153 }
154 }
155
156 bool Server::signal(int signo) {
157 if (!pid) {
158 return false;
159 }
160 signalled[signo] += 1;
161 return 0 <= kill(pid, signo);
162 }
163
164 bool Server::check() {
165 return signal(0);
166 }
167
168 bool Server::wait(int flags) {
169 if (pid && pid == waitpid(pid, &status, flags)) {
170 if (drain().length()) {
171 cerr << "Ouput of " << *this << ":\n" << output << endl;
172 output.clear();
173 }
174 pid = 0;
175 if (pipe != -1) {
176 close(pipe);
177 pipe = -1;
178 }
179 return true;
180 }
181 return false;
182 }
183
184 bool Server::tryWait() {
185 return wait(WNOHANG);
186 }
187
188 Server::Server(const Server &s) {
189 binary = s.binary;
190 args = s.args;
191 socket_or_port = s.socket_or_port;
192 }
193
194 Server &Server::operator=(const Server &s) {
195 binary = s.binary;
196 args = s.args;
197 socket_or_port = s.socket_or_port;
198 return *this;
199 }
200
201 pid_t Server::getPid() const {
202 return pid;
203 }
204
205 const string &Server::getBinary() const {
206 return binary;
207 }
208
209 const Server::argv_t &Server::getArgs() const {
210 return args;
211 }
212
213 const socket_or_port_t &Server::getSocketOrPort() const {
214 return socket_or_port;
215 }
216
217 int Server::getPipe() const {
218 return pipe;
219 }
220
221 string &Server::drain() {
222 if (pipe != -1) {
223 again:
224 char read_buf[1<<12];
225 auto read_len = read(pipe, read_buf, sizeof(read_buf));
226
227 if (read_len > 0) {
228 output.append(read_buf, read_len);
229 goto again;
230 }
231 if (read_len == -1) {
232 switch (errno) {
233 case EINTR:
234 goto again;
235 default:
236 perror("Server::drain read()");
237 [[fallthrough]];
238 case EAGAIN:
239 #if EWOULDBLOCK != EAGAIN
240 case EWOULDBLOCK:
241 #endif
242 break;
243 }
244 }
245 }
246 return output;
247 }