summaryrefslogblamecommitdiff
path: root/hscript/script_i.hh
blob: 9d854eb3dab2b3285b7d68701500291ba7b82acc (plain) (tree)















































                                                                           

                                       





































































































                                                                                









                                                                            










































































































































                                                                                 
/*
 * script_i.hh - Implementation of internal doodads for the Script class
 * libhscript, the HorizonScript library for
 * Project Horizon
 *
 * Copyright (c) 2019 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 <assert.h>
#include <map>
#include <memory>
#include <string>
#include <vector>

#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<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 CPU architecture. */
    std::unique_ptr<Arch> arch;
    /*! 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, Key *obj, int lineno,
                   int *errors, int *warnings, const ScriptOptions &opts);

#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(line),\
                 "duplicate value for key '" + std::string(KEY) + "'",\
                 err_str);

    bool store_network(Key* obj, int line, int *errors, int *, ScriptOptions) {
        if(network) {
            DUPLICATE_ERROR(network, "network",
                            network->test() ? "true" : "false")
            return false;
        }
        std::unique_ptr<Network> net(dynamic_cast<Network *>(obj));
        network = std::move(net);
        return true;
    }

    bool store_hostname(Key* obj, int line, int *errors, int *, ScriptOptions) {
        if(hostname) {
            DUPLICATE_ERROR(hostname, "hostname", hostname->value())
            return false;
        }
        std::unique_ptr<Hostname> name(dynamic_cast<Hostname *>(obj));
        hostname = std::move(name);
        return true;
    }

    bool store_pkginstall(Key* obj, int line, 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(line),
                               "pkginstall: package '" + pkg +
                               "' has already been specified");
                continue;
            }
            packages.insert(pkg);
        }
        delete install;
        return true;
    }

    bool store_arch(Key* obj, int line, int *errors, int *, ScriptOptions) {
        if(arch) {
            DUPLICATE_ERROR(arch, "arch", arch->value())
            return false;
        }
        std::unique_ptr<Arch> a(dynamic_cast<Arch *>(obj));
        arch = std::move(a);
        return true;
    }

    bool store_rootpw(Key* obj, int line, int *errors, int *, ScriptOptions) {
        if(rootpw) {
            DUPLICATE_ERROR(rootpw, "rootpw", "an encrypted passphrase")
            return false;
        }
        std::unique_ptr<RootPassphrase> r(dynamic_cast<RootPassphrase *>(obj));
        rootpw = std::move(r);
        return true;
    }

    bool store_firmware(Key *obj, int line, int *errors, int *, ScriptOptions) {
        std::unique_ptr<Firmware> f(dynamic_cast<Firmware *>(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, int line, int *errors, int *, ScriptOptions) {
        if(lang) {
            DUPLICATE_ERROR(lang, "language", lang->value())
            return false;
        }
        std::unique_ptr<Language> l(dynamic_cast<Language *>(obj));
        lang = std::move(l);
        return true;
    }

    bool store_keymap(Key *obj, int line, int *errors, int *, ScriptOptions) {
        if(keymap) {
            DUPLICATE_ERROR(keymap, "keymap", keymap->value())
            return false;
        }
        std::unique_ptr<Keymap> k(dynamic_cast<Keymap *>(obj));
        keymap = std::move(k);
        return true;
    }

    bool store_timezone(Key *obj, int line, int *errors, int *, ScriptOptions) {
        if(tzone) {
            DUPLICATE_ERROR(tzone, "timezone", tzone->value())
            return false;
        }
        std::unique_ptr<Timezone> t(dynamic_cast<Timezone *>(obj));
        tzone = std::move(t);
        return true;
    }

    bool store_username(Key *obj, int line, int *errors, int *, ScriptOptions) {
        if(accounts.size() >= 255) {
            if(errors) *errors += 1;
            output_error("installfile:" + std::to_string(line),
                         "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)));
        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(line),\
                     std::string(KEY) + ": account name " + OBJ->username() +\
                     " is unknown");\
        return false;\
    }\
    UserDetail *detail = (*accounts.find(OBJ->username())).second.get();

    bool store_useralias(Key* obj, int line, 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);
        return true;
    }

    bool store_userpw(Key *obj, int line, 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);
        return true;
    }

    bool store_usericon(Key *obj, int line, 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);
        return true;
    }

    bool store_usergroups(Key* obj, int line, 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));
        return true;
    }
#undef GET_USER_DETAIL

#undef DUPLICATE_ERROR
};

}

#endif /* !HSCRIPT_SCRIPT_INTERNAL_HH_ */