/* * script_i.hh - Implementation of internal doodads for the Script class * libhscript, the HorizonScript library for * Project Horizon * * Copyright (c) 2019-2020 Adélie Linux and contributors. All rights reserved. * This code is licensed under the AGPL 3.0 license, as noted in the * LICENSE-code file in the root directory of this repository. * * SPDX-License-Identifier: AGPL-3.0-only */ #ifndef HSCRIPT_SCRIPT_INTERNAL_HH_ #define HSCRIPT_SCRIPT_INTERNAL_HH_ #include #include #include #include #include #include "script_l.hh" #include "disk.hh" #include "meta.hh" #include "network.hh" #include "user.hh" using namespace Horizon::Keys; namespace Horizon { /*! Describes a user account. */ struct UserDetail { std::unique_ptr name; std::unique_ptr alias; std::unique_ptr passphrase; std::unique_ptr icon; std::vector< std::unique_ptr > groups; }; struct Script::ScriptPrivate { /*! Determines the target directory (usually /target) */ std::string target; /*! Determines whether or not to enable networking. */ std::unique_ptr network; /*! Determines the network configuration system to use. */ std::unique_ptr netconfig; /*! The target system's hostname. */ std::unique_ptr hostname; /*! The packages to install to the target system. */ std::set packages; /*! The root shadow line. */ std::unique_ptr rootpw; /*! The system CPU architecture. */ std::unique_ptr arch; /*! The system language. */ std::unique_ptr lang; /*! The system keymap. */ std::unique_ptr keymap; /*! The system timezone. */ std::unique_ptr tzone; /*! The version of Adélie to install. */ std::unique_ptr version; /*! The desired bootloader configuration. */ std::unique_ptr boot; /*! The desired root shell. */ std::unique_ptr root_shell; /*! Network addressing configuration */ std::vector< std::unique_ptr > addresses; /*! Network nameserver resolver addresses */ std::vector< std::unique_ptr > nses; /*! Wireless networking configuration */ std::vector< std::unique_ptr > ssids; /*! PPPoE configuration */ std::vector< std::unique_ptr > pppoes; /*! APK repositories */ std::vector< std::unique_ptr > repos; /*! APK repository keys */ std::vector< std::unique_ptr > repo_keys; /*! Services to enable */ std::vector< std::unique_ptr > svcs_enable; /*! User account information */ std::map< std::string, std::unique_ptr > accounts; /*! Disk identification keys */ std::vector< std::unique_ptr > diskids; /*! Disklabel configuration keys */ std::vector< std::unique_ptr > disklabels; /*! Partition creation keys */ std::vector< std::unique_ptr > partitions; /*! LVM physical volume keys */ std::vector< std::unique_ptr > lvm_pvs; /*! LVM volume group keys */ std::vector< std::unique_ptr > lvm_vgs; /*! LVM logical volume keys */ std::vector< std::unique_ptr > lvm_lvs; /*! LUKS creation keys */ std::vector< std::unique_ptr > luks; /*! Filesystem creation keys */ std::vector< std::unique_ptr > fses; /*! Target system's mountpoints. */ std::vector< std::unique_ptr > mounts; #ifdef NON_LIBRE_FIRMWARE std::unique_ptr firmware; #endif /*! Store +key_obj+ representing the key +key_name+. * @param key_name The name of the key that is being stored. * @param obj The Key object associated with the key. * @param pos The on-disk script position of the key. * @param errors Output parameter: if given, incremented on error. * @param warnings Output parameter: if given, incremented on warning. * @param opts Script parsing options. */ bool store_key(const std::string &key_name, Key *obj, const ScriptLocation &pos, int *errors, int *warnings, const ScriptOptions &opts); #define DUPLICATE_ERROR(OBJ, KEY, OLD_VAL) \ if(pos.inherited) return true;\ std::string err_str("previous value was ");\ err_str += OLD_VAL;\ err_str += " at " + OBJ->where().name;\ err_str += ":" + std::to_string(OBJ->where().line);\ if(errors) *errors += 1;\ output_error(pos, "duplicate value for key '" + std::string(KEY) + "'",\ err_str); bool store_network(Key* obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(network) { DUPLICATE_ERROR(network, "network", network->test() ? "true" : "false") return false; } std::unique_ptr net(dynamic_cast(obj)); network = std::move(net); return true; } bool store_netconfig(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(netconfig) { DUPLICATE_ERROR(netconfig, "netconfigtype", netconfig->type()); return false; } std::unique_ptr nc(dynamic_cast(obj)); netconfig = std::move(nc); return true; } bool store_pppoe(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { PPPoE *ppp = dynamic_cast(obj); for(const auto &ppplink : pppoes) { if(ppplink->iface() == ppp->iface()) { DUPLICATE_ERROR(ppplink, "pppoe", ppplink->iface()); return false; } } std::unique_ptr uppp(ppp); pppoes.push_back(std::move(uppp)); return true; } bool store_hostname(Key* obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(hostname) { DUPLICATE_ERROR(hostname, "hostname", hostname->value()) return false; } std::unique_ptr name(dynamic_cast(obj)); hostname = std::move(name); return true; } bool store_pkginstall(Key* obj, const ScriptLocation &pos, int *, int *warnings, const ScriptOptions &opts) { PkgInstall *install = dynamic_cast(obj); for(auto &pkg : install->packages()) { if(opts.test(StrictMode) && packages.find(pkg) != packages.end()) { if(warnings) *warnings += 1; output_warning(pos, "pkginstall: package '" + pkg + "' has already been specified"); continue; } packages.insert(pkg); } delete install; return true; } bool store_arch(Key* obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(arch) { DUPLICATE_ERROR(arch, "arch", arch->value()) return false; } std::unique_ptr a(dynamic_cast(obj)); arch = std::move(a); return true; } bool store_rootpw(Key* obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(rootpw) { DUPLICATE_ERROR(rootpw, "rootpw", "an encrypted passphrase") return false; } std::unique_ptr r(dynamic_cast(obj)); rootpw = std::move(r); return true; } bool store_firmware(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { std::unique_ptr f(dynamic_cast(obj)); #ifdef NON_LIBRE_FIRMWARE if(firmware) { DUPLICATE_ERROR(firmware, "firmware", (firmware->test()) ? "true" : "false") return false; } firmware = std::move(f); return true; #else assert(!f->test()); return true; #endif } bool store_lang(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(lang) { DUPLICATE_ERROR(lang, "language", lang->value()) return false; } std::unique_ptr l(dynamic_cast(obj)); lang = std::move(l); return true; } bool store_keymap(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(keymap) { DUPLICATE_ERROR(keymap, "keymap", keymap->value()) return false; } std::unique_ptr k(dynamic_cast(obj)); keymap = std::move(k); packages.insert("kbd-keymaps"); return true; } bool store_timezone(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(tzone) { DUPLICATE_ERROR(tzone, "timezone", tzone->value()) return false; } std::unique_ptr t(dynamic_cast(obj)); tzone = std::move(t); return true; } bool store_svcenable(Key *obj, const ScriptLocation &pos, int *, int *warn, const ScriptOptions &) { std::unique_ptr svc(dynamic_cast(obj)); for(const auto &s : svcs_enable) { if(s->service() == svc->service()) { if(warn) *warn += 1; output_warning(pos, "svcenable: service already enabled", s->service()); return true; } } svcs_enable.push_back(std::move(svc)); return true; } bool store_version(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(version) { DUPLICATE_ERROR(version, "version", version->value()) return false; } std::unique_ptr v(dynamic_cast(obj)); version = std::move(v); return true; } bool store_rootshell(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(root_shell) { DUPLICATE_ERROR(root_shell, "rootshell", root_shell->value()) return false; } std::unique_ptr r(dynamic_cast(obj)); root_shell = std::move(r); return true; } bool store_bootloader(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(boot) { DUPLICATE_ERROR(boot, "bootloader", boot->bootloader()) return false; } std::unique_ptr b(dynamic_cast(obj)); boot = std::move(b); return true; } bool store_username(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(accounts.size() >= 255) { if(errors) *errors += 1; output_error(pos, "username: too many users", "you may only specify 255 users"); return false; } std::unique_ptr name(dynamic_cast(obj)); if(accounts.find(name->value()) != accounts.end()) { DUPLICATE_ERROR((*accounts.find(name->value())).second->name, "username", "assigned") return false; } std::unique_ptr detail(new UserDetail); detail->name = std::move(name); accounts.insert(std::make_pair(detail->name->value(), std::move(detail))); return true; } #define GET_USER_DETAIL(OBJ, KEY) \ if(accounts.find(OBJ->username()) == accounts.end()) {\ if(errors) *errors += 1;\ output_error(pos, std::string(KEY) + ": account name " +\ OBJ->username() + " is unknown");\ return false;\ }\ UserDetail *detail = (*accounts.find(OBJ->username())).second.get(); bool store_useralias(Key* obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { std::unique_ptr alias(dynamic_cast(obj)); GET_USER_DETAIL(alias, "useralias") /* REQ: Runner.Validate.useralias.Unique */ if(detail->alias) { DUPLICATE_ERROR(detail->alias, "useralias", detail->alias->alias()) return false; } detail->alias = std::move(alias); return true; } bool store_userpw(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { std::unique_ptr pw(dynamic_cast(obj)); GET_USER_DETAIL(pw, "userpw") /* REQ: Runner.Validate.userpw.Unique */ if(detail->passphrase) { DUPLICATE_ERROR(detail->passphrase, "userpw", "an encrypted passphrase") return false; } detail->passphrase = std::move(pw); return true; } bool store_usericon(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { std::unique_ptr icon(dynamic_cast(obj)); GET_USER_DETAIL(icon, "usericon") /* REQ: Runner.Validate.usericon.Unique */ if(detail->icon) { DUPLICATE_ERROR(detail->icon, "usericon", detail->icon->icon()) return false; } detail->icon = std::move(icon); return true; } bool store_usergroups(Key* obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { std::unique_ptr grp(dynamic_cast(obj)); GET_USER_DETAIL(grp, "usergroups") detail->groups.push_back(std::move(grp)); return true; } #undef GET_USER_DETAIL #undef DUPLICATE_ERROR }; } #endif /* !HSCRIPT_SCRIPT_INTERNAL_HH_ */