mirror of https://github.com/oxen-io/lokinet
lokinet-bootserv
parent
e58f2c521e
commit
bd0a98ba5a
@ -0,0 +1 @@
|
|||||||
|
lokinet-bootserv
|
@ -0,0 +1,29 @@
|
|||||||
|
# replace your.server.tld with your server's fqdn
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your.server.tld;
|
||||||
|
location / {
|
||||||
|
return 302 https://your.server.tld$request_uri;
|
||||||
|
}
|
||||||
|
location /.well-known/acme-challenge {
|
||||||
|
root /var/www/letsencrypt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name your.server.tld;
|
||||||
|
ssl_certificate /etc/letsencrypt/live/your.server.tld/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/your.server.tld/privkey.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /var/www/lokinet-bootserv;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /bootstrap.signed {
|
||||||
|
include /etc/nginx/fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /usr/local/bin/lokinet-bootserv;
|
||||||
|
fastcgi_pass unix://tmp/cgi.sock;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
# set me to where the nodedb is for lokinet
|
||||||
|
#[nodedb]
|
||||||
|
#dir=/path/to/nodedb/
|
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
SRC = $(sort $(wildcard src/*.cpp))
|
||||||
|
OBJS = $(SRC:.cpp=.cpp.o)
|
||||||
|
|
||||||
|
CGI_EXE = lokinet-bootserv
|
||||||
|
|
||||||
|
CXX = clang++
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
build: $(CGI_EXE)
|
||||||
|
|
||||||
|
%.cpp.o: %.cpp
|
||||||
|
$(CXX) -g -std=c++17 -c -Wall -Werror -Wpedantic $^ -o $^.o
|
||||||
|
|
||||||
|
$(CGI_EXE): $(OBJS)
|
||||||
|
$(CXX) -o $(CGI_EXE) $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(CGI_EXE) $(OBJS)
|
@ -0,0 +1,35 @@
|
|||||||
|
# lokinet-bootserv
|
||||||
|
|
||||||
|
cgi executable for serving a random RC for bootstrap from a nodedb
|
||||||
|
|
||||||
|
## configuring
|
||||||
|
|
||||||
|
copy the example config (privileged)
|
||||||
|
|
||||||
|
# cp configs/lokinet-bootserv.ini /usr/local/etc/lokinet-bootserv.ini
|
||||||
|
|
||||||
|
edit config to have proper values,
|
||||||
|
specifically make sure the `[nodedb]` section has a `dir` value that points to a static copy of a healthy nodedb
|
||||||
|
|
||||||
|
## building
|
||||||
|
|
||||||
|
to build:
|
||||||
|
|
||||||
|
$ make
|
||||||
|
|
||||||
|
## installing (priviledged)
|
||||||
|
|
||||||
|
install cgi binary:
|
||||||
|
|
||||||
|
# cp lokinet-bootserv /usr/local/bin/lokinet-bootserv
|
||||||
|
|
||||||
|
set up with nginx cgi:
|
||||||
|
|
||||||
|
# cp configs/lokinet-bootserv-nginx.conf /etc/nginx/sites-available/lokinet-bootserv.conf
|
||||||
|
# ln -s /etc/nginx/sites-available/lokinet-bootserv.conf /etc/nginx/sites-enabled/
|
||||||
|
|
||||||
|
## maintainence
|
||||||
|
|
||||||
|
add the following to crontab
|
||||||
|
|
||||||
|
0 0 * * * /usr/local/bin/lokinet-bootserv --cron
|
@ -0,0 +1,171 @@
|
|||||||
|
#include "lokinet-cgi.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <list>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
CGIHandler::CGIHandler(std::ostream& o) : Handler(o)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CGIHandler::~CGIHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
CGIHandler::Exec(const Config& conf)
|
||||||
|
{
|
||||||
|
const char* e = getenv("REQUEST_METHOD");
|
||||||
|
if(e == nullptr)
|
||||||
|
return ReportError("$REQUEST_METHOD not set");
|
||||||
|
std::string_view method(e);
|
||||||
|
|
||||||
|
if(method != "GET")
|
||||||
|
{
|
||||||
|
out << "Content-Type: text/plain" << std::endl;
|
||||||
|
out << "Status: 405 Method Not Allowed" << std::endl << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fname;
|
||||||
|
if(!conf.VisitSection(
|
||||||
|
"nodedb", [&](const Config::Section_t& sect) -> bool {
|
||||||
|
auto itr = sect.find("dir");
|
||||||
|
if(itr == sect.end())
|
||||||
|
return false;
|
||||||
|
fname = PickRandomFileInDir(
|
||||||
|
std::string(itr->second.data(), itr->second.size()));
|
||||||
|
return true;
|
||||||
|
}))
|
||||||
|
|
||||||
|
return ReportError("bad values in nodedb section of config");
|
||||||
|
if(fname.empty())
|
||||||
|
{
|
||||||
|
// no files in nodedb
|
||||||
|
out << "Content-Type: text/plain" << std::endl;
|
||||||
|
out << "Status: 404 Not Found" << std::endl << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ServeFile(fname.c_str(), "application/octect-stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
CGIHandler::PickRandomFileInDir(std::string dirname) const
|
||||||
|
{
|
||||||
|
// collect files
|
||||||
|
std::list< std::string > files;
|
||||||
|
{
|
||||||
|
DIR* d = opendir(dirname.c_str());
|
||||||
|
if(d == nullptr)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
std::list< std::string > subdirs;
|
||||||
|
dirent* ent = nullptr;
|
||||||
|
while((ent = readdir(d)))
|
||||||
|
{
|
||||||
|
std::string_view f = ent->d_name;
|
||||||
|
if(f != "." && f != "..")
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << dirname;
|
||||||
|
ss << '/';
|
||||||
|
ss << f;
|
||||||
|
subdirs.emplace_back(ss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
for(const auto& subdir : subdirs)
|
||||||
|
{
|
||||||
|
d = opendir(subdir.c_str());
|
||||||
|
if(d)
|
||||||
|
{
|
||||||
|
while((ent = readdir(d)))
|
||||||
|
{
|
||||||
|
std::string_view f;
|
||||||
|
f = ent->d_name;
|
||||||
|
if(f != "." && f != ".."
|
||||||
|
&& f.find_last_of(".signed") != std::string_view::npos)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << subdir << "/" << f;
|
||||||
|
files.emplace_back(ss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t randint;
|
||||||
|
{
|
||||||
|
std::basic_ifstream< uint32_t > randf("/dev/urandom");
|
||||||
|
if(!randf.is_open())
|
||||||
|
return "";
|
||||||
|
randf.read(&randint, 1);
|
||||||
|
}
|
||||||
|
auto itr = files.begin();
|
||||||
|
if(files.size() > 1)
|
||||||
|
std::advance(itr, randint % files.size());
|
||||||
|
return *itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
CGIHandler::ServeFile(const char* fname, const char* contentType) const
|
||||||
|
{
|
||||||
|
std::ifstream f(fname);
|
||||||
|
if(f.is_open())
|
||||||
|
{
|
||||||
|
f.seekg(0, std::ios::end);
|
||||||
|
auto sz = f.tellg();
|
||||||
|
f.seekg(0, std::ios::beg);
|
||||||
|
if(sz)
|
||||||
|
{
|
||||||
|
out << "Content-Type: " << contentType << std::endl;
|
||||||
|
out << "Status: 200 OK" << std::endl;
|
||||||
|
out << "Content-Length: " << std::to_string(sz) << std::endl
|
||||||
|
<< std::endl;
|
||||||
|
char buf[512] = {0};
|
||||||
|
size_t r = 0;
|
||||||
|
while((r = f.readsome(buf, sizeof(buf))) > 0)
|
||||||
|
out.write(buf, r);
|
||||||
|
out << std::flush;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out << "Content-Type: text/plain" << std::endl;
|
||||||
|
out << "Status: 500 Internal Server Error" << std::endl << std::endl;
|
||||||
|
out << "could not serve '" << fname << "' as it is an empty file"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out << "Content-Type: text/plain" << std::endl;
|
||||||
|
out << "Status: 404 Not Found" << std::endl << std::endl;
|
||||||
|
out << "could not serve '" << fname
|
||||||
|
<< "' as it does not exist on the filesystem" << std::endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
CGIHandler::ReportError(const char* err)
|
||||||
|
{
|
||||||
|
out << "Content-Type: text/plain" << std::endl;
|
||||||
|
out << "Status: 500 Internal Server Error" << std::endl << std::endl;
|
||||||
|
out << err << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handler_ptr
|
||||||
|
NewCGIHandler(std::ostream& out)
|
||||||
|
{
|
||||||
|
return std::make_unique< CGIHandler >(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
@ -0,0 +1,43 @@
|
|||||||
|
#ifndef LOKINET_BOOTSERV_HANDLER_HPP
|
||||||
|
#define LOKINET_BOOTSERV_HANDLER_HPP
|
||||||
|
#include <iostream>
|
||||||
|
#include "lokinet-config.hpp"
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
struct Handler
|
||||||
|
{
|
||||||
|
Handler(std::ostream& o) : out(o){};
|
||||||
|
|
||||||
|
virtual ~Handler(){};
|
||||||
|
|
||||||
|
/// handle command
|
||||||
|
/// return exit code
|
||||||
|
virtual int
|
||||||
|
Exec(const Config& conf) = 0;
|
||||||
|
|
||||||
|
/// report an error to system however that is done
|
||||||
|
/// return exit code
|
||||||
|
virtual int
|
||||||
|
ReportError(const char* err) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::ostream& out;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Handler_ptr = std::unique_ptr< Handler >;
|
||||||
|
|
||||||
|
/// create cgi handler
|
||||||
|
Handler_ptr
|
||||||
|
NewCGIHandler(std::ostream& out);
|
||||||
|
|
||||||
|
/// create cron handler
|
||||||
|
Handler_ptr
|
||||||
|
NewCronHandler(std::ostream& out);
|
||||||
|
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef BOOTSERV_LOKINET_CRON_HPP
|
||||||
|
#define BOOTSERV_LOKINET_CRON_HPP
|
||||||
|
|
||||||
|
#include "handler.hpp"
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
struct CGIHandler final : public Handler
|
||||||
|
{
|
||||||
|
CGIHandler(std::ostream& o);
|
||||||
|
~CGIHandler();
|
||||||
|
|
||||||
|
int
|
||||||
|
Exec(const Config& conf) override;
|
||||||
|
|
||||||
|
int
|
||||||
|
ReportError(const char* err) override;
|
||||||
|
|
||||||
|
int
|
||||||
|
ServeFile(const char* fname, const char* mime) const;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
PickRandomFileInDir(std::string dirname) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,132 @@
|
|||||||
|
#include "lokinet-config.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <list>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
const char* Config::DefaultPath = "/usr/local/etc/lokinet-bootserv.ini";
|
||||||
|
|
||||||
|
bool
|
||||||
|
Config::LoadFile(const char* fname)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::ifstream f(fname);
|
||||||
|
if(!f.is_open())
|
||||||
|
return false;
|
||||||
|
f.seekg(0, std::ios::end);
|
||||||
|
m_Data.resize(f.tellg());
|
||||||
|
f.seekg(0, std::ios::beg);
|
||||||
|
if(m_Data.size() == 0)
|
||||||
|
return false;
|
||||||
|
f.read(m_Data.data(), m_Data.size());
|
||||||
|
}
|
||||||
|
return Parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::Clear()
|
||||||
|
{
|
||||||
|
m_Config.clear();
|
||||||
|
m_Data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Config::Parse()
|
||||||
|
{
|
||||||
|
std::list< String_t > lines;
|
||||||
|
{
|
||||||
|
auto itr = m_Data.begin();
|
||||||
|
// split into lines
|
||||||
|
while(itr != m_Data.end())
|
||||||
|
{
|
||||||
|
auto beg = itr;
|
||||||
|
while(itr != m_Data.end() && *itr != '\n' && *itr != '\r')
|
||||||
|
++itr;
|
||||||
|
lines.emplace_back(std::addressof(*beg), (itr - beg));
|
||||||
|
if(itr == m_Data.end())
|
||||||
|
break;
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String_t sectName;
|
||||||
|
|
||||||
|
for(const auto& line : lines)
|
||||||
|
{
|
||||||
|
String_t realLine;
|
||||||
|
auto comment = line.find_first_of(';');
|
||||||
|
if(comment == String_t::npos)
|
||||||
|
comment = line.find_first_of('#');
|
||||||
|
if(comment == String_t::npos)
|
||||||
|
realLine = line;
|
||||||
|
else
|
||||||
|
realLine = line.substr(0, comment);
|
||||||
|
// blank or commented line?
|
||||||
|
if(realLine.size() == 0)
|
||||||
|
continue;
|
||||||
|
// find delimiters
|
||||||
|
auto sectOpenPos = realLine.find_first_of('[');
|
||||||
|
auto sectClosPos = realLine.find_first_of(']');
|
||||||
|
auto kvDelim = realLine.find_first_of('=');
|
||||||
|
if(sectOpenPos != String_t::npos && sectClosPos != String_t::npos
|
||||||
|
&& kvDelim == String_t::npos)
|
||||||
|
{
|
||||||
|
// section header
|
||||||
|
|
||||||
|
// clamp whitespaces
|
||||||
|
++sectOpenPos;
|
||||||
|
while(std::isspace(realLine[sectOpenPos])
|
||||||
|
&& sectOpenPos != sectClosPos)
|
||||||
|
++sectOpenPos;
|
||||||
|
--sectClosPos;
|
||||||
|
while(std::isspace(realLine[sectClosPos])
|
||||||
|
&& sectClosPos != sectOpenPos)
|
||||||
|
--sectClosPos;
|
||||||
|
// set section name
|
||||||
|
sectName = realLine.substr(sectOpenPos, sectClosPos);
|
||||||
|
}
|
||||||
|
else if(kvDelim != String_t::npos)
|
||||||
|
{
|
||||||
|
// key value pair
|
||||||
|
String_t::size_type k_start = 0;
|
||||||
|
String_t::size_type k_end = kvDelim;
|
||||||
|
String_t::size_type v_start = kvDelim + 1;
|
||||||
|
String_t::size_type v_end = realLine.size() - 1;
|
||||||
|
// clamp whitespaces
|
||||||
|
while(std::isspace(realLine[k_start]) && k_start != kvDelim)
|
||||||
|
++k_start;
|
||||||
|
while(std::isspace(realLine[k_end]) && k_end != k_start)
|
||||||
|
--k_end;
|
||||||
|
while(std::isspace(realLine[v_start]) && v_start != v_end)
|
||||||
|
++v_start;
|
||||||
|
while(std::isspace(realLine[v_end]))
|
||||||
|
--v_end;
|
||||||
|
|
||||||
|
// sect.k = v
|
||||||
|
String_t k = realLine.substr(k_start, k_end);
|
||||||
|
String_t v = realLine.substr(v_start, v_end);
|
||||||
|
Section_t& sect = m_Config[sectName];
|
||||||
|
sect[k] = v;
|
||||||
|
}
|
||||||
|
else // malformed?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Config::VisitSection(
|
||||||
|
const char* name,
|
||||||
|
std::function< bool(const Section_t& sect) > visit) const
|
||||||
|
{
|
||||||
|
auto itr = m_Config.find(name);
|
||||||
|
if(itr == m_Config.end())
|
||||||
|
return false;
|
||||||
|
return visit(itr->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef LOKINET_BOOTSERV_CONFIG_HPP
|
||||||
|
#define LOKINET_BOOTSERV_CONFIG_HPP
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string_view>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
struct Config
|
||||||
|
{
|
||||||
|
using String_t = std::string_view;
|
||||||
|
using Section_t = std::unordered_map< String_t, String_t >;
|
||||||
|
using Config_impl_t = std::unordered_map< String_t, Section_t >;
|
||||||
|
|
||||||
|
static const char* DefaultPath;
|
||||||
|
|
||||||
|
/// clear config
|
||||||
|
void
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
/// load config file for bootserv
|
||||||
|
/// return true on success
|
||||||
|
/// return false on error
|
||||||
|
bool
|
||||||
|
LoadFile(const char* fname);
|
||||||
|
|
||||||
|
/// visit a section in config read only by name
|
||||||
|
/// return false if no section or value propagated from visitor
|
||||||
|
bool
|
||||||
|
VisitSection(const char* name,
|
||||||
|
std::function< bool(const Section_t&) > visit) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool
|
||||||
|
Parse();
|
||||||
|
|
||||||
|
std::vector< char > m_Data;
|
||||||
|
Config_impl_t m_Config;
|
||||||
|
};
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,37 @@
|
|||||||
|
#include "lokinet-cron.hpp"
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
CronHandler::CronHandler(std::ostream& o) : Handler(o)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CronHandler::~CronHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
CronHandler::Exec(const Config& conf)
|
||||||
|
{
|
||||||
|
// this runs the cron tasks
|
||||||
|
// TODO: implement me
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
CronHandler::ReportError(const char* err)
|
||||||
|
{
|
||||||
|
out << "error: " << err << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handler_ptr
|
||||||
|
NewCronHandler(std::ostream& out)
|
||||||
|
{
|
||||||
|
return std::make_unique< CronHandler >(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef BOOTSERV_LOKINET_CRON_HPP
|
||||||
|
#define BOOTSERV_LOKINET_CRON_HPP
|
||||||
|
|
||||||
|
#include "handler.hpp"
|
||||||
|
|
||||||
|
namespace lokinet
|
||||||
|
{
|
||||||
|
namespace bootserv
|
||||||
|
{
|
||||||
|
struct CronHandler final : public Handler
|
||||||
|
{
|
||||||
|
CronHandler(std::ostream& o);
|
||||||
|
~CronHandler();
|
||||||
|
|
||||||
|
int
|
||||||
|
Exec(const Config& conf) override;
|
||||||
|
|
||||||
|
int
|
||||||
|
ReportError(const char* err) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bootserv
|
||||||
|
} // namespace lokinet
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,60 @@
|
|||||||
|
#include "handler.hpp"
|
||||||
|
#include "lokinet-config.hpp"
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
static int
|
||||||
|
printhelp(const char* exe)
|
||||||
|
{
|
||||||
|
std::cout << "usage: " << exe << " [--cron] [--conf /path/to/alt/config.ini]"
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
bool RunCron = false;
|
||||||
|
|
||||||
|
const char* confFile = lokinet::bootserv::Config::DefaultPath;
|
||||||
|
lokinet::bootserv::Config config;
|
||||||
|
|
||||||
|
lokinet::bootserv::Handler_ptr handler;
|
||||||
|
|
||||||
|
option longopts[] = {{"cron", no_argument, 0, 'C'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"conf", required_argument, 0, 'c'},
|
||||||
|
{0, 0, 0, 0}};
|
||||||
|
|
||||||
|
int c = 0;
|
||||||
|
int index = 0;
|
||||||
|
while((c = getopt_long(argc, argv, "hCc:", longopts, &index)) != -1)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case 'h':
|
||||||
|
return printhelp(argv[0]);
|
||||||
|
case 'C':
|
||||||
|
RunCron = true;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
confFile = optarg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(RunCron)
|
||||||
|
handler = lokinet::bootserv::NewCronHandler(std::cout);
|
||||||
|
else
|
||||||
|
handler = lokinet::bootserv::NewCGIHandler(std::cout);
|
||||||
|
|
||||||
|
if(!config.LoadFile(confFile))
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "failed to load " << confFile;
|
||||||
|
return handler->ReportError(ss.str().c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return handler->Exec(config);
|
||||||
|
}
|
Loading…
Reference in New Issue