b3d0118baf9a51f14dbab99101e8f85355ef79f5
[awesomized/libmemcached] / testing / 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{forward<string>(binary_)}
10 , args{forward<argv_t>(args_)}
11 {}
12
13 Server::~Server() {
14 stop();
15 wait();
16 if (holds_alternative<string>(socket_or_port)) {
17 unlink(get<string>(socket_or_port).c_str());
18 }
19 }
20
21 static inline string extractArg(const Server::arg_t &arg_cont, const string &func_arg) {
22 if (holds_alternative<string>(arg_cont)) {
23 return get<string>(arg_cont);
24 } else {
25 return get<Server::arg_func_t>(arg_cont)(func_arg);
26 }
27 }
28
29 static inline void pushArg(vector<char *> &arr, const string &arg) {
30 auto len = arg.size();
31 auto str = arg.data(), end = str + len + 1;
32 auto ptr = new char[len + 1];
33 copy(str, end, ptr);
34 arr.push_back(ptr);
35 }
36
37 optional<string> Server::handleArg(vector<char *> &arr, const string &arg, const arg_func_t &next_arg) {
38 pushArg(arr, arg);
39 if (arg == "-p" || arg == "--port") {
40 auto port = next_arg(arg);
41 pushArg(arr, port);
42 pushArg(arr, "-U");
43 pushArg(arr, port);
44 socket_or_port = stoi(port);
45 return port;
46 } else if (arg == "-s" || arg == "--unix-socket") {
47 auto sock = next_arg(arg);
48 pushArg(arr, sock);
49 socket_or_port = sock;
50 return sock;
51 }
52 return {};
53 }
54
55 [[nodiscard]]
56 vector<char *> Server::createArgv() {
57 vector<char *> arr;
58
59 pushArg(arr, binary);
60 pushArg(arr, "-v");
61
62 for (auto it = args.cbegin(); it != args.cend(); ++it) {
63 if (holds_alternative<arg_t>(*it)) {
64 // a single argument
65 auto arg = extractArg(get<arg_t>(*it), binary);
66 handleArg(arr, arg, [&it](const string &arg_) {
67 return extractArg(get<arg_t>(*++it), arg_);
68 });
69 } else {
70 // an argument pair
71 auto &[one, two] = get<arg_pair_t>(*it);
72 auto arg_one = extractArg(one, binary);
73 auto arg_two = extractArg(two, arg_one);
74
75 auto next = handleArg(arr, arg_one, [&arg_two](const string &) {
76 return arg_two;
77 });
78
79 if (!next.has_value()) {
80 pushArg(arr, arg_two);
81 }
82 }
83 }
84
85 arr.push_back(nullptr);
86
87 return arr;
88 }
89
90 optional<pid_t> Server::start() {
91 if (pid) {
92 return pid;
93 }
94
95 auto argv = createArgv();
96 auto child = ForkAndExec{binary.c_str(), argv.data()}();
97
98 for (auto argp : argv) {
99 delete [] argp;
100 }
101
102 if (child.has_value()) {
103 pid = child.value();
104 }
105
106 return child;
107 }
108
109 bool Server::isListening() {
110 Connection conn(socket_or_port);
111
112 if (!conn.open()) {
113 return false;
114 }
115 return conn.isOpen();
116 }
117
118 bool Server::stop() {
119 if (!pid) {
120 return true;
121 }
122 if (signalled[SIGTERM]) {
123 return signal(SIGKILL);
124 } else {
125 return signal(SIGTERM);
126 }
127 }
128
129 bool Server::signal(int signo) {
130 if (!pid) {
131 return false;
132 }
133 signalled[signo] += 1;
134 return 0 <= kill(pid, signo);
135 }
136
137 bool Server::check() {
138 return signal(0);
139 }
140
141 bool Server::wait(int flags) {
142 if (pid && pid == waitpid(pid, &status, flags)) {
143 pid = 0;
144 return true;
145 }
146 return false;
147 }
148
149 bool Server::tryWait() {
150 return wait(WNOHANG);
151 }
152
153 Server::Server(const Server &s) {
154 binary = s.binary;
155 args = s.args;
156 socket_or_port = s.socket_or_port;
157 }
158
159 Server &Server::operator=(const Server &s) {
160 binary = s.binary;
161 args = s.args;
162 socket_or_port = s.socket_or_port;
163 return *this;
164 }
165
166 pid_t Server::getPid() const {
167 return pid;
168 }
169
170 const string &Server::getBinary() const {
171 return binary;
172 }
173
174 const Server::argv_t &Server::getArgs() const {
175 return args;
176 }
177
178 const socket_or_port_t &Server::getSocketOrPort() const {
179 return socket_or_port;
180 }
181