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() {
71 pushArg(arr
, "nobody");
73 for (auto it
= args
.cbegin(); it
!= args
.cend(); ++it
) {
74 if (holds_alternative
<arg_t
>(*it
)) {
76 auto arg
= extractArg(get
<arg_t
>(*it
), binary
);
77 handleArg(arr
, arg
, [&it
](const string
&arg_
) {
78 return extractArg(get
<arg_t
>(*++it
), arg_
);
82 auto &[one
, two
] = get
<arg_pair_t
>(*it
);
83 auto arg_one
= extractArg(one
, binary
);
84 auto arg_two
= extractArg(two
, arg_one
);
86 auto next
= handleArg(arr
, arg_one
, [&arg_two
](const string
&) {
90 if (!next
.has_value()) {
91 pushArg(arr
, arg_two
);
96 arr
.push_back(nullptr);
101 optional
<Server::ChildProc
> Server::start() {
103 auto argv
= createArgv();
104 ForkAndExec fork_and_exec
{binary
.c_str(), argv
.data()};
106 pipe
= fork_and_exec
.createPipe();
107 pid
= fork_and_exec();
109 for (auto argp
: argv
) {
117 return ChildProc
{pid
, pipe
};
120 bool Server::isListening() {
123 if (holds_alternative
<string
>(socket_or_port
)) {
124 if (memcached_server_add_unix_socket(*memc
, get
<string
>(socket_or_port
).c_str())) {
128 if (memcached_server_add(*memc
, "localhost", get
<int>(socket_or_port
))) {
134 memcached_behavior_set(*memc
, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL
, 1);
135 memcached_set_sasl_auth_data(*memc
, "memcached", "memcached");
138 Malloced
stat(memcached_stat(*memc
, nullptr, nullptr));
139 if (!*stat
|| !stat
->pid
|| stat
->pid
== -1) {
142 if (stat
->pid
!= pid
) {
143 cerr
<< "Another server is listening on " << socket_or_port
144 << " (expected pid " << pid
<< " found pid " << stat
->pid
<< ")\n";
151 bool Server::ensureListening() {
152 return Retry
{[this] {
155 if (!isListening()) {
160 return isListening();
164 bool Server::stop() {
168 if (signalled
[SIGTERM
]) {
169 return signal(SIGKILL
);
171 return signal(SIGTERM
);
175 bool Server::signal(int signo
) {
179 signalled
[signo
] += 1;
180 return 0 <= kill(pid
, signo
);
183 bool Server::check() {
187 bool Server::wait(int flags
) {
188 if (pid
&& pid
== waitpid(pid
, &status
, flags
)) {
189 if (drain().length() && output
!= "Signal handled: Terminated.\n") {
190 cerr
<< "Output of " << *this << ":\n";
192 istringstream iss
{output
};
195 while (getline(iss
, line
)) {
196 cerr
<< " " << line
<< "\n";
199 if (output
.back() != '\n') {
214 bool Server::tryWait() {
215 return wait(WNOHANG
);
218 Server::Server(const Server
&s
) {
221 socket_or_port
= s
.socket_or_port
;
224 Server
&Server::operator=(const Server
&s
) {
227 socket_or_port
= s
.socket_or_port
;
231 pid_t
Server::getPid() const {
235 const string
&Server::getBinary() const {
239 const Server::argv_t
&Server::getArgs() const {
243 const socket_or_port_t
&Server::getSocketOrPort() const {
244 return socket_or_port
;
247 int Server::getPipe() const {
251 string
&Server::drain() {
254 char read_buf
[1<<12];
255 auto read_len
= read(pipe
, read_buf
, sizeof(read_buf
));
258 output
.append(read_buf
, read_len
);
261 if (read_len
== -1) {
266 perror("Server::drain read()");
269 #if EWOULDBLOCK != EAGAIN