aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-04-03 15:54:25 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-04-03 15:54:25 +0200
commit798a0aab2fbb5aed01bbd8419b65a03fb4602b3f (patch)
tree480c89c64c5113cc015934a82d03e0dc45ed5c67
parentc13366d910ebbdfc30c8b399ff29161747537d1c (diff)
Make agent simple systemd daemon, add worker stub
-rw-r--r--bbot/.gitignore1
-rw-r--r--bbot/agent.cxx75
-rw-r--r--bbot/bbot-agent@.service13
-rw-r--r--bbot/buildfile49
-rw-r--r--bbot/diagnostics84
-rw-r--r--bbot/diagnostics.cxx30
-rw-r--r--bbot/worker.cli42
-rw-r--r--bbot/worker.cxx103
8 files changed, 279 insertions, 118 deletions
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.h> // signal()
-#else
-# include <stdlib.h> // getenv(), _putenv()
-#endif
+#include <signal.h> // signal()
+#include <unistd.h> // sleep()
#include <iostream>
@@ -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 <bbot/types-parsers>" \
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<simple_prologue_base>;
- using location_prologue = butl::diag_prologue<location_prologue_base>;
+ using simple_prologue = butl::diag_prologue<simple_prologue_base>;
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 <typename L>
- 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 <typename F, typename L, typename C>
- location_prologue
- operator() (F&& f, L&& l, C&& c) const
- {
- return location_prologue (
- epilogue_,
- type_,
- name_,
- location (forward<F> (f), forward<L> (l), forward<C> (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<basic_mark_base>;
- 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<trace_mark_base>;
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<fail_end_base>;
- 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 <bbot/common.cli>;
+
+"\section=1"
+"\name=bbot-worker"
+"\summary=build bot worker"
+
+namespace bbot
+{
+ {
+ "<options> <file>",
+
+ "
+ \h|SYNOPSIS|
+
+ \cb{bbot-worker --help}\n
+ \cb{bbot-worker --version}\n
+ \c{\b{bbot-worker} [<options>] <toolchain>}
+
+ \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.h> // signal()
+#else
+# include <stdlib.h> // getenv(), _putenv()
+#endif
+
+#include <iostream>
+
+#include <butl/pager>
+
+#include <bbot/types>
+#include <bbot/utility>
+
+#include <bbot/diagnostics>
+#include <bbot/worker-options>
+
+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;
+}