From 798a0aab2fbb5aed01bbd8419b65a03fb4602b3f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 3 Apr 2017 15:54:25 +0200 Subject: Make agent simple systemd daemon, add worker stub --- bbot/.gitignore | 1 + bbot/agent.cxx | 75 +++++++++++++++++++++------------- bbot/bbot-agent@.service | 13 ++++++ bbot/buildfile | 49 ++++++++++++++++++---- bbot/diagnostics | 84 +++++++++----------------------------- bbot/diagnostics.cxx | 30 +++++++------- bbot/worker.cli | 42 +++++++++++++++++++ bbot/worker.cxx | 103 +++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 279 insertions(+), 118 deletions(-) create mode 100644 bbot/bbot-agent@.service create mode 100644 bbot/worker.cli create mode 100644 bbot/worker.cxx (limited to 'bbot') diff --git a/bbot/.gitignore b/bbot/.gitignore index 579654d..e31326e 100644 --- a/bbot/.gitignore +++ b/bbot/.gitignore @@ -1,3 +1,4 @@ *-options *-options.?xx bbot-agent +bbot-worker diff --git a/bbot/agent.cxx b/bbot/agent.cxx index a8c1b6e..26b0073 100644 --- a/bbot/agent.cxx +++ b/bbot/agent.cxx @@ -2,11 +2,8 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#ifndef _WIN32 -# include // signal() -#else -# include // getenv(), _putenv() -#endif +#include // signal() +#include // sleep() #include @@ -22,44 +19,57 @@ using namespace std; using namespace butl; using namespace bbot; +extern "C" void +handle_signal (int sig) +{ + switch (sig) + { + case SIGHUP: exit (3); // Unimplemented feature. + case SIGTERM: exit (0); + default: assert (false); + } +} + int main (int argc, char* argv[]) try { - // This is a little hack to make out baseutils for Windows work when called - // with absolute path. In a nutshell, MSYS2's exec*p() doesn't search in the - // parent's executable directory, only in PATH. And since we are running - // without a shell (that would read /etc/profile which sets PATH to some - // sensible values), we are only getting Win32 PATH values. And MSYS2 /bin - // is not one of them. So what we are going to do is add /bin at the end of - // PATH (which will be passed as is by the MSYS2 machinery). This will make - // MSYS2 search in /bin (where our baseutils live). And for everyone else - // this should be harmless since it is not a valid Win32 path. + // Map to systemd severity prefixes (see sd-daemon(3) for details). Note + // that here we assume we will never have location (like file name which + // would end up being before the prefix). // -#ifdef _WIN32 - { - string mp ("PATH="); - if (const char* p = getenv ("PATH")) - { - mp += p; - mp += ';'; - } - mp += "/bin"; - - _putenv (mp.c_str ()); - } -#endif + const char indent[] = "\xE2\x86\xB2\n"; // Right arrow followed by newline. + + trace_indent = + fail.indent_ = + error.indent_ = + warn.indent_ = + info.indent_ = + text.indent_ = indent; + + fail.type_ = "<3>"; + error.type_ = "<3>"; + warn.type_ = "<4>"; + info.type_ = "<6>"; + trace_type = "<7>"; + + tracer trace ("main"); // On POSIX ignore SIGPIPE which is signaled to a pipe-writing process if // the pipe reading end is closed. Note that by default this signal // terminates a process. Also note that there is no way to disable this // behavior on a file descriptor basis or for the write() function call. // -#ifndef _WIN32 if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) fail << "unable to ignore broken pipe (SIGPIPE) signal: " << system_error (errno, generic_category ()); // Sanitize. -#endif + + // Handle SIGHUP and SIGTERM. + // + if (signal (SIGHUP, &handle_signal) == SIG_ERR || + signal (SIGTERM, &handle_signal) == SIG_ERR) + fail << "unable to set signal handler: " + << system_error (errno, generic_category ()); // Sanitize. cli::argv_scanner scan (argc, argv, true); agent_options ops (scan); @@ -88,6 +98,13 @@ try // return p.wait () ? 0 : 1; } + + for (;;) + { + error << "sleeping" << + warn << "lightly"; + sleep (10); + } } catch (const failed&) { diff --git a/bbot/bbot-agent@.service b/bbot/bbot-agent@.service new file mode 100644 index 0000000..7336610 --- /dev/null +++ b/bbot/bbot-agent@.service @@ -0,0 +1,13 @@ +[Unit] +Description=bbot agent for %I +After=default.target + +[Service] +Type=simple +ExecStart=/usr/bin/bbot-agent %i +User=build +Group=build +WorkingDirectory=~ + +[Install] +WantedBy=default.target diff --git a/bbot/buildfile b/bbot/buildfile index bcd0b93..c2d5d05 100644 --- a/bbot/buildfile +++ b/bbot/buildfile @@ -2,17 +2,49 @@ # copyright : Copyright (c) 2014-2017 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file +# Systemd .service file. +# +# @@ Currently the executable path is hardcoded as /usr/bin/bbot-agent. To +# handle this properly would need to generate/pre-process it )and detect +# update for install). +# +define service: file +service{*}: extension = service +service{*}: install = lib/systemd/system/ +service{*}: install.mode = 644 + import libs = libbutl%lib{butl} import libs += libbbot%lib{bbot} -exe{bbot-agent}: \ -{ cxx}{ agent } {hxx ixx cxx}{ agent-options } \ - {hxx ixx cxx}{ common-options } \ -{hxx cxx}{ diagnostics } \ -{hxx }{ types } \ -{hxx cxx}{ types-parsers } \ -{hxx cxx}{ utility } \ -{hxx }{ version-impl } \ +# Agent is a systemd service. +# +# @@ Have to package on Linux. +# +if ($cxx.target.class == "linux") +{ + ./: exe{bbot-agent} service{'bbot-agent@'} + + exe{bbot-agent}: \ + { cxx}{ agent } {hxx ixx cxx}{ agent-options } \ + {hxx ixx cxx}{ common-options } \ + {hxx cxx}{ diagnostics } \ + {hxx }{ types } \ + {hxx cxx}{ types-parsers } \ + {hxx cxx}{ utility } \ + {hxx }{ version-impl } \ + $libs +} + +./: exe{bbot-worker} + +exe{bbot-worker}: \ +{ cxx}{ worker } {hxx ixx cxx}{ worker-options } \ + {hxx ixx cxx}{ common-options } \ +{hxx cxx}{ diagnostics } \ +{hxx }{ types } \ +{hxx cxx}{ types-parsers } \ +{hxx cxx}{ utility } \ +{hxx }{ version-impl } \ $libs # Generated options parser. @@ -21,6 +53,7 @@ if $cli.configured { cli.cxx{common-options}: cli{common} cli.cxx{agent-options}: cli{agent} + cli.cxx{worker-options}: cli{worker} cli.options += -I $src_root --include-with-brackets --include-prefix bbot \ --guard-prefix BBOT --cxx-prologue "#include " \ diff --git a/bbot/diagnostics b/bbot/diagnostics index c849aae..6699c45 100644 --- a/bbot/diagnostics +++ b/bbot/diagnostics @@ -61,97 +61,49 @@ namespace bbot const char* name_; }; - class location - { - public: - location () {} - location (string f, uint64_t l, uint64_t c) - : file (move (f)), line (l), column (c) {} - - string file; - uint64_t line; - uint64_t column; - }; - - struct location_prologue_base - { - location_prologue_base (const char* type, - const char* name, - const location& l) - : type_ (type), name_ (name), loc_ (l) {} - - void - operator() (const diag_record& r) const; - - private: - const char* type_; - const char* name_; - const location loc_; - }; - struct basic_mark_base { - using simple_prologue = butl::diag_prologue; - using location_prologue = butl::diag_prologue; + using simple_prologue = butl::diag_prologue; explicit basic_mark_base (const char* type, + const char* indent = "\n ", const char* name = nullptr, const void* data = nullptr, diag_epilogue* epilogue = nullptr) - : type_ (type), name_ (name), data_ (data), epilogue_ (epilogue) {} + : type_ (type), name_ (name), data_ (data), + indent_ (indent), epilogue_ (epilogue) {} simple_prologue operator() () const { - return simple_prologue (epilogue_, type_, name_); - } - - location_prologue - operator() (const location& l) const - { - return location_prologue (epilogue_, type_, name_, l); - } - - template - location_prologue - operator() (const L& l) const - { - return location_prologue ( - epilogue_, type_, name_, get_location (l, data_)); + return simple_prologue (indent_, epilogue_, type_, name_); } - template - location_prologue - operator() (F&& f, L&& l, C&& c) const - { - return location_prologue ( - epilogue_, - type_, - name_, - location (forward (f), forward (l), forward (c))); - } - - protected: + public: const char* type_; const char* name_; const void* data_; + + const char* indent_; diag_epilogue* const epilogue_; }; using basic_mark = butl::diag_mark; - extern const basic_mark error; - extern const basic_mark warn; - extern const basic_mark info; - extern const basic_mark text; + extern basic_mark error; + extern basic_mark warn; + extern basic_mark info; + extern basic_mark text; // trace // + extern const char* trace_type; + extern const char* trace_indent; + struct trace_mark_base: basic_mark_base { explicit - trace_mark_base (const char* name, const void* data = nullptr) - : basic_mark_base ("trace", name, data) {} + trace_mark_base (const char* name, const void* data = nullptr); }; using trace_mark = butl::diag_mark; using tracer = trace_mark; @@ -162,8 +114,10 @@ namespace bbot { explicit fail_mark_base (const char* type, + const char* indent = "\n ", const void* data = nullptr) : basic_mark_base (type, + indent, nullptr, data, [](const diag_record& r) @@ -189,7 +143,7 @@ namespace bbot }; using fail_end = butl::diag_noreturn_end; - extern const fail_mark fail; + extern fail_mark fail; extern const fail_end endf; } diff --git a/bbot/diagnostics.cxx b/bbot/diagnostics.cxx index 1aa58f0..5c356f5 100644 --- a/bbot/diagnostics.cxx +++ b/bbot/diagnostics.cxx @@ -20,28 +20,26 @@ namespace bbot operator() (const diag_record& r) const { if (type_ != nullptr) - r << type_ << ": "; + r << type_; if (name_ != nullptr) r << name_ << ": "; } - void location_prologue_base:: - operator() (const diag_record& r) const - { - r << loc_.file << ':' << loc_.line << ':' << loc_.column << ": "; - - if (type_ != nullptr) - r << type_ << ": "; + const char* trace_type = "trace: "; + const char* trace_indent = "\n "; - if (name_ != nullptr) - r << name_ << ": "; + trace_mark_base:: + trace_mark_base (const char* name, const void* data) + : basic_mark_base (trace_type, trace_indent, name, data) + { } - const basic_mark error ("error"); - const basic_mark warn ("warning"); - const basic_mark info ("info"); - const basic_mark text (nullptr); - const fail_mark fail ("error"); - const fail_end endf; + basic_mark error ("error: "); + basic_mark warn ("warning: "); + basic_mark info ("info: "); + basic_mark text (nullptr); + fail_mark fail ("error: "); + + const fail_end endf; } diff --git a/bbot/worker.cli b/bbot/worker.cli new file mode 100644 index 0000000..7457790 --- /dev/null +++ b/bbot/worker.cli @@ -0,0 +1,42 @@ +// file : bbot/worker.cli +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +include ; + +"\section=1" +"\name=bbot-worker" +"\summary=build bot worker" + +namespace bbot +{ + { + " ", + + " + \h|SYNOPSIS| + + \cb{bbot-worker --help}\n + \cb{bbot-worker --version}\n + \c{\b{bbot-worker} [] } + + \h|DESCRIPTION| + + \cb{bbot-worker} @@ TODO. + " + } + + class worker_options + { + "\h|OPTIONS|" + + bool --help {"Print usage information and exit."} + bool --version {"Print version and exit."} + }; + + " + \h|EXIT STATUS| + + Non-zero exit status is returned in case of an error. + " +} diff --git a/bbot/worker.cxx b/bbot/worker.cxx new file mode 100644 index 0000000..a1d81f5 --- /dev/null +++ b/bbot/worker.cxx @@ -0,0 +1,103 @@ +// file : bbot/worker.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef _WIN32 +# include // signal() +#else +# include // getenv(), _putenv() +#endif + +#include + +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace butl; +using namespace bbot; + +int +main (int argc, char* argv[]) +try +{ + // This is a little hack to make out baseutils for Windows work when called + // with absolute path. In a nutshell, MSYS2's exec*p() doesn't search in the + // parent's executable directory, only in PATH. And since we are running + // without a shell (that would read /etc/profile which sets PATH to some + // sensible values), we are only getting Win32 PATH values. And MSYS2 /bin + // is not one of them. So what we are going to do is add /bin at the end of + // PATH (which will be passed as is by the MSYS2 machinery). This will make + // MSYS2 search in /bin (where our baseutils live). And for everyone else + // this should be harmless since it is not a valid Win32 path. + // +#ifdef _WIN32 + { + string mp ("PATH="); + if (const char* p = getenv ("PATH")) + { + mp += p; + mp += ';'; + } + mp += "/bin"; + + _putenv (mp.c_str ()); + } +#endif + + // On POSIX ignore SIGPIPE which is signaled to a pipe-writing process if + // the pipe reading end is closed. Note that by default this signal + // terminates a process. Also note that there is no way to disable this + // behavior on a file descriptor basis or for the write() function call. + // +#ifndef _WIN32 + if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) + fail << "unable to ignore broken pipe (SIGPIPE) signal: " + << system_error (errno, generic_category ()); // Sanitize. +#endif + + cli::argv_scanner scan (argc, argv, true); + worker_options ops (scan); + + // Version. + // + if (ops.version ()) + { + cout << "bbot-worker " << BBOT_VERSION_STR << endl + << "libbbot " << LIBBBOT_VERSION_STR << endl + << "libbutl " << LIBBUTL_VERSION_STR << endl + << "Copyright (c) 2014-2017 Code Synthesis Ltd" << endl + << "MIT; see accompanying LICENSE file" << endl; + + return 0; + } + + // Help. + // + if (ops.help ()) + { + pager p ("bbot-worker help", false); + print_bbot_worker_usage (p.stream ()); + + // If the pager failed, assume it has issued some diagnostics. + // + return p.wait () ? 0 : 1; + } + + warn << "starting up" << + info << "lightly"; +} +catch (const failed&) +{ + return 1; // Diagnostics has already been issued. +} +catch (const cli::exception& e) +{ + error << e; + return 1; +} -- cgit v1.1