From eaad2886ee1c2fff25f473e4ab933e53ef959beb Mon Sep 17 00:00:00 2001
From: "A. Wilcox" <AWilcox@Wilcox-Tech.com>
Date: Sat, 12 Oct 2019 09:48:49 -0500
Subject: hscript: Add hostname execution

---
 hscript/meta.cc   | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 hscript/script.cc | 10 +++++++++-
 2 files changed, 59 insertions(+), 4 deletions(-)

(limited to 'hscript')

diff --git a/hscript/meta.cc b/hscript/meta.cc
index 33a9326..6bf4587 100644
--- a/hscript/meta.cc
+++ b/hscript/meta.cc
@@ -10,7 +10,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+#include <fstream>
 #include <regex>
+#include <unistd.h>
 #include "meta.hh"
 #include "util/output.hh"
 
@@ -63,9 +65,54 @@ bool Hostname::validate(ScriptOptions) const {
     return !any_failure;
 }
 
-bool Hostname::execute(ScriptOptions) const {
-    /* Write the hostname to /etc/hostname in the target environment. */
-    return false;
+bool Hostname::execute(ScriptOptions opts) const {
+    /* Set the hostname of the target computer */
+    std::string actual;
+
+    if(this->_value.size() > 64) {
+        /* Linux has a nodename limit of 64 characters.
+         * That's fine, because we have a limit of 64 chars per segment.
+         * Assuming a dot is present, just chop at the first dot. */
+        std::string::size_type dot = this->_value.find_first_of('.');
+        if(dot == std::string::npos) {
+            output_error("installfile:" + std::to_string(this->lineno()),
+                         "hostname: nodename too long",
+                         "Linux requires nodename to be <= 64 characters.");
+            return false;
+        }
+        std::copy_n(this->_value.cbegin(), dot, actual.begin());
+    } else {
+        actual = this->_value;
+    }
+
+    /* Runner.Execute.hostname. */
+    if(opts.test(Simulate)) {
+        output_info("installfile:" + std::to_string(this->lineno()),
+                    "hostname: set hostname to '" + actual + "'");
+    } else {
+        if(sethostname(actual.c_str(), actual.size()) == -1) {
+            output_error("installfile:" + std::to_string(this->lineno()),
+                         "hostname: failed to set host name",
+                         std::string(strerror(errno)));
+            return false;
+        }
+    }
+
+    /* Runner.Execute.hostname.Write. */
+    if(opts.test(Simulate)) {
+        output_info("installfile:" + std::to_string(this->lineno()),
+                    "hostname: write '" + actual + "' to /etc/hostname");
+    } else {
+        std::ofstream hostname_f("/target/etc/hostname");
+        if(!hostname_f) {
+            output_error("installfile:" + std::to_string(this->lineno()),
+                         "hostname: could not open /etc/hostname for writing");
+            return false;
+        }
+        hostname_f << actual;
+    }
+
+    return true;
 }
 
 
diff --git a/hscript/script.cc b/hscript/script.cc
index a31fbc3..0583a3c 100644
--- a/hscript/script.cc
+++ b/hscript/script.cc
@@ -387,11 +387,19 @@ bool Script::execute() const {
     output_step_end("validate");
     if(!success) {
         /* Runner.Execute.Verify.Failure */
-        output_error("validator", "The HorizonScript failed validation.",
+        output_error("validator", "The HorizonScript failed validation",
                      "Check the output from the validator.");
         return false;
     }
 
+    output_step_start("metadata");
+    if(!this->internal->hostname->execute(opts) ||
+       !this->internal->rootpw->execute(opts)) {
+        output_error("metadata", "The HorizonScript failed to execute",
+                     "Check the log file for more details.");
+        return false;
+    }
+    output_step_end("metadata");
     return false;
 }
 
-- 
cgit v1.2.3-70-g09d2