Commit 1f7100b0 authored by Ciarán Ó Rourke's avatar Ciarán Ó Rourke
Browse files

Make server a daemon process. Support for start and stop subcommands.

Outline usage profile of the fiphoboserver executable in the README

Change log:
* start server daemon subcommand
* move appropriate flags to start subcommand
* main function forks process given start command
* stop command to kill running server process
* integration tests: reflect subcommand invoking of server
* detail usage in README
parent ba82dd36
Pipeline #1441 passed with stages
in 6 minutes and 54 seconds
......@@ -53,7 +53,7 @@ Option | Effect
`-DCMAKE_CXX_COMPILER=...` | Set the C++ compiler.
`-DCMAKE_CXX_FLAGS=...` | Set the flags to pass to the C++ compiler. Overrides the default flags.
#### Enabling / Disabling Sections of FIPhoboServer
#### Enabling/Disabling Sections of FIPhoboServer
Option | Effect
------ | ------
`-DFIPHOBOSERVER_BUILD_TESTS=...` | Set to `ON` to build FIPhoboServer tests and enable the `make test` target, or `OFF` to skip (Default `OFF`).
......@@ -66,3 +66,28 @@ Integration tests can be run from the `test/integration_tests/` directory with `
## Documentation
The Doxygen documentation can be found [here](doc/Markdown/fiphoboserver).
## Usage
The `fiphoboserver` executable requires exactly one subcommand.
#### Subcommands
Subcommand | Effect
---------- | ------
`start [hostname]` | Start the server daemon, listening to `hostname`.
`stop` | Stop the server daemon.
The `start` subcommand supports a number of optional flags and options.
#### Flags/Options
Flag/Option | Effect
----------- | ------
`--http INT` | Set the port to listen on with HTTP protocol.
`--http2 INT` | Set the port to listen on with HTTP2 protocol.
`-t,--threads INT` | Set the number of threads to listen on (<= 0 will use number of available cores).
`-w,--write-config TEXT` | Write the configuration to `TEXT`
Options can also be given to the `start` subcommand using `--config [config_filename]`. Note that if `hostname` is specified in the configuration file then the `--config` options must be suppied before the `start` subcommand;
```
fiphoboserver --config [config_filename] start
```
......@@ -13,6 +13,10 @@
#include <proxygen/httpserver/HTTPServer.h>
#include <proxygen/httpserver/RequestHandlerFactory.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "fiphoboserver_exception.h"
#include "server/handler_factory.h"
#include "utils/config.h"
......@@ -32,21 +36,74 @@ int main(int argc, char* argv[])
parse_result = config.parse(argc, argv);
}
catch (const fiphoboserver::FIPhoboServerException& ex) {
return 0;
return EXIT_SUCCESS;
}
std::vector<proxygen::HTTPServer::IPConfig> IPs; // NOLINT
try {
IPs = {
{SocketAddress(parse_result.ip, parse_result.http_port, true),
Protocol::HTTP},
{SocketAddress(parse_result.ip, parse_result.http2_port, true),
Protocol::HTTP2},
};
std::string pid_filename("/tmp/fiphoboserver_pid");
if (config.start_daemon()) {
try {
IPs = {
{SocketAddress(parse_result.ip, parse_result.http_port, true),
Protocol::HTTP},
{SocketAddress(parse_result.ip, parse_result.http2_port, true),
Protocol::HTTP2},
};
}
catch (const std::system_error& ex) {
std::cerr << "fiphoboserver: " << ex.what() << '\n';
return EXIT_FAILURE;
}
pid_t pid, sid;
pid = fork();
if (pid < 0) {
return EXIT_FAILURE;
}
if (pid > 0) {
return EXIT_SUCCESS;
}
umask(0);
chdir("/");
sid = setsid();
if (sid < 0) {
return EXIT_FAILURE;
}
std::ofstream pid_file(
pid_filename, std::ofstream::out | std::ofstream::trunc);
pid_file << sid;
pid_file.close();
/* close standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
catch (const std::system_error& ex) {
std::cerr << "fiphoboserver: " << ex.what() << '\n';
return 1;
if (config.stop_daemon()) {
pid_t pid;
std::ifstream pid_file(pid_filename);
if (pid_file.good()) {
pid_file >> pid;
remove(pid_filename.c_str());
}
else {
std::cerr
<< "fiphoboserver: Error stopping the server daemon. Is the server daemon running?"
<< '\n';
return EXIT_FAILURE;
}
int rc = kill(pid, SIGTERM);
if (rc < 0) {
std::cerr
<< "fiphoboserver: Error stopping the server daemon. terminate signal unsuccessful";
}
return EXIT_SUCCESS;
}
proxygen::HTTPServerOptions options;
......@@ -72,5 +129,5 @@ int main(int argc, char* argv[])
std::thread t([&]() { server.start(); });
t.join();
return 0;
return EXIT_SUCCESS;
}
......@@ -15,6 +15,7 @@ cli_options Config::parse(int argc, char* argv[])
m_parser.parse(argc, argv);
}
catch (const CLI::ParseError& e) {
std::cout << "m_hostname = " << m_hostname << std::endl;
m_parser.exit(e);
throw FIPhoboServerException(
"Returning after command line parsing. --help or invalid argument(s)");
......@@ -24,17 +25,23 @@ cli_options Config::parse(int argc, char* argv[])
void Config::define_options()
{
m_parser.add_option("hostname,--hostname", m_ip, "IP/Hostname to bind to")
m_parser.require_subcommand(1, 1);
m_start = m_parser.add_subcommand("start", "start the server daemon");
m_stop = m_parser.add_subcommand("stop", "stop the server daemon");
m_start
->add_option(
"hostname,--hostname", m_hostname, "IP/Hostname to bind to")
->required();
m_parser.add_option(
m_start->add_option(
"--http", m_http_port, "port to listen on with HTTP protocol");
m_parser.add_option(
m_start->add_option(
"--http2", m_http2_port, "port to listen on with HTTP2 protocol");
m_parser.add_option(
m_start->add_option(
"-t,--threads", m_thread_count, "number of threads to listen on");
m_parser.set_config("--config");
m_parser.add_option(
m_start->add_option(
"-w,--write-config", m_config_filename,
"write configuration options to file");
}
......@@ -51,7 +58,7 @@ cli_options Config::handle_arguments()
file.close();
}
return {m_http_port, m_http2_port, m_ip, m_thread_count};
return {m_http_port, m_http2_port, m_hostname, m_thread_count};
}
} // namespace util
......
......@@ -48,33 +48,27 @@ class Config {
/// @returns struct of command line options or error code
///
cli_options parse(int argc, char* argv[]);
///
/// @brief get the HTTP port
///
/// @returns the HTTP port
///
const int http_port() const { return m_http_port; }
///
/// @brief get the HTTP2 port
/// @brief query if the server daemon should be started
///
/// @returns the HTTP2 port
/// @returns whether the start subcommand has been parsed
///
const int http2_port() const { return m_http2_port; }
bool start_daemon() const { return m_start->parsed(); }
///
/// @brief get the IP/Hostname
/// @brief query if the server daemon should be stopped
///
/// @returns the IP/Hostname
/// @returns whether the stop subcommand has been parsed
///
const std::string ip() const { return m_ip; }
bool stop_daemon() const { return m_stop->parsed(); }
///
/// @brief get the number of threads
/// @brief query if the server daemon should be restarted
///
/// @returns the number of threads
/// @returns whether the restart subcommand has been parsed
///
const int threads() const { return m_thread_count; }
bool restart_daemon() const { return m_restart->parsed(); }
private:
///
......@@ -94,6 +88,21 @@ class Config {
///
CLI::App m_parser;
///
/// @brief CLI11 class for start subcommand
///
CLI::App* m_start;
///
/// @brief CLI11 class for stop subcommand
///
CLI::App* m_stop;
///
/// @brief CLI11 class for restart subcommand
///
CLI::App* m_restart;
///
/// @brief port to listen on with HTTP protocol
///
......@@ -108,7 +117,7 @@ class Config {
///
/// @brief IP/Hostname to bind to
///
std::string m_ip;
std::string m_hostname;
///
/// @brief Number of threads to listen on
......
import unittest
import subprocess
import signal
import os
from multiprocessing import Process
......@@ -15,9 +14,10 @@ class Server_test_case(unittest.TestCase):
def setUpClass(cls):
cls.bucket_name = "ichec.phobos.7a3d12c6-632b-4291-b87e-07badca60cd0"
cls.client = s3_client.S3_client('http://localhost:11000/')
cls.server = subprocess.Popen('../../build/fiphoboserver localhost',
shell=True,
preexec_fn=os.setsid)
# start the server daemon
subprocess.run("../../build/fiphoboserver start localhost", shell=True)
cls.put_file_name = 'data/EmperorWu.txt'
cls.get_file_name = 'data/EmperorWu_get.txt'
cls.put_empty_file_name = 'data/empty.txt'
......@@ -28,10 +28,9 @@ class Server_test_case(unittest.TestCase):
def tearDownClass(cls):
os.remove(cls.get_file_name)
os.remove(cls.get_empty_file_name)
cls.server.terminate()
# kill the fiphoboserver process
os.killpg(os.getpgid(cls.server.pid), signal.SIGTERM)
# start the server daemon
subprocess.run("../../build/fiphoboserver stop", shell=True)
def test_file_put_and_get(self):
self.client.put(self.put_file_name, self.bucket_name, self.key)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment