3 #include "ForkAndExec.hpp"
8 Server::Server(string binary_
, Server::argv_t args_
)
9 : binary
{move(binary_
)}
19 if (holds_alternative
<string
>(socket_or_port
)) {
20 unlink(get
<string
>(socket_or_port
).c_str());
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
);
28 return get
<Server::arg_func_t
>(arg_cont
)(func_arg
);
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];
40 optional
<string
> Server::handleArg(vector
<char *> &arr
, const string
&arg
, const arg_func_t
&next_arg
) {
42 if (arg
== "-U" || arg
== "--udp-port") {
43 auto port
= next_arg(arg
);
47 socket_or_port
= stoi(port
);
49 } else if (arg
== "-p" || arg
== "--port") {
50 auto port
= next_arg(arg
);
52 socket_or_port
= stoi(port
);
54 } else if (arg
== "-s" || arg
== "--unix-socket") {
55 auto sock
= next_arg(arg
);
57 socket_or_port
= sock
;
59 } else if (arg
== "-S" || arg
== "--enable-sasl") {
66 vector
<char *> Server::createArgv() {
72 for (auto it
= args
.cbegin(); it
!= args
.cend(); ++it
) {
73 if (holds_alternative
<arg_t
>(*it
)) {
75 auto arg
= extractArg(get
<arg_t
>(*it
), binary
);
76 handleArg(arr
, arg
, [&it
](const string
&arg_
) {
77 return extractArg(get
<arg_t
>(*++it
), arg_
);
81 auto &[one
, two
] = get
<arg_pair_t
>(*it
);
82 auto arg_one
= extractArg(one
, binary
);
83 auto arg_two
= extractArg(two
, arg_one
);
85 auto next
= handleArg(arr
, arg_one
, [&arg_two
](const string
&) {
89 if (!next
.has_value()) {
90 pushArg(arr
, arg_two
);
95 arr
.push_back(nullptr);
100 optional
<Server::ChildProc
> Server::start() {
102 auto argv
= createArgv();
103 ForkAndExec fork_and_exec
{binary
.c_str(), argv
.data()};
105 pipe
= fork_and_exec
.createPipe();
106 pid
= fork_and_exec();
108 for (auto argp
: argv
) {
116 return ChildProc
{pid
, pipe
};
119 bool Server::isListening() {
122 if (holds_alternative
<string
>(socket_or_port
)) {
123 if (memcached_server_add_unix_socket(*memc
, get
<string
>(socket_or_port
).c_str())) {
127 if (memcached_server_add(*memc
, "localhost", get
<int>(socket_or_port
))) {
133 memcached_behavior_set(*memc
, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL
, 1);
134 memcached_set_sasl_auth_data(*memc
, "memcached", "memcached");
137 Malloced
stat(memcached_stat(*memc
, nullptr, nullptr));
138 if (!*stat
|| !stat
->pid
|| stat
->pid
== -1) {
141 if (stat
->pid
!= pid
) {
142 cerr
<< "Another server is listening on " << socket_or_port
143 << " (expected pid " << pid
<< " found pid " << stat
->pid
<< ")\n";
150 bool Server::ensureListening() {
151 return Retry
{[this] {
154 if (!isListening()) {
159 return isListening();
163 bool Server::stop() {
167 if (signalled
[SIGTERM
]) {
168 return signal(SIGKILL
);
170 return signal(SIGTERM
);
174 bool Server::signal(int signo
) {
178 signalled
[signo
] += 1;
179 return 0 <= kill(pid
, signo
);
182 bool Server::check() {
186 bool Server::wait(int flags
) {
187 if (pid
&& pid
== waitpid(pid
, &status
, flags
)) {
188 if (drain().length() && output
!= "Signal handled: Terminated.\n") {
189 cerr
<< "Output of " << *this << ":\n";
191 istringstream iss
{output
};
194 while (getline(iss
, line
)) {
195 cerr
<< " " << line
<< "\n";
198 if (output
.back() != '\n') {
213 bool Server::tryWait() {
214 return wait(WNOHANG
);
217 Server::Server(const Server
&s
) {
220 socket_or_port
= s
.socket_or_port
;
223 Server
&Server::operator=(const Server
&s
) {
226 socket_or_port
= s
.socket_or_port
;
230 pid_t
Server::getPid() const {
234 const string
&Server::getBinary() const {
238 const Server::argv_t
&Server::getArgs() const {
242 const socket_or_port_t
&Server::getSocketOrPort() const {
243 return socket_or_port
;
246 int Server::getPipe() const {
250 string
&Server::drain() {
253 char read_buf
[1<<12];
254 auto read_len
= read(pipe
, read_buf
, sizeof(read_buf
));
257 output
.append(read_buf
, read_len
);
260 if (read_len
== -1) {
265 perror("Server::drain read()");
268 #if EWOULDBLOCK != EAGAIN