summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2023-10-20 05:20:11 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2023-10-20 05:20:11 -0500
commita9ccd9bc7e22573c5c327d3f75722ebd3cca6a82 (patch)
treee2a5bfcdcad071b5045586673b32f9849b8b6991
parent926aeb7fe8f50e5f1243b88a63d305c7d9f85371 (diff)
downloadhorizon-a9ccd9bc7e22573c5c327d3f75722ebd3cca6a82.tar.gz
horizon-a9ccd9bc7e22573c5c327d3f75722ebd3cca6a82.tar.bz2
horizon-a9ccd9bc7e22573c5c327d3f75722ebd3cca6a82.tar.xz
horizon-a9ccd9bc7e22573c5c327d3f75722ebd3cca6a82.zip
hscript: Allow svcenable to create new runlevels
This is going to be useful in general for some advanced use cases, but most importantly, it will allow us to define runlevels for live media. Ref: #346
-rw-r--r--devel/requirements/3b_runner.xml8
-rw-r--r--hscript/meta.cc12
-rw-r--r--hscript/script_e.cc19
3 files changed, 37 insertions, 2 deletions
diff --git a/devel/requirements/3b_runner.xml b/devel/requirements/3b_runner.xml
index a5080fe..b5e9848 100644
--- a/devel/requirements/3b_runner.xml
+++ b/devel/requirements/3b_runner.xml
@@ -820,6 +820,14 @@
<title>Runner.Execute.timezone.Missing</title>
<para>If the zoneinfo file for the specified timezone in the <literal>timezone</literal> key is missing in the target namespace, but present in the installation environment, the system shall copy the zoneinfo file from the installation environment to the <literal>/etc/localtime</literal> file in the target namespace.</para>
</formalpara>
+ <formalpara id="Runner.Execute.svcenable">
+ <title>Runner.Execute.svcenable</title>
+ <para>The system shall configure the service manager in the target namespace to start services as specified in any specified <literal>svcenable</literal> keys.</para>
+ </formalpara>
+ <formalpara id="Runner.Execute.svcenable.AddlRunlevels">
+ <title>Runner.Execute.svcenable.AddlRunlevels</title>
+ <para>If a runlevel not defined by the system service manager is specified in a <literal>svcenable</literal> key, it shall be interpreted to be a stacked runlevel atop the default runlevel and shall be configured as such in the target namespace.</para>
+ </formalpara>
<formalpara id="Runner.Execute.bootloader">
<title>Runner.Execute.bootloader</title>
<para>If a <literal>bootloader</literal> key is specified in the HorizonScript, the system shall perform the requested bootloader configuration.</para>
diff --git a/hscript/meta.cc b/hscript/meta.cc
index af72eaf..a12b874 100644
--- a/hscript/meta.cc
+++ b/hscript/meta.cc
@@ -652,12 +652,14 @@ bool SvcEnable::validate() const {
/* LCOV_EXCL_STOP */
bool SvcEnable::execute() const {
- const std::string target = script->targetDirectory() +
- "/etc/runlevels/" + _runlevel + "/" + _svc;
+ const std::string target_rl = script->targetDirectory() +
+ "/etc/runlevels/" + _runlevel;
+ const std::string target = target_rl + "/" + _svc;
const std::string initd = "/etc/init.d/" + _svc;
output_info(pos, "svcenable: enabling service " + _svc);
if(script->options().test(Simulate)) {
+ std::cout << "mkdir -p " << target_rl << std::endl;
std::cout << "ln -s " << initd << " " << target << std::endl;
return true;
}
@@ -667,6 +669,12 @@ bool SvcEnable::execute() const {
if(!fs::exists(script->targetDirectory() + initd, ec)) {
output_warning(pos, "svcenable: missing service", _svc);
}
+ fs::create_directory(target_rl, ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error(pos, "svcenable: could not create runlevel directory "
+ "/etc/runlevels/" + _runlevel, ec.message());
+ return false;
+ }
fs::create_symlink(initd, target, ec);
if(ec && ec.value() != EEXIST) {
diff --git a/hscript/script_e.cc b/hscript/script_e.cc
index 3cdc78f..8f0f4b4 100644
--- a/hscript/script_e.cc
+++ b/hscript/script_e.cc
@@ -677,6 +677,25 @@ bool Script::execute() const {
for(const auto &svc : internal->svcs_enable) {
EXECUTE_OR_FAIL("svcenable", svc)
}
+#ifdef HAS_INSTALL_ENV
+ if(!opts.test(Simulate)) {
+ const std::set<std::string> def_runlevels{
+ "boot", "default", "empty", "nonetwork", "shutdown", "sysinit"
+ };
+ const auto rl_dir{targ_etc + "/runlevels"};
+ for(auto &&file : fs::directory_iterator(rl_dir)) {
+ const auto runlevel{file.path().filename().string()};
+ if(def_runlevels.find(runlevel) == def_runlevels.end()) {
+ fs::create_symlink("../default",
+ rl_dir + "/" + runlevel + "/default", ec);
+ if(ec) {
+ output_error("internal", "could not stack runlevel " +
+ runlevel, ec.message());
+ }
+ }
+ }
+ }
+#endif
if(internal->boot) {
EXECUTE_OR_FAIL("bootloader", internal->boot)