aboutsummaryrefslogtreecommitdiff
path: root/bbot/agent.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bbot/agent.cxx')
-rw-r--r--bbot/agent.cxx108
1 files changed, 97 insertions, 11 deletions
diff --git a/bbot/agent.cxx b/bbot/agent.cxx
index e608af2..2489ce4 100644
--- a/bbot/agent.cxx
+++ b/bbot/agent.cxx
@@ -4,9 +4,16 @@
#include <bbot/agent>
+#include <pwd.h> // getpwuid()
#include <limits.h> // PATH_MAX
#include <signal.h> // signal()
-#include <unistd.h> // sleep(), realink()
+#include <unistd.h> // sleep(), realink(), getuid()
+
+#include <net/if.h> // ifreq
+#include <netinet/in.h> // sockaddr_in
+#include <arpa/inet.h> // inet_ntop()
+#include <sys/ioctl.h>
+#include <sys/socket.h>
#include <iostream>
@@ -19,23 +26,57 @@
#include <bbot/utility>
#include <bbot/diagnostics>
+#include <bbot/tftp>
#include <bbot/machine>
#include <bbot/bootstrap-manifest>
+using namespace std;
+using namespace butl;
+using namespace bbot;
+
namespace bbot
{
agent_options ops;
- const string bs_prot ("1"); // Bootstrap protocol version.
+ const string bs_prot ("1");
- string tc_name; // Toolchain name.
- string tc_num; // Toolchain number.
- string tc_id; // Toolchain id.
-}
+ string tc_name;
+ string tc_num;
+ string tc_id;
-using namespace std;
-using namespace butl;
-using namespace bbot;
+ uid_t uid;
+ string uname;
+
+ // Note: Linux-specific implementation.
+ //
+ string
+ iface_addr (const string& i)
+ {
+ if (i.size () >= IFNAMSIZ)
+ throw invalid_argument ("interface nama too long");
+
+ auto_fd fd (socket (AF_INET, SOCK_DGRAM, 0));
+
+ if (fd.get () == -1)
+ throw_system_error (errno);
+
+ ifreq ifr;
+ ifr.ifr_addr.sa_family = AF_INET;
+ strcpy (ifr.ifr_name, i.c_str ());
+
+ if (ioctl (fd.get (), SIOCGIFADDR, &ifr) == -1)
+ throw_system_error (errno);
+
+ char buf[3 * 4 + 3 + 1]; // IPv4 address.
+ if (inet_ntop (AF_INET,
+ &reinterpret_cast<sockaddr_in*> (&ifr.ifr_addr)->sin_addr,
+ buf,
+ sizeof (buf)) == nullptr)
+ throw_system_error (errno);
+
+ return buf;
+ }
+}
// The btrfs tool likes to print informational messages, like "Created
// snapshot such and such". Luckily, it writes them to stdout while proper
@@ -65,6 +106,8 @@ bootstrap_machine (const dir_path& md,
const machine_manifest& mm,
optional<bootstrapped_machine_manifest> obmm)
{
+ tracer trace ("bootstrap_machine");
+
bootstrapped_machine_manifest r {
mm,
toolchain_manifest {tc_id},
@@ -83,15 +126,51 @@ bootstrap_machine (const dir_path& md,
r.machine.mac = "de:ad:be:ef:de:ad";
}
else
+ try
{
+ string br ("br1"); // Use private bridge for now.
+
+ // Start the TFTP server (server chroot is /build/tftp). Map:
+ //
+ // GET requests to /build/tftp/toolchain/<name>/*
+ // PUT requests to /build/tftp/bootstrap/<name>/*
+ //
+ auto_rmdir arm (dir_path ("/build/tftp/bootstrap/" + tc_name));
+ try_mkdir_p (arm.path ());
+
+ tftp_server tftpd ("Gr ^/?(.+)$ /toolchain/" + tc_name + "/\\1\n" +
+ "Pr ^/?(.+)$ /bootstrap/" + tc_name + "/\\1\n");
+
+ l2 ([&]{trace << "tftp server on port " << tftpd.port ();});
+
+ // Start the machine.
+ //
unique_ptr<machine> m (
start_machine (md,
mm,
- obmm ? obmm->machine.mac : nullopt));
+ obmm ? obmm->machine.mac : nullopt,
+ br,
+ tftpd.port ()));
r.machine.mac = m->mac;
- sleep (10);
+ // The first request should be the toolchain download. Wait for up to 60
+ // seconds for that to arrive. In a sense we use it as an indication that
+ // the machine has booted and the bootstrap process has started.
+ //
+ size_t timeout (60);
+ if (tftpd.serve (timeout))
+ {
+ l2 ([&]{trace << "received first request in " << 60 - timeout << "s";});
+ }
+ else
+ {
+ // @@ What should be do here? Non-fatal? Mark the machine as failed?
+ //
+ error << "bootstrap timeout during first request for machine " << md;
+ m->forcedown ();
+ throw failed ();
+ }
if (!m->shutdown ())
{
@@ -100,6 +179,10 @@ bootstrap_machine (const dir_path& md,
throw failed ();
}
}
+ catch (const system_error& e)
+ {
+ fail << "tftp server error: " << e;
+ }
serialize_manifest (r, md / "manifest", "bootstrapped machine");
return r;
@@ -389,6 +472,9 @@ try
verb = ops.verbose ();
+ uid = getuid ();
+ uname = getpwuid (uid)->pw_name;
+
if (ops.systemd_daemon ())
{
// Map to systemd severity prefixes (see sd-daemon(3) for details). Note