Commit aeaa06fe authored by louiz’'s avatar louiz’

Add the child handler, and the master and slave proto files

It manages subprocesses, wait for the SIGCHILD, etc
parent 6ac6ecb3
#include <iostream>
#include <logging/logging.hpp>
#include <network/transfer_sender.hpp>
#include <network/remote_client.hpp>
#include <master_server/remote_client.hpp>
using namespace std::string_literals;
......
// Object used only by the MenuClient and the MasterServer. It includes
// things like authentication, social (friends list, messages, user info,
// etc), game info and negociation, etc
package ser.master;
message User
{
required fixed64 bataid = 1;
optional string login = 2;
}
message Authenticate
{
required string login = 1;
required string password = 2;
}
message AuthResponse
{
enum Result {
SUCCESS = 0;
ERROR_INVALID_LOGIN = 1;
ERROR_INVALID_PASSWORD = 2;
ERROR_BANNED = 3;
}
optional AuthResponse.Result result = 1 [default = ERROR_INVALID_LOGIN];
}
message GameConnectionInfo
{
required uint32 port = 1;
required uint32 ip = 2;
required string password = 3;
}
// Object used between a Master and a Slave. It is used to negociate a
// game, get some information about the Slave, send informations about the
// running games (mainly the result) etc
import "game.proto";
package ser.slave;
message InfoRequest
{
}
message StartGameRequest
{
required uint64 game_id = 1;
// TODO: Ability to spawn a game from an existing replay
// optional Replay;
// optional StartTurn;
}
message SlaveInfo
{
repeated ser.game.GameInfo games_info = 1;
optional uint32 free_slots = 2;
}
#include <slave/child_game.hpp>
#include <logging/logging.hpp>
ChildGame::ChildGame(Slave* slave):
pid(0),
slave(slave)
{
this->ipc.watch_read([this](const std::string& msg)
{
log_debug("Received things from child " << this->pid << ": [" << msg << "]");
});
}
void ChildGame::on_end()
{
log_debug("ChildGame::on_end()");
}
std::string ChildGame::get_ipc_endpoint_path() const
{
return this->ipc.get_path();
}
void ChildGame::set_pid(const pid_t pid)
{
this->pid = pid;
}
#ifndef CHILD_GAME_HPP_INCLUDED
#define CHILD_GAME_HPP_INCLUDED
#include <network/ipc_endpoint.hpp>
#include <string>
class Slave;
class ChildGame
{
public:
ChildGame(Slave* slave);
~ChildGame() = default;
void on_end();
std::string get_ipc_endpoint_path() const;
void set_pid(const pid_t pid);
private:
Slave* slave;
IPCEndpoint ipc;
pid_t pid;
ChildGame(const ChildGame&) = delete;
ChildGame(ChildGame&&) = delete;
ChildGame& operator=(const ChildGame&) = delete;
ChildGame& operator=(ChildGame&&) = delete;
};
#endif /* CHILD_GAME_HPP_INCLUDED */
#include <slave/children_handler.hpp>
#include <slave/child_game.hpp>
#include <network/ioservice.hpp>
#include <logging/logging.hpp>
#include <thread>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
ChildrenHandler::ChildrenHandler(Slave* slave):
slave(slave),
signal_set(IoService::get(), SIGCHLD)
{
this->install_signal_handler();
}
void ChildrenHandler::install_signal_handler()
{
this->signal_set.async_wait([this](const boost::system::error_code& error,
int)
{
log_debug("sigchld received");
if (!error)
this->on_child_exit();
else
{
log_error("Failed to wait for SIGCHILD: " << error);
}
});
}
void ChildrenHandler::on_child_exit()
{
log_debug("on_child_exit");
siginfo_t siginfo;
siginfo.si_pid = 0;
int res = ::waitid(P_ALL, 0, &siginfo, WEXITED|WNOHANG);
if (res != 0)
perror("waitid");
else if (siginfo.si_pid == 0)
log_error("SIGCHLD received but no child process exited.");
else
{
pid_t pid = siginfo.si_pid;
// TODO do things differently if the process exited unexpectedly, see
// siginfo.si_code and siginfo.si_status
auto it = this->children.find(pid);
if (it == this->children.end())
log_error("Child process number " << pid << " exited but is not managed by us.");
else
{
it->second->on_end();
this->children.erase(it);
}
}
this->install_signal_handler();
}
void ChildrenHandler::start_subprocess()
{
auto child = std::make_unique<ChildGame>(this->slave);
pid_t pid = fork();
if (pid == 0)
{
const short port = 7879;
const char* argv[6] = {
"./batajelo_game_server",
"-p", std::to_string(port).data(),
"-i", child->get_ipc_endpoint_path().data(),
nullptr
};
execv("./batajelo_game_server", const_cast<char* const*>(argv));
}
else
this->children[pid] = std::move(child);
}
std::size_t ChildrenHandler::size() const
{
return this->children.size();
}
#ifndef CHILDREN_HANDLER_HPP_INCLUDED
#define CHILDREN_HANDLER_HPP_INCLUDED
/**
* Spawn children processes, and associate an object with each child PID.
*
* Use boost asio and a signal handler to manage the lifetime of the children
* and properly wait() for them.
*
* Instantiate a communication channel with nanomsg IPC (man nn_ipc)
*/
#include <slave/child_game.hpp>
#include <boost/asio.hpp>
#include <unistd.h>
#include <memory>
#include <map>
class Slave;
class ChildrenHandler
{
public:
ChildrenHandler(Slave*);
~ChildrenHandler() = default;
void start_subprocess();
std::size_t size() const;
private:
Slave* slave;
void install_signal_handler();
void on_child_exit();
boost::asio::signal_set signal_set;
std::map<pid_t, std::unique_ptr<ChildGame>> children;
ChildrenHandler(const ChildrenHandler&) = delete;
ChildrenHandler(ChildrenHandler&&) = delete;
ChildrenHandler& operator=(const ChildrenHandler&) = delete;
ChildrenHandler& operator=(ChildrenHandler&&) = delete;
};
#endif /* CHILDREN_HANDLER_HPP_INCLUDED */
Markdown is supported
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