diff options
Diffstat (limited to 'hscript/script.cc')
-rw-r--r-- | hscript/script.cc | 1338 |
1 files changed, 75 insertions, 1263 deletions
diff --git a/hscript/script.cc b/hscript/script.cc index 4ed52b7..fcb7035 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -11,18 +11,15 @@ */ #include <algorithm> -#include <assert.h> #include "util/filesystem.hh" #include <fstream> #include <iostream> #include <map> -#ifdef HAS_INSTALL_ENV -# include <parted/parted.h> -#endif /* HAS_INSTALL_ENV */ #include <set> #include <sstream> #include "script.hh" +#include "script_i.hh" #include "disk.hh" #include "meta.hh" #include "network.hh" @@ -74,363 +71,95 @@ const std::map<std::string, key_parse_fn> valid_keys = { namespace Horizon { -/*! Describes a user account. */ -struct UserDetail { - std::unique_ptr<Username> name; - std::unique_ptr<UserAlias> alias; - std::unique_ptr<UserPassphrase> passphrase; - std::unique_ptr<UserIcon> icon; - std::vector< std::unique_ptr<UserGroups> > groups; -}; - -struct Script::ScriptPrivate { - /*! Determines whether or not to enable networking. */ - std::unique_ptr<Network> network; - /*! The target system's hostname. */ - std::unique_ptr<Hostname> hostname; - /*! The packages to install to the target system. */ - std::set<std::string> packages; - /*! The root shadow line. */ - std::unique_ptr<RootPassphrase> rootpw; - /*! The system language. */ - std::unique_ptr<Language> lang; - /*! The system keymap. */ - std::unique_ptr<Keymap> keymap; - /*! The system timezone. */ - std::unique_ptr<Timezone> tzone; - - /*! Network addressing configuration */ - std::vector< std::unique_ptr<NetAddress> > addresses; - /*! Network nameserver resolver addresses */ - std::vector< std::unique_ptr<Nameserver> > nses; - /*! Wireless networking configuration */ - std::vector< std::unique_ptr<NetSSID> > ssids; - - /*! APK repositories */ - std::vector< std::unique_ptr<Repository> > repos; - /*! APK repository keys */ - std::vector< std::unique_ptr<SigningKey> > repo_keys; - - /*! User account information */ - std::map< std::string, std::unique_ptr<UserDetail> > accounts; - - /*! Disk identification keys */ - std::vector< std::unique_ptr<DiskId> > diskids; - /*! Disklabel configuration keys */ - std::vector< std::unique_ptr<DiskLabel> > disklabels; - /*! Partition creation keys */ - std::vector< std::unique_ptr<Partition> > partitions; - /*! LVM physical volume keys */ - std::vector< std::unique_ptr<LVMPhysical> > lvm_pvs; - /*! LVM volume group keys */ - std::vector< std::unique_ptr<LVMGroup> > lvm_vgs; - /*! LVM logical volume keys */ - std::vector< std::unique_ptr<LVMVolume> > lvm_lvs; - /*! LUKS creation keys */ - std::vector< std::unique_ptr<Encrypt> > luks; - /*! Filesystem creation keys */ - std::vector< std::unique_ptr<Filesystem> > fses; - /*! Target system's mountpoints. */ - std::vector< std::unique_ptr<Mount> > mounts; - -#ifdef NON_LIBRE_FIRMWARE - std::unique_ptr<Firmware> 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 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, Keys::Key *obj, int lineno, - int *errors, int *warnings, const ScriptOptions &opts) { - if(key_name == "network") { - return store_network(obj, lineno, errors, warnings, opts); - } else if(key_name == "netaddress") { - std::unique_ptr<NetAddress> addr(dynamic_cast<NetAddress *>(obj)); - this->addresses.push_back(std::move(addr)); - return true; - } else if(key_name == "nameserver") { - std::unique_ptr<Nameserver> ns(dynamic_cast<Nameserver *>(obj)); - this->nses.push_back(std::move(ns)); - return true; - } else if(key_name == "netssid") { - std::unique_ptr<NetSSID> ssid(dynamic_cast<NetSSID *>(obj)); - this->ssids.push_back(std::move(ssid)); - return true; - } else if(key_name == "hostname") { - return store_hostname(obj, lineno, errors, warnings, opts); - } else if(key_name == "pkginstall") { - return store_pkginstall(obj, lineno, errors, warnings, opts); - } else if(key_name == "rootpw") { - return store_rootpw(obj, lineno, errors, warnings, opts); - } else if(key_name == "language") { - return store_lang(obj, lineno, errors, warnings, opts); - } else if(key_name == "keymap") { - return store_keymap(obj, lineno, errors, warnings, opts); - } else if(key_name == "firmware") { - return store_firmware(obj, lineno, errors, warnings, opts); - } else if(key_name == "timezone") { - return store_timezone(obj, lineno, errors, warnings, opts); - } else if(key_name == "repository") { - std::unique_ptr<Repository> repo(dynamic_cast<Repository *>(obj)); - this->repos.push_back(std::move(repo)); - return true; - } else if(key_name == "signingkey") { - std::unique_ptr<SigningKey> key(dynamic_cast<SigningKey *>(obj)); - this->repo_keys.push_back(std::move(key)); - return true; - } else if(key_name == "username") { - return store_username(obj, lineno, errors, warnings, opts); - } else if(key_name == "useralias") { - return store_useralias(obj, lineno, errors, warnings, opts); - } else if(key_name == "userpw") { - return store_userpw(obj, lineno, errors, warnings, opts); - } else if(key_name == "usericon") { - return store_usericon(obj, lineno, errors, warnings, opts); - } else if(key_name == "usergroups") { - return store_usergroups(obj, lineno, errors, warnings, opts); - } else if(key_name == "diskid") { - std::unique_ptr<DiskId> diskid(dynamic_cast<DiskId *>(obj)); - this->diskids.push_back(std::move(diskid)); - return true; - } else if(key_name == "disklabel") { - std::unique_ptr<DiskLabel> l(dynamic_cast<DiskLabel *>(obj)); - this->disklabels.push_back(std::move(l)); - return true; - } else if(key_name == "partition") { - std::unique_ptr<Partition> p(dynamic_cast<Partition *>(obj)); - this->partitions.push_back(std::move(p)); - return true; - } else if(key_name == "lvm_pv") { - std::unique_ptr<LVMPhysical> pv(dynamic_cast<LVMPhysical *>(obj)); - this->lvm_pvs.push_back(std::move(pv)); - return true; - } else if(key_name == "lvm_vg") { - std::unique_ptr<LVMGroup> vg(dynamic_cast<LVMGroup *>(obj)); - this->lvm_vgs.push_back(std::move(vg)); - return true; - } else if(key_name == "lvm_lv") { - std::unique_ptr<LVMVolume> lv(dynamic_cast<LVMVolume *>(obj)); - this->lvm_lvs.push_back(std::move(lv)); - return true; - } else if(key_name == "encrypt") { - std::unique_ptr<Encrypt> e(dynamic_cast<Encrypt *>(obj)); - this->luks.push_back(std::move(e)); - return true; - } else if(key_name == "fs") { - std::unique_ptr<Filesystem> fs(dynamic_cast<Filesystem *>(obj)); - this->fses.push_back(std::move(fs)); - return true; - } else if(key_name == "mount") { - std::unique_ptr<Mount> mount(dynamic_cast<Mount *>(obj)); - this->mounts.push_back(std::move(mount)); - return true; - } else { - return false; - } - } - -#define DUPLICATE_ERROR(OBJ, KEY, OLD_VAL) \ - std::string err_str("previous value was ");\ - err_str += OLD_VAL;\ - err_str += " at installfile:" + std::to_string(OBJ->lineno());\ - if(errors) *errors += 1;\ - output_error("installfile:" + std::to_string(lineno),\ - "duplicate value for key '" + std::string(KEY) + "'",\ - err_str); - - bool store_network(Keys::Key* obj, int lineno, int *errors, int *, - ScriptOptions) { - if(this->network) { - DUPLICATE_ERROR(this->network, "network", - this->network->test() ? "true" : "false") - return false; - } - std::unique_ptr<Network> net(dynamic_cast<Network *>(obj)); - this->network = std::move(net); +bool Script::ScriptPrivate::store_key(const std::string &key_name, Key *obj, + int lineno, int *errors, int *warnings, + const ScriptOptions &opts) { + if(key_name == "network") { + return store_network(obj, lineno, errors, warnings, opts); + } else if(key_name == "netaddress") { + std::unique_ptr<NetAddress> addr(dynamic_cast<NetAddress *>(obj)); + this->addresses.push_back(std::move(addr)); return true; - } - - bool store_hostname(Keys::Key* obj, int lineno, int *errors, int *, - ScriptOptions) { - if(this->hostname) { - DUPLICATE_ERROR(this->hostname, "hostname", - this->hostname->value()) - return false; - } - std::unique_ptr<Hostname> name(dynamic_cast<Hostname *>(obj)); - this->hostname = std::move(name); + } else if(key_name == "nameserver") { + std::unique_ptr<Nameserver> ns(dynamic_cast<Nameserver *>(obj)); + this->nses.push_back(std::move(ns)); return true; - } - - bool store_pkginstall(Keys::Key* obj, int lineno, int *, int *warnings, - ScriptOptions opts) { - PkgInstall *install = dynamic_cast<PkgInstall *>(obj); - for(auto &pkg : install->packages()) { - if(opts.test(StrictMode) && packages.find(pkg) != packages.end()) { - if(warnings) *warnings += 1; - output_warning("installfile:" + std::to_string(lineno), - "pkginstall: package '" + pkg + - "' has already been specified"); - continue; - } - packages.insert(pkg); - } - delete install; + } else if(key_name == "netssid") { + std::unique_ptr<NetSSID> ssid(dynamic_cast<NetSSID *>(obj)); + this->ssids.push_back(std::move(ssid)); return true; - } - - bool store_rootpw(Keys::Key* obj, int lineno, int *errors, int *, - ScriptOptions) { - if(this->rootpw) { - DUPLICATE_ERROR(this->rootpw, std::string("rootpw"), - "an encrypted passphrase") - return false; - } - std::unique_ptr<RootPassphrase> r(dynamic_cast<RootPassphrase *>(obj)); - this->rootpw = std::move(r); + } else if(key_name == "hostname") { + return store_hostname(obj, lineno, errors, warnings, opts); + } else if(key_name == "pkginstall") { + return store_pkginstall(obj, lineno, errors, warnings, opts); + } else if(key_name == "rootpw") { + return store_rootpw(obj, lineno, errors, warnings, opts); + } else if(key_name == "language") { + return store_lang(obj, lineno, errors, warnings, opts); + } else if(key_name == "keymap") { + return store_keymap(obj, lineno, errors, warnings, opts); + } else if(key_name == "firmware") { + return store_firmware(obj, lineno, errors, warnings, opts); + } else if(key_name == "timezone") { + return store_timezone(obj, lineno, errors, warnings, opts); + } else if(key_name == "repository") { + std::unique_ptr<Repository> repo(dynamic_cast<Repository *>(obj)); + this->repos.push_back(std::move(repo)); return true; - } - - bool store_firmware(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - std::unique_ptr<Firmware> f(dynamic_cast<Firmware *>(obj)); -#ifdef NON_LIBRE_FIRMWARE - if(this->firmware) { - DUPLICATE_ERROR(this->firmware, std::string("firmware"), - (this->firmware->test()) ? "true" : "false") - return false; - } - this->firmware = std::move(f); + } else if(key_name == "signingkey") { + std::unique_ptr<SigningKey> key(dynamic_cast<SigningKey *>(obj)); + this->repo_keys.push_back(std::move(key)); return true; -#else - assert(!f->test()); + } else if(key_name == "username") { + return store_username(obj, lineno, errors, warnings, opts); + } else if(key_name == "useralias") { + return store_useralias(obj, lineno, errors, warnings, opts); + } else if(key_name == "userpw") { + return store_userpw(obj, lineno, errors, warnings, opts); + } else if(key_name == "usericon") { + return store_usericon(obj, lineno, errors, warnings, opts); + } else if(key_name == "usergroups") { + return store_usergroups(obj, lineno, errors, warnings, opts); + } else if(key_name == "diskid") { + std::unique_ptr<DiskId> diskid(dynamic_cast<DiskId *>(obj)); + this->diskids.push_back(std::move(diskid)); return true; -#endif - } - - bool store_lang(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - if(this->lang) { - DUPLICATE_ERROR(this->lang, std::string("language"), - this->lang->value()) - return false; - } - std::unique_ptr<Language> l(dynamic_cast<Language *>(obj)); - this->lang = std::move(l); + } else if(key_name == "disklabel") { + std::unique_ptr<DiskLabel> l(dynamic_cast<DiskLabel *>(obj)); + this->disklabels.push_back(std::move(l)); return true; - } - - bool store_keymap(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - if(this->keymap) { - DUPLICATE_ERROR(this->keymap, std::string("keymap"), - this->keymap->value()) - return false; - } - std::unique_ptr<Keymap> k(dynamic_cast<Keymap *>(obj)); - this->keymap = std::move(k); + } else if(key_name == "partition") { + std::unique_ptr<Partition> p(dynamic_cast<Partition *>(obj)); + this->partitions.push_back(std::move(p)); return true; - } - - bool store_timezone(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - if(this->tzone) { - DUPLICATE_ERROR(this->tzone, std::string("timezone"), - this->tzone->value()) - return false; - } - std::unique_ptr<Timezone> t(dynamic_cast<Timezone *>(obj)); - this->tzone = std::move(t); + } else if(key_name == "lvm_pv") { + std::unique_ptr<LVMPhysical> pv(dynamic_cast<LVMPhysical *>(obj)); + this->lvm_pvs.push_back(std::move(pv)); return true; - } - - bool store_username(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - if(accounts.size() >= 255) { - if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "username: too many users", - "you may only specify 255 users"); - return false; - } - std::unique_ptr<Username> name(dynamic_cast<Username *>(obj)); - if(accounts.find(name->value()) != accounts.end()) { - DUPLICATE_ERROR((*accounts.find(name->value())).second->name, - "username", "assigned") - return false; - } - std::unique_ptr<UserDetail> detail(new UserDetail); - detail->name = std::move(name); - accounts.insert(std::make_pair(detail->name->value(), - std::move(detail))); + } else if(key_name == "lvm_vg") { + std::unique_ptr<LVMGroup> vg(dynamic_cast<LVMGroup *>(obj)); + this->lvm_vgs.push_back(std::move(vg)); return true; - } - -#define GET_USER_DETAIL(OBJ, KEY) \ - if(accounts.find(OBJ->username()) == accounts.end()) {\ - if(errors) *errors += 1;\ - output_error("installfile:" + std::to_string(lineno),\ - std::string(KEY) + ": account name " + OBJ->username() +\ - " is unknown");\ - return false;\ - }\ - UserDetail *detail = (*accounts.find(OBJ->username())).second.get(); - - bool store_useralias(Keys::Key* obj, int lineno, int *errors, - int *, ScriptOptions) { - std::unique_ptr<UserAlias> alias(dynamic_cast<UserAlias *>(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); + } else if(key_name == "lvm_lv") { + std::unique_ptr<LVMVolume> lv(dynamic_cast<LVMVolume *>(obj)); + this->lvm_lvs.push_back(std::move(lv)); return true; - } - - bool store_userpw(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - std::unique_ptr<UserPassphrase> pw(dynamic_cast<UserPassphrase *>(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); + } else if(key_name == "encrypt") { + std::unique_ptr<Encrypt> e(dynamic_cast<Encrypt *>(obj)); + this->luks.push_back(std::move(e)); return true; - } - - bool store_usericon(Keys::Key *obj, int lineno, int *errors, int *, - ScriptOptions) { - std::unique_ptr<UserIcon> icon(dynamic_cast<UserIcon *>(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); + } else if(key_name == "fs") { + std::unique_ptr<Filesystem> fs(dynamic_cast<Filesystem *>(obj)); + this->fses.push_back(std::move(fs)); return true; - } - - bool store_usergroups(Keys::Key* obj, int lineno, int *errors, - int *, ScriptOptions) { - std::unique_ptr<UserGroups> grp(dynamic_cast<UserGroups *>(obj)); - GET_USER_DETAIL(grp, "usergroups") - detail->groups.push_back(std::move(grp)); + } else if(key_name == "mount") { + std::unique_ptr<Mount> mount(dynamic_cast<Mount *>(obj)); + this->mounts.push_back(std::move(mount)); return true; + } else { + return false; } -#undef GET_USER_DETAIL - -#undef DUPLICATE_ERROR -}; +} Script::Script() { @@ -585,921 +314,4 @@ const Script *Script::load(std::istream &sstream, #undef PARSER_ERROR } - -/*! Perform all necessary validations on a single user account. - * @param name The username of the account. - * @param detail The UserDetail record of the account. - * @param opts The ScriptOptions in use. - * @returns A count of errors encountered, or 0 if the account is valid. - */ -int validate_one_account(const std::string &name, UserDetail *detail, - ScriptOptions opts) { - int failures = 0; - - /* REQ: Runner.Validate.username */ - if(!detail->name->validate(opts)) { - failures++; - } - - /* REQ: Runner.Validate.useralias */ - if(detail->alias && !detail->alias->validate(opts)) { - failures++; - } - - /* REQ: Runner.Validate.userpw */ - if(detail->passphrase && !detail->passphrase->validate(opts)) { - failures++; - } - - /* REQ: Runner.Validate.userpw.None */ - if(!detail->passphrase) { - long line = detail->name->lineno(); - output_warning("installfile:" + std::to_string(line), - "username: " + name + " has no set passphrase", - "This account will not be able to log in."); - } - - /* REQ: Runner.Validate.usericon */ - if(detail->icon && !detail->icon->validate(opts)) { - failures++; - } - - if(detail->groups.size() > 0) { - std::set<std::string> seen_groups; - for(auto &group : detail->groups) { - /* REQ: Runner.Validate.usergroups */ - if(!group->validate(opts)) { - failures++; - } - - /* REQ: Runner.Validate.usergroups.Unique */ - const std::set<std::string> these = group->groups(); - if(!std::all_of(these.begin(), these.end(), - [&seen_groups](std::string elem) { - return seen_groups.find(elem) == seen_groups.end(); - }) - ) { - output_error("installfile:" + std::to_string(group->lineno()), - "usergroups: duplicate group name specified"); - failures++; - } - seen_groups.insert(these.begin(), these.end()); - } - - /* REQ: Runner.Validate.usergroups.Count */ - if(seen_groups.size() > 16) { - output_error("installfile:0", - "usergroups: " + name + " is a member of " + - "more than 16 groups"); - failures++; - } - } - - return failures; -} - - -/*! Add the default repositories to the repo list. - * @param repos The list of repositories - * The list +repos+ will be modified with the default repositories for - * Adélie Linux. Both system/ and user/ will be added. - */ -bool add_default_repos(std::vector<std::unique_ptr<Keys::Repository>> &repos) { - Keys::Repository *sys_key = dynamic_cast<Keys::Repository *>( - Horizon::Keys::Repository::parseFromData( - "https://distfiles.adelielinux.org/adelie/stable/system", 0, - nullptr, nullptr - ) - ); - if(!sys_key) { - output_error("internal", "failed to create default system repository"); - return false; - } - std::unique_ptr<Keys::Repository> sys_repo(sys_key); - repos.push_back(std::move(sys_repo)); - Keys::Repository *user_key = dynamic_cast<Keys::Repository *>( - Horizon::Keys::Repository::parseFromData( - "https://distfiles.adelielinux.org/adelie/stable/user", 0, - nullptr, nullptr - ) - ); - if(!user_key) { - output_error("internal", "failed to create default user repository"); - return false; - } - std::unique_ptr<Keys::Repository> user_repo(user_key); - repos.push_back(std::move(user_repo)); - -#ifdef NON_LIBRE_FIRMWARE - /* REQ: Runner.Execute.firmware.Repository */ - if(this->internal->firmware && this->internal->firmware->test()) { - Keys::Repository *fw_key = dynamic_cast<Keys::Repository *>( - Horizon::Keys::Repository::parseFromData( - "https://distfiles.apkfission.net/adelie-stable/nonfree", - 0, nullptr, nullptr - ) - ); - if(!fw_key) { - output_error("internal", - "failed to create firmware repository"); - return false; - } - std::unique_ptr<Keys::Repository> fw_repo(fw_key); - repos.push_back(std::move(fw_repo)); - } -#endif - return true; -} - - -/*! Add the default repository keys to the signing key list. - * @param keys The list of repository keys. - * The list +keys+ will be modified with the default repository signing keys - * for Adélie Linux. - */ -bool add_default_repo_keys(std::vector<std::unique_ptr<Keys::SigningKey>> &keys) { - Keys::SigningKey *key = dynamic_cast<Keys::SigningKey *>( - Horizon::Keys::SigningKey::parseFromData( - "/etc/apk/keys/packages@adelielinux.org.pub", 0, nullptr, nullptr) - ); - if(!key) { - output_error("internal", "failed to create default repository signing key"); - return false; - } - std::unique_ptr<Keys::SigningKey> repo_key(key); - keys.push_back(std::move(repo_key)); - return true; -} - - -bool Script::validate() const { - int failures = 0; - std::set<std::string> seen_diskids, seen_labels, seen_parts, seen_pvs, - seen_vg_names, seen_vg_pvs, seen_lvs, seen_fses, seen_mounts, - seen_luks; - std::map<const std::string, int> seen_iface; -#ifdef HAS_INSTALL_ENV - error_code ec; -#endif /* HAS_INSTALL_ENV */ - - /* REQ: Runner.Validate.network */ - if(!this->internal->network->validate(this->opts)) failures++; - - /* REQ: Runner.Validate.network.netaddress */ - if(this->internal->network->test() && - this->internal->addresses.size() == 0) { - failures++; - output_error("installfile:0", - "networking requested but no 'netaddress' defined", - "You need to specify at least one address to enable " - "networking."); - } - for(auto &address : this->internal->addresses) { - if(!address->validate(this->opts)) { - failures++; - } - - /* REQ: Runner.Validate.network.netaddress.Count */ - if(seen_iface.find(address->iface()) == seen_iface.end()) { - seen_iface.insert(std::make_pair(address->iface(), 1)); - } else { - seen_iface[address->iface()] += 1; - if(seen_iface[address->iface()] > 255) { - failures++; - output_error("installfile:" + std::to_string(address->lineno()), - "netaddress: interface '" + address->iface() + - "' has too many addresses assigned"); - } - } - } - - /* REQ: Runner.Validate.nameserver */ - for(auto &ns : this->internal->nses) { - if(!ns->validate(this->opts)) { - failures++; - } - } - - /* REQ: Runner.Validate.network.netssid */ - for(auto &ssid : this->internal->ssids) { - if(!ssid->validate(this->opts)) { - failures++; - } - } - - /* REQ: Runner.Validate.hostname */ - if(!this->internal->hostname->validate(this->opts)) failures++; - - /* REQ: Runner.Validate.rootpw */ - if(!this->internal->rootpw->validate(this->opts)) failures++; - - /* REQ: Runner.Validate.language */ - if(internal->lang && !internal->lang->validate(this->opts)) failures++; - - /* REQ: Runner.Validate.keymap */ - if(internal->keymap && !internal->keymap->validate(this->opts)) failures++; - -#ifdef NON_LIBRE_FIRMWARE - /* REQ: Runner.Validate.firmware */ - if(!this->internal->firmware->validate(this->opts)) failures++; -#endif - - /* REQ: Runner.Execute.timezone */ - if(!internal->tzone) { - Keys::Timezone *utc = dynamic_cast<Keys::Timezone *>( - Horizon::Keys::Timezone::parseFromData("UTC", 0, - &failures, nullptr) - ); - if(!utc) { - output_error("internal", "failed to create default timezone"); - return false; - } - std::unique_ptr<Keys::Timezone> zone(utc); - this->internal->tzone = std::move(zone); - } - - /* REQ: Runner.Validate.timezone */ - if(!this->internal->tzone->validate(this->opts)) failures++; - - /* REQ: Script.repository */ - if(this->internal->repos.size() == 0) { - if(!add_default_repos(this->internal->repos)) { - return false; - } - } - - /* REQ: Runner.Validate.repository */ - for(auto &repo : this->internal->repos) { - if(!repo->validate(this->opts)) { - failures++; - } - } - if(this->internal->repos.size() > 10) { - failures++; - output_error("installfile:" + - std::to_string(this->internal->repos[11]->lineno()), - "repository: too many repositories specified", - "You may only specify up to 10 repositories."); - } - - /* REQ: Script.signingkey */ - if(this->internal->repo_keys.size() == 0) { - if(!add_default_repo_keys(this->internal->repo_keys)) { - return false; - } - } - - /* REQ: Runner.Validate.signingkey */ - for(auto &key : this->internal->repo_keys) { - if(!key->validate(this->opts)) { - failures++; - } - } - if(this->internal->repo_keys.size() > 10) { - failures++; - output_error("installfile:" + - std::to_string(this->internal->repo_keys[11]->lineno()), - "signingkey: too many keys specified", - "You may only specify up to 10 repository keys."); - } - - for(auto &acct : this->internal->accounts) { - UserDetail *detail = acct.second.get(); - failures += validate_one_account(acct.first, detail, this->opts); - } - - /* REQ: Runner.Validate.diskid */ - for(auto &diskid : this->internal->diskids) { - if(!diskid->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.diskid.Unique */ - if(seen_diskids.find(diskid->device()) != seen_diskids.end()) { - failures++; - output_error("installfile:" + std::to_string(diskid->lineno()), - "diskid: device " + diskid->device() + - " has already been identified"); - } - seen_diskids.insert(diskid->device()); - } - - /* REQ: Runner.Validate.disklabel */ - for(auto &label : this->internal->disklabels) { - if(!label->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.disklabel.Unique */ - if(seen_labels.find(label->device()) != seen_labels.end()) { - failures++; - output_error("installfile:" + std::to_string(label->lineno()), - "disklabel: device " + label->device() + - " already has a label queued"); - } - seen_labels.insert(label->device()); - } - - /* REQ: Runner.Validate.partition */ - for(auto &part : this->internal->partitions) { - if(!part->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.partition.Unique */ - const std::string &dev = part->device(); - const std::string maybe_p(::isdigit(dev[dev.size() - 1]) ? "p" : ""); - std::string name = dev + maybe_p + std::to_string(part->partno()); - if(seen_parts.find(name) != seen_parts.end()) { - failures++; - output_error("installfile:" + std::to_string(part->lineno()), - "partition: partition #" + - std::to_string(part->partno()) + - " already exists on device " + part->device()); - } - seen_parts.insert(name); - } - - /* REQ: Runner.Validate.lvm_pv */ - for(auto &pv : this->internal->lvm_pvs) { - if(!pv->validate(this->opts)) { - failures++; - continue; - } - - /* We don't actually have a requirement, but... */ - if(seen_pvs.find(pv->value()) != seen_pvs.end()) { - failures++; - output_error("installfile:" + std::to_string(pv->lineno()), - "lvm_pv: a physical volume already exists on device " - + pv->value()); - } - seen_pvs.insert(pv->value()); - - /* REQ: Runner.Validate.lvm_pv.Block */ - if(opts.test(InstallEnvironment)) { -#ifdef HAS_INSTALL_ENV - if(!fs::exists(pv->value(), ec) && - seen_parts.find(pv->value()) == seen_parts.end()) { - failures++; - output_error("installfile:" + std::to_string(pv->lineno()), - "lvm_pv: device " + pv->value() + - " does not exist"); - } -#endif /* HAS_INSTALL_ENV */ - } - } - - /* REQ: Runner.Validate.lvm_vg */ - for(auto &vg : this->internal->lvm_vgs) { - if(!vg->validate(this->opts)) { - failures++; - continue; - } - - if(seen_vg_names.find(vg->name()) != seen_vg_names.end()) { - failures++; - output_error("installfile:" + std::to_string(vg->lineno()), - "lvm_vg: duplicate volume group name specified", - vg->name() + " already given"); - } - seen_vg_names.insert(vg->name()); - - if(seen_vg_pvs.find(vg->pv()) != seen_vg_pvs.end()) { - failures++; - output_error("installfile:" + std::to_string(vg->lineno()), - "lvm_vg: a volume group already exists on " + - vg->pv()); - } - seen_vg_pvs.insert(vg->pv()); - - /* REQ: Runner.Validate.lvm_vg.PhysicalVolume */ - /* If we already know a PV is being created there, we know it's fine */ - if(seen_pvs.find(vg->pv()) == seen_pvs.end()) { - /* Okay, let's see if a PV already exists there... */ - if(opts.test(InstallEnvironment)) { -#ifdef HAS_INSTALL_ENV - if(!vg->test_pv(opts)) { - failures++; - output_error("installfile:" + std::to_string(vg->lineno()), - "lvm_vg: a physical volume does not exist on " - + vg->pv()); - } -#endif /* HAS_INSTALL_ENV */ - } else { - /* We can't tell if we aren't running on the target. */ - output_warning("installfile:" + std::to_string(vg->lineno()), - "lvm_vg: please ensure an LVM physical volume " - "already exists at " + vg->pv()); - } - } - } - - /* REQ: Runner.Validate.lvm_lv */ - for(auto &lv : this->internal->lvm_lvs) { - const std::string lvpath(lv->vg() + "/" + lv->name()); - if(!lv->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.lvm_lv.Name */ - if(seen_lvs.find(lvpath) != seen_lvs.end()) { - failures++; - output_error("installfile:" + std::to_string(lv->lineno()), - "lvm_lv: a volume with the name " + lv->name() + - " already exists on the volume group " + lv->vg()); - } - seen_lvs.insert(lvpath); - - /* REQ: Runner.Validate.lvm_lv.VolumeGroup */ - if(seen_vg_names.find(lv->vg()) == seen_vg_names.end()) { - /* Let's make sure it still exists, if we are running in the IE */ - if(opts.test(InstallEnvironment)) { -#ifdef HAS_INSTALL_ENV - if(!fs::exists("/dev/" + lv->vg())) { - failures++; - output_error("installfile:" + std::to_string(lv->lineno()), - "lvm_lv: volume group " + lv->vg() + - " does not exist"); - } -#endif /* HAS_INSTALL_ENV */ - } - } - } - -#define CHECK_EXIST_PART_LV(device, key, line) \ - if(!fs::exists(device, ec) &&\ - seen_parts.find(device) == seen_parts.end() &&\ - seen_lvs.find(device.substr(5)) == seen_lvs.end()) {\ - failures++;\ - output_error("installfile:" + std::to_string(line),\ - std::string(key) + ": device " + device +\ - " does not exist");\ - } - - /* REQ: Runner.Validate.encrypt */ - for(auto &crypt : this->internal->luks) { - if(!crypt->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.encrypt.Unique */ - if(seen_luks.find(crypt->device()) != seen_luks.end()) { - failures++; - output_error("installfile:" + std::to_string(crypt->lineno()), - "encrypt: encryption is already scheduled for " + - crypt->device()); - } - seen_luks.insert(crypt->device()); - - /* REQ: Runner.Validate.encrypt.Block */ - if(opts.test(InstallEnvironment)) { -#ifdef HAS_INSTALL_ENV - CHECK_EXIST_PART_LV(crypt->device(), "encrypt", crypt->lineno()) -#endif /* HAS_INSTALL_ENV */ - } - } - - /* REQ: Runner.Validate.fs */ - for(auto &fs : this->internal->fses) { - if(!fs->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.fs.Unique */ - if(seen_fses.find(fs->device()) != seen_fses.end()) { - failures++; - output_error("installfile:" + std::to_string(fs->lineno()), - "fs: a filesystem is already scheduled to be " - "created on " + fs->device()); - } - seen_fses.insert(fs->device()); - - /* REQ: Runner.Validate.fs.Block */ - if(opts.test(InstallEnvironment)) { -#ifdef HAS_INSTALL_ENV - CHECK_EXIST_PART_LV(fs->device(), "fs", fs->lineno()) -#endif /* HAS_INSTALL_ENV */ - } - } - - /* REQ: Runner.Validate.mount */ - for(auto &mount : this->internal->mounts) { - if(!mount->validate(this->opts)) { - failures++; - continue; - } - - /* REQ: Runner.Validate.mount.Unique */ - if(seen_mounts.find(mount->mountpoint()) != seen_mounts.end()) { - failures++; - output_error("installfile:" + std::to_string(mount->lineno()), - "mount: mountpoint " + mount->mountpoint() + - " has already been specified; " + mount->device() + - " is a duplicate"); - } - seen_mounts.insert(mount->mountpoint()); - - /* REQ: Runner.Validate.mount.Block */ - if(opts.test(InstallEnvironment)) { -#ifdef HAS_INSTALL_ENV - CHECK_EXIST_PART_LV(mount->device(), "mount", mount->lineno()) -#endif /* HAS_INSTALL_ENV */ - } - } - -#undef CHECK_EXIST_PART_LV - - /* REQ: Runner.Validate.mount.Root */ - if(seen_mounts.find("/") == seen_mounts.end()) { - failures++; - output_error("installfile:0", "mount: no root mount specified"); - } - - output_log("validator", "0", "installfile", - std::to_string(failures) + " failure(s).", ""); - return (failures == 0); -} - - -bool Script::execute() const { - bool success; - error_code ec; - - /* assume create_directory will give us the error if removal fails */ - if(fs::exists("/tmp/horizon", ec)) { - fs::remove_all("/tmp/horizon", ec); - } - - if(!fs::create_directory("/tmp/horizon", ec)) { - output_error("internal", "could not create temporary directory", - ec.message()); - return false; - } - - /* REQ: Runner.Execute.Verify */ - output_step_start("validate"); - success = this->validate(); - output_step_end("validate"); - if(!success) { - /* REQ: Runner.Execute.Verify.Failure */ - output_error("validator", "The HorizonScript failed validation", - "Check the output from the validator."); - return false; - } - -#define EXECUTE_FAILURE(phase) \ - output_error(phase, "The HorizonScript failed to execute",\ - "Check the log file for more details.") - - /**************** DISK SETUP ****************/ - output_step_start("disk"); -#ifdef HAS_INSTALL_ENV - if(opts.test(InstallEnvironment)) ped_device_probe_all(); -#endif /* HAS_INSTALL_ENV */ - /* REQ: Runner.Execute.diskid */ - for(auto &diskid : this->internal->diskids) { - if(!diskid->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* REQ: Runner.Execute.disklabel */ - for(auto &label : this->internal->disklabels) { - if(!label->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* REQ: Runner.Execute.partition */ - /* Ensure partitions are created in on-disk order. */ - std::sort(this->internal->partitions.begin(), this->internal->partitions.end(), - [](std::unique_ptr<Keys::Partition> const &e1, - std::unique_ptr<Keys::Partition> const &e2) { - return (e1->device() + "p" + std::to_string(e1->partno())) < - (e2->device() + "p" + std::to_string(e2->partno())); - }); - for(auto &part : this->internal->partitions) { - if(!part->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* encrypt PVs */ - - /* REQ: Runner.Execute.lvm_pv */ - for(auto &pv : this->internal->lvm_pvs) { - if(!pv->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* REQ: Runner.Execute.lvm_vg */ - for(auto &vg : this->internal->lvm_vgs) { - if(!vg->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* REQ: Runner.Execute.lvm_lv */ - for(auto &lv : this->internal->lvm_lvs) { - if(!lv->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* encrypt */ - - /* REQ: Runner.Execute.fs */ - for(auto &fs : this->internal->fses) { - if(!fs->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } - - /* REQ: Runner.Execute.mount */ - /* Sort by mountpoint. - * This ensures that any subdirectory mounts come after their parent. */ - std::sort(this->internal->mounts.begin(), this->internal->mounts.end(), - [](std::unique_ptr<Keys::Mount> const &e1, - std::unique_ptr<Keys::Mount> const &e2) { - return e1->mountpoint() < e2->mountpoint(); - }); - for(auto &mount : this->internal->mounts) { - if(!mount->execute(opts)) { - EXECUTE_FAILURE("disk"); - return false; - } - } -#ifdef HAS_INSTALL_ENV - if(opts.test(InstallEnvironment)) ped_device_free_all(); -#endif /* HAS_INSTALL_ENV */ - output_step_end("disk"); - - /**************** PRE PACKAGE METADATA ****************/ - output_step_start("pre-metadata"); - - /* REQ: Runner.Execute.hostname */ - if(!this->internal->hostname->execute(opts)) { - EXECUTE_FAILURE("pre-metadata"); - return false; - } - - /* REQ: Runner.Execute.repository */ - if(opts.test(Simulate)) { - std::cout << "mkdir -p /target/etc/apk" << std::endl; - } -#ifdef HAS_INSTALL_ENV - else { - if(!fs::exists("/target/etc/apk", ec)) { - fs::create_directory("/target/etc/apk", ec); - if(ec) { - output_error("internal", "failed to initialise APK"); - EXECUTE_FAILURE("pre-metadata"); - return false; - } - } - } -#endif /* HAS_INSTALL_ENV */ - for(auto &repo : this->internal->repos) { - if(!repo->execute(opts)) { - EXECUTE_FAILURE("pre-metadata"); - return false; - } - } - -#ifdef NON_LIBRE_FIRMWARE - /* REQ: Runner.Execute.firmware */ - if(this->internal->firmware && this->internal->firmware->test()) { - this->internal->packages.insert("linux-firmware"); - } -#endif - output_step_end("pre-metadata"); - - /**************** NETWORK ****************/ - output_step_start("net"); - - if(!this->internal->ssids.empty()) { - std::ofstream wpao("/tmp/horizon/wpa_supplicant.conf", - std::ios_base::trunc); - if(wpao) { - wpao << "# Enable the control interface for wpa_cli and wpa_gui" - << std::endl - << "ctrl_interface=/var/run/wpa_supplicant" << std::endl - << "ctrl_interface_group=wheel" << std::endl - << "update_config=1" << std::endl; - wpao.close(); - } else { - output_error("internal", - "cannot write wireless networking configuration"); - } - - for(auto &ssid : this->internal->ssids) { - if(!ssid->execute(opts)) { - EXECUTE_FAILURE("net"); - /* "Soft" error. Not fatal. */ - } - } - - if(opts.test(Simulate)) { - std::ifstream wpai("/tmp/horizon/wpa_supplicant.conf"); - if(wpai) { - std::cout << "cat >/target/etc/wpa_supplicant/wpa_supplicant.conf " - << "<<- WPA_EOF" << std::endl - << wpai.rdbuf() << std::endl - << "WPA_EOF" << std::endl; - } else { - output_error("internal", - "cannot read wireless networking configuration"); - } - } else { - if(!fs::exists("/target/etc/wpa_supplicant", ec)) { - fs::create_directory("/target/etc/wpa_supplicant", ec); - } - fs::copy_file("/tmp/horizon/wpa_supplicant.conf", - "/target/etc/wpa_supplicant/wpa_supplicant.conf", - fs_overwrite, ec); - if(ec) { - output_error("internal", "cannot save wireless networking " - "configuration to target", ec.message()); - } - } - } - - if(!this->internal->addresses.empty()) { - fs::path netifrc_dir("/tmp/horizon/netifrc"); - if(!fs::exists(netifrc_dir) && - !fs::create_directory(netifrc_dir, ec)) { - output_error("internal", "cannot create temporary directory", - ec.message()); - } - - for(auto &addr : this->internal->addresses) { - if(!addr->execute(opts)) { - EXECUTE_FAILURE("net"); - /* "Soft" error. Not fatal. */ - } - } - - std::ostringstream conf; - for(auto &&var_dent : fs::directory_iterator(netifrc_dir)) { - const std::string variable(var_dent.path().filename().string()); - std::ifstream contents(var_dent.path().string()); - if(!contents) { - output_error("internal", "cannot read network configuration"); - EXECUTE_FAILURE("net"); - continue; - } - conf << variable << "=\""; - if(contents.rdbuf()->in_avail()) conf << contents.rdbuf(); - conf << "\"" << std::endl; - } - - if(opts.test(Simulate)) { - std::cout << "mkdir -p /target/etc/conf.d" << std::endl; - std::cout << "cat >>/target/etc/conf.d/net <<- NETCONF_EOF" - << std::endl << conf.str() << std::endl - << "NETCONF_EOF" << std::endl; - } else { - if(!fs::exists("/target/etc/conf.d")) { - fs::create_directory("/target/etc/conf.d", ec); - if(ec) { - output_error("internal", "could not create /etc/conf.d " - "directory", ec.message()); - } - } - std::ofstream conf_file("/target/etc/conf.d/net", - std::ios_base::app); - if(!conf_file) { - output_error("internal", "cannot save network configuration " - "to target"); - EXECUTE_FAILURE("net"); - } else { - conf_file << conf.str(); - } - } - } - - if(!this->internal->network->execute(opts)) { - EXECUTE_FAILURE("net"); - return false; - } - - if(this->internal->network->test()) { - bool do_wpa = !this->internal->ssids.empty(); - - if(opts.test(Simulate)) { - if(do_wpa) { - std::cout << "cp /target/etc/wpa_supplicant/wpa_supplicant.conf " - << "/etc/wpa_supplicant/wpa_supplicant.conf" - << std::endl; - } - std::cout << "cp /target/etc/conf.d/net /etc/conf.d/net" - << std::endl; - } else { - if(do_wpa) { - fs::copy_file("/target/etc/wpa_supplicant/wpa_supplicant.conf", - "/etc/wpa_supplicant/wpa_supplicant.conf", - fs_overwrite, ec); - if(ec) { - output_error("internal", "cannot use wireless configuration " - "during installation", ec.message()); - EXECUTE_FAILURE("net"); - } - } - fs::copy_file("/target/etc/conf.d/net", "/etc/conf.d/net", - fs_overwrite, ec); - if(ec) { - output_error("internal", "cannot use networking configuration " - "during installation", ec.message()); - EXECUTE_FAILURE("net"); - return false; - } - } - } - output_step_end("net"); - - /**************** PKGDB ****************/ - output_step_start("pkgdb"); - - /* REQ: Runner.Execute.signingkey */ - for(auto &key : this->internal->repo_keys) { - if(!key->execute(opts)) { - EXECUTE_FAILURE("pkgdb"); - return false; - } - } - - /* REQ: Runner.Execute.pkginstall.APKDB */ - output_info("internal", "initialising APK"); - if(opts.test(Simulate)) { - std::cout << "apk --root /target --initdb add" << std::endl; - } else { - if(system("apk --root /target --initdb add") != 0) { - EXECUTE_FAILURE("pkgdb"); - return false; - } - } - - /* REQ: Runner.Execute.pkginstall */ - output_info("internal", "installing packages to target"); - std::ostringstream pkg_list; - for(auto &pkg : this->internal->packages) { - pkg_list << pkg << " "; - } - if(opts.test(Simulate)) { - std::cout << "apk --root /target update" << std::endl; - std::cout << "apk --root /target add " << pkg_list.str() << std::endl; - } else { - if(system("apk --root /target update") != 0) { - EXECUTE_FAILURE("pkgdb"); - return false; - } - std::string apk_invoc = "apk --root /target add " + pkg_list.str(); - if(system(apk_invoc.c_str()) != 0) { - EXECUTE_FAILURE("pkgdb"); - return false; - } - } - - output_step_end("pkgdb"); - - /**************** POST PACKAGE METADATA ****************/ - output_step_start("post-metadata"); - - if(!this->internal->rootpw->execute(opts)) { - EXECUTE_FAILURE("post-metadata"); - return false; - } - - if(this->internal->lang && !this->internal->lang->execute(opts)) { - EXECUTE_FAILURE("post-metadata"); - return false; - } - - /* keymap */ - /* UserAccounts */ - - if(!this->internal->tzone->execute(opts)) { - EXECUTE_FAILURE("post-metadata"); - return false; - } - - output_step_end("post-metadata"); - return true; -} - } |