X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=testing%2Flib%2FServer.cpp;h=2ab4d5b70b29b1f4cc675b36a8f105f6c963a98c;hb=b5ed9c3cab58f1a8e519d5551951ab8c25fc3b84;hp=bc5de99fe7a0aeee997535b71bafe6b44e863ff7;hpb=5e54d17fc535a901f384fcbf2cfd420f3a2e7a81;p=awesomized%2Flibmemcached diff --git a/testing/lib/Server.cpp b/testing/lib/Server.cpp index bc5de99f..2ab4d5b7 100644 --- a/testing/lib/Server.cpp +++ b/testing/lib/Server.cpp @@ -1,137 +1,133 @@ #include "Server.hpp" -#include "WaitForExec.hpp" -#include "WaitForConn.hpp" +#include "Connection.hpp" +#include "ForkAndExec.hpp" -#include -#include -#include -#include #include #include -#include -#include +Server::Server(string binary_, Server::argv_t args_) + : binary{move(binary_)} + , args{move(args_)} +{} -using namespace std; - -[[nodiscard]] -auto Server::createArgv() { - auto i = 0, port = -1, socket = -1; - auto arr = new char *[str_args.size() + dyn_args.size()*2 + 2] { - strdup(binary.c_str()) - }; - - for (auto &arg : str_args) { - arr[++i] = strdup(arg.c_str()); - if (arg == "-p") { - port = i + 1; - } else if (arg == "-s") { - socket = i + 1; - } +Server::~Server() { + stop(); + wait(); + if (pipe != -1) { + close(pipe); } - for (auto &arg : dyn_args) { - arr[++i] = strdup(arg.first.c_str()); - arr[++i] = strdup(arg.second(arg.first).c_str()); - if (arg.first == "-p") { - port = i; - } else if (arg.first == "-s") { - socket = i; - } - + if (holds_alternative(socket_or_port)) { + unlink(get(socket_or_port).c_str()); } - arr[i+1] = nullptr; +} - if (socket > -1) { - socket_or_port = arr[socket]; - } else if (port > -1) { - socket_or_port = stoi(arr[port]); +static inline string extractArg(const Server::arg_t &arg_cont, const string &func_arg) { + if (holds_alternative(arg_cont)) { + return get(arg_cont); } else { - socket_or_port = 11211; + return get(arg_cont)(func_arg); } +} - return arr; +static inline void pushArg(vector &arr, const string &arg) { + auto len = arg.size(); + auto str = arg.data(), end = str + len + 1; + auto ptr = new char[len + 1]; + copy(str, end, ptr); + arr.push_back(ptr); +} + +optional Server::handleArg(vector &arr, const string &arg, const arg_func_t &next_arg) { + pushArg(arr, arg); + if (arg == "-p" || arg == "--port") { + auto port = next_arg(arg); + pushArg(arr, port); + pushArg(arr, "-U"); + pushArg(arr, port); + socket_or_port = stoi(port); + return port; + } else if (arg == "-s" || arg == "--unix-socket") { + auto sock = next_arg(arg); + pushArg(arr, sock); + socket_or_port = sock; + return sock; + } + return {}; } [[nodiscard]] -optional Server::createSocket() { - sockaddr_storage addr; - unsigned size = 0; - int sock; +vector Server::createArgv() { + vector arr; + + pushArg(arr, binary); + //pushArg(arr, "-v"); + + for (auto it = args.cbegin(); it != args.cend(); ++it) { + if (holds_alternative(*it)) { + // a single argument + auto arg = extractArg(get(*it), binary); + handleArg(arr, arg, [&it](const string &arg_) { + return extractArg(get(*++it), arg_); + }); + } else { + // an argument pair + auto &[one, two] = get(*it); + auto arg_one = extractArg(one, binary); + auto arg_two = extractArg(two, arg_one); + + auto next = handleArg(arr, arg_one, [&arg_two](const string &) { + return arg_two; + }); + + if (!next.has_value()) { + pushArg(arr, arg_two); + } + } + } - if (holds_alternative(socket_or_port)) { - const auto path = get(socket_or_port); - const auto safe = path.c_str(); - const auto zlen = path.length() + 1; - const auto ulen = sizeof(sockaddr_un) - sizeof(sa_family_t); + arr.push_back(nullptr); - if (zlen >= ulen) { - cerr << "Server::isListening socket(): path too long '" << path << "'\n"; - return {}; - } + return arr; +} - if (0 > (sock = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0))) { - perror("Server::isListening socket()"); - return {}; - } +optional Server::start() { + if (!pid) { + auto argv = createArgv(); + ForkAndExec fork_and_exec{binary.c_str(), argv.data()}; - auto sa = reinterpret_cast(&addr); - sa->sun_family = AF_UNIX; - strncpy(sa->sun_path, safe, zlen); + pipe = fork_and_exec.createPipe(); + pid = fork_and_exec(); - size = sizeof(*sa); - } else { - if (0 > (sock = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0))) { - perror("Server::isListening socket()"); - return {}; + for (auto argp : argv) { + delete [] argp; } - const auto port = get(socket_or_port); - auto sa = reinterpret_cast(&addr); - sa->sin_family = AF_INET; - sa->sin_port = htons(static_cast(port)), - sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - size = sizeof(*sa); + if (!pid) { + return {}; + } } - - return optional{make_tuple(sock, addr, size)}; + return ChildProc{pid, pipe}; } -optional Server::start() { - if (pid) { - return pid; - } - - WaitForExec wait_for_exec; +bool Server::isListening() { + MemcachedPtr memc; - switch (pid = fork()) { - case 0: - execvp(binary.c_str(), createArgv()); - [[fallthrough]]; - case -1: - perror("Server::start fork() & exec()"); - return {}; - - default: - if (!wait_for_exec()) { - cerr << "Server::start exec(): incomplete\n"; - } - return pid; + if (holds_alternative(socket_or_port)) { + if (memcached_server_add_unix_socket(*memc, get(socket_or_port).c_str())) { + return false; + } + } else { + if (memcached_server_add(*memc, "localhost", get(socket_or_port))) { + return false; + } } -} - -bool Server::isListening(int max_timeout) { - auto conn = createSocket(); - if (!conn) { + Malloced stat(memcached_stat(*memc, nullptr, nullptr)); + if (!*stat || !stat->pid || stat->pid != pid) { return false; } - WaitForConn wait_for_conn{ - {conn.value()}, - Poll{POLLOUT, 2, max_timeout} - }; - return wait_for_conn(); + return true; } bool Server::stop() { @@ -159,7 +155,15 @@ bool Server::check() { bool Server::wait(int flags) { if (pid && pid == waitpid(pid, &status, flags)) { + if (drain().length()) { + cerr << "Ouput of " << *this << ":\n" << output << endl; + output.clear(); + } pid = 0; + if (pipe != -1) { + close(pipe); + pipe = -1; + } return true; } return false; @@ -168,3 +172,64 @@ bool Server::wait(int flags) { bool Server::tryWait() { return wait(WNOHANG); } + +Server::Server(const Server &s) { + binary = s.binary; + args = s.args; + socket_or_port = s.socket_or_port; +} + +Server &Server::operator=(const Server &s) { + binary = s.binary; + args = s.args; + socket_or_port = s.socket_or_port; + return *this; +} + +pid_t Server::getPid() const { + return pid; +} + +const string &Server::getBinary() const { + return binary; +} + +const Server::argv_t &Server::getArgs() const { + return args; +} + +const socket_or_port_t &Server::getSocketOrPort() const { + return socket_or_port; +} + +int Server::getPipe() const { + return pipe; +} + +string &Server::drain() { + if (pipe != -1) { + again: + char read_buf[1<<12]; + auto read_len = read(pipe, read_buf, sizeof(read_buf)); + + if (read_len > 0) { + output.append(read_buf, read_len); + goto again; + } + if (read_len == -1) { + switch (errno) { + case EINTR: + goto again; + default: + perror("Server::drain read()"); + [[fallthrough]]; + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + break; + } + } + } + return output; +}