3 #include "ForkAndExec.hpp"
10 Server::Server(string binary_
, Server::argv_t args_
)
11 : binary
{move(binary_
)}
21 if (holds_alternative
<string
>(socket_or_port
)) {
22 unlink(get
<string
>(socket_or_port
).c_str());
26 static inline string
extractArg(const Server::arg_t
&arg_cont
, const string
&func_arg
) {
27 if (holds_alternative
<string
>(arg_cont
)) {
28 return get
<string
>(arg_cont
);
30 return get
<Server::arg_func_t
>(arg_cont
)(func_arg
);
34 static inline void pushArg(vector
<char *> &arr
, const string
&arg
) {
35 auto len
= arg
.size();
36 auto str
= arg
.data(), end
= str
+ len
+ 1;
37 auto ptr
= new char[len
+ 1];
42 optional
<string
> Server::handleArg(vector
<char *> &arr
, const string
&arg
, const arg_func_t
&next_arg
) {
44 if (arg
== "-U" || arg
== "--udp-port") {
45 auto port
= next_arg(arg
);
49 socket_or_port
= stoi(port
);
51 } else if (arg
== "-p" || arg
== "--port") {
52 auto port
= next_arg(arg
);
54 socket_or_port
= stoi(port
);
56 } else if (arg
== "-s" || arg
== "--unix-socket") {
57 auto sock
= next_arg(arg
);
59 socket_or_port
= sock
;
61 } else if (arg
== "-S" || arg
== "--enable-sasl") {
68 vector
<char *> Server::createArgv() {
73 pushArg(arr
, "nobody");
75 for (auto it
= args
.cbegin(); it
!= args
.cend(); ++it
) {
76 if (holds_alternative
<arg_t
>(*it
)) {
78 auto arg
= extractArg(get
<arg_t
>(*it
), binary
);
79 handleArg(arr
, arg
, [&it
](const string
&arg_
) {
80 return extractArg(get
<arg_t
>(*++it
), arg_
);
84 auto &[one
, two
] = get
<arg_pair_t
>(*it
);
85 auto arg_one
= extractArg(one
, binary
);
86 auto arg_two
= extractArg(two
, arg_one
);
88 auto next
= handleArg(arr
, arg_one
, [&arg_two
](const string
&) {
92 if (!next
.has_value()) {
93 pushArg(arr
, arg_two
);
98 arr
.push_back(nullptr);
103 optional
<Server::ChildProc
> Server::start() {
105 auto argv
= createArgv();
106 ForkAndExec fork_and_exec
{binary
.c_str(), argv
.data()};
108 pipe
= fork_and_exec
.createPipe();
109 pid
= fork_and_exec();
111 for (auto argp
: argv
) {
119 return ChildProc
{pid
, pipe
};
122 bool Server::isListening() const {
125 if (holds_alternative
<string
>(socket_or_port
)) {
126 if (memcached_server_add_unix_socket(*memc
, get
<string
>(socket_or_port
).c_str())) {
130 if (memcached_server_add(*memc
, "localhost", get
<int>(socket_or_port
))) {
136 memcached_behavior_set(*memc
, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL
, 1);
137 memcached_set_sasl_auth_data(*memc
, "memcached", "memcached");
140 Malloced
stat(memcached_stat(*memc
, nullptr, nullptr));
141 if (!*stat
|| !stat
->pid
|| stat
->pid
== -1) {
144 if (stat
->pid
!= pid
) {
145 cerr
<< "Another server is listening on " << socket_or_port
146 << " (expected pid " << pid
<< " found pid " << stat
->pid
<< ")\n";
153 bool Server::ensureListening() {
157 return Retry
{[this] {
160 if (!isListening()) {
163 cerr
<< "Collected zombie " << *this << "(old pid=" << old
<< ")\n";
167 return isListening();
171 bool Server::stop() {
175 if (signalled
[SIGTERM
]) {
176 return signal(SIGKILL
);
178 return signal(SIGTERM
);
182 bool Server::signal(int signo
) {
186 signalled
[signo
] += 1;
187 return 0 <= kill(pid
, signo
);
190 bool Server::check() {
194 bool Server::wait(int flags
) {
195 if (pid
&& pid
== waitpid(pid
, &status
, flags
)) {
196 if (drain().length() &&
197 output
.rfind("Signal handled: Terminated", 0) != 0) {
198 cerr
<< "Output of " << *this << ":\n";
200 istringstream iss
{output
};
203 while (getline(iss
, line
)) {
204 cerr
<< " " << line
<< "\n";
207 if (output
.back() != '\n') {
222 bool Server::tryWait() {
223 return wait(WNOHANG
);
226 Server::Server(const Server
&s
) {
229 socket_or_port
= s
.socket_or_port
;
232 Server
&Server::operator=(const Server
&s
) {
235 socket_or_port
= s
.socket_or_port
;
239 pid_t
Server::getPid() const {
243 const string
&Server::getBinary() const {
247 const Server::argv_t
&Server::getArgs() const {
251 const socket_or_port_t
&Server::getSocketOrPort() const {
252 return socket_or_port
;
255 int Server::getPipe() const {
259 string
&Server::drain() {
262 char read_buf
[1<<12];
263 auto read_len
= read(pipe
, read_buf
, sizeof(read_buf
));
266 output
.append(read_buf
, read_len
);
269 if (read_len
== -1) {
274 perror("Server::drain read()");
277 #if EWOULDBLOCK != EAGAIN