diff options
Diffstat (limited to 'hscript')
-rw-r--r-- | hscript/disk.cc | 202 | ||||
-rw-r--r-- | hscript/disk.hh | 86 | ||||
-rw-r--r-- | hscript/disk_lvm.cc | 78 | ||||
-rw-r--r-- | hscript/key.cc | 2 | ||||
-rw-r--r-- | hscript/key.hh | 23 | ||||
-rw-r--r-- | hscript/meta.cc | 211 | ||||
-rw-r--r-- | hscript/meta.hh | 80 | ||||
-rw-r--r-- | hscript/network.cc | 176 | ||||
-rw-r--r-- | hscript/network.hh | 40 | ||||
-rw-r--r-- | hscript/script.cc | 58 | ||||
-rw-r--r-- | hscript/script.hh | 6 | ||||
-rw-r--r-- | hscript/script_i.hh | 78 | ||||
-rw-r--r-- | hscript/script_l.hh | 35 | ||||
-rw-r--r-- | hscript/script_v.cc | 111 | ||||
-rw-r--r-- | hscript/user.cc | 119 | ||||
-rw-r--r-- | hscript/user.hh | 53 |
16 files changed, 619 insertions, 739 deletions
diff --git a/hscript/disk.cc b/hscript/disk.cc index 049410e..07499b8 100644 --- a/hscript/disk.cc +++ b/hscript/disk.cc @@ -36,24 +36,22 @@ using namespace Horizon::Keys; #ifdef HAS_INSTALL_ENV /*! Determine if _block is a valid block device. * @param key The key associated with this test. - * @param line The line number where the key exists. + * @param pos The location where the key exists. * @param _block The path to test. * @returns true if _block is valid, false otherwise. * @note Will output_error if an error occurs. */ -bool is_block_device(const std::string &key, long line, +bool is_block_device(const std::string &key, const Horizon::ScriptLocation &pos, const std::string &_block) { struct stat blk_stat; const char *block_c = _block.c_str(); if(access(block_c, F_OK) != 0 || stat(block_c, &blk_stat) != 0) { - output_error("installfile:" + std::to_string(line), - key + ": error opening device " + _block, + output_error(pos, key + ": error opening device " + _block, strerror(errno)); return false; } if(!S_ISBLK(blk_stat.st_mode)) { - output_error("installfile:" + std::to_string(line), - key + ": " + _block + " is not a valid block device"); + output_error(pos, key + ": " + _block + " is not a valid block device"); return false; } return true; @@ -61,21 +59,20 @@ bool is_block_device(const std::string &key, long line, #endif /* HAS_INSTALL_ENV */ -Key *DiskId::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *DiskId::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { std::string block, ident; std::string::size_type block_end = data.find_first_of(' '); if(block_end == std::string::npos) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "diskid: expected an identification string", + output_error(pos, "diskid: expected an identification string", "valid format for diskid is: [block] [id-string]"); return nullptr; } block = data.substr(0, block_end); ident = data.substr(block_end + 1); - return new DiskId(script, lineno, block, ident); + return new DiskId(script, pos, block, ident); } bool DiskId::validate() const { @@ -84,7 +81,7 @@ bool DiskId::validate() const { if(script->options().test(InstallEnvironment)) { /* Unlike 'mount', 'diskid' *does* require that the block device exist * before installation begins. This test is always valid. */ - return is_block_device("diskid", this->lineno(), _block); + return is_block_device("diskid", where(), _block); } #endif /* HAS_INSTALL_ENV */ @@ -94,9 +91,8 @@ bool DiskId::validate() const { bool DiskId::execute() const { bool match = false; - output_info("installfile:" + std::to_string(line), - "diskid: Checking " + _block + " for identification string " + - _ident); + output_info(pos, "diskid: Checking " + _block + + " for identification string " + _ident); if(!script->options().test(InstallEnvironment)) return true; @@ -107,8 +103,7 @@ bool DiskId::execute() const { struct stat blk_stat; const char *block_c = _block.c_str(); if(stat(block_c, &blk_stat) != 0) { - output_error("installfile:" + std::to_string(line), - "diskid: error opening device " + _block, + output_error(pos, "diskid: error opening device " + _block, strerror(errno)); return false; } @@ -116,16 +111,14 @@ bool DiskId::execute() const { udev = udev_new(); if(!udev) { - output_error("installfile:" + std::to_string(line), - "diskid: failed to communicate with udevd", + output_error(pos, "diskid: failed to communicate with udevd", "cannot read disk information"); return false; } device = udev_device_new_from_devnum(udev, 'b', blk_stat.st_rdev); if(!device) { udev_unref(udev); - output_error("installfile:" + std::to_string(line), - "diskid: failed to retrieve disk from udevd", + output_error(pos, "diskid: failed to retrieve disk from udevd", "cannot read disk information"); return false; } @@ -136,15 +129,13 @@ bool DiskId::execute() const { std::string full_str(serial); match = (full_str.find(_ident) != std::string::npos); } else { - output_error("installfile:" + std::to_string(line), - "diskid: failed to retrieve disk identification", + output_error(pos, "diskid: failed to retrieve disk identification", "cannot read disk information"); } if(!match) { - output_error("installfile:" + std::to_string(line), - "diskid: device does not match expected identification " - "string"); + output_error(pos, "diskid: device does not match expected " + "identification string"); } udev_device_unref(device); @@ -155,8 +146,9 @@ bool DiskId::execute() const { } -Key *DiskLabel::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *DiskLabel::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { std::string block, label; std::string::size_type sep = data.find_first_of(' '); LabelType type; @@ -164,8 +156,7 @@ Key *DiskLabel::parseFromData(const std::string &data, int lineno, int *errors, /* REQ: Runner.Validate.disklabel.Validity */ if(sep == std::string::npos || data.length() == sep + 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "disklabel: expected a label type", + output_error(pos, "disklabel: expected a label type", "valid format for disklabel is: [disk] [type]"); return nullptr; } @@ -182,13 +173,12 @@ Key *DiskLabel::parseFromData(const std::string &data, int lineno, int *errors, type = GPT; } else { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "disklabel: '" + label + "' is not a valid label type", + output_error(pos, "disklabel: invalid label type '" + label + "'", "valid label types are: apm, mbr, gpt"); return nullptr; } - return new DiskLabel(script, lineno, block, type); + return new DiskLabel(script, pos, block, type); } bool DiskLabel::validate() const { @@ -196,7 +186,7 @@ bool DiskLabel::validate() const { /* REQ: Runner.Validate.disklabel.Block */ if(script->options().test(InstallEnvironment)) { /* disklabels are created before any others, so we can check now */ - return is_block_device("disklabel", this->lineno(), _block); + return is_block_device("disklabel", where(), _block); } #endif /* HAS_INSTALL_ENV */ @@ -217,8 +207,7 @@ bool DiskLabel::execute() const { break; } - output_info("installfile:" + std::to_string(this->lineno()), - "disklabel: creating new " + type_str + " disklabel on " + + output_info(pos, "disklabel: creating new " + type_str + " disklabel on " + device()); if(script->options().test(Simulate)) { @@ -233,8 +222,7 @@ bool DiskLabel::execute() const { int res; if(label == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), - "disklabel: Parted does not support label type " + + output_error(pos, "disklabel: Parted does not support label type " + type_str + "!"); return false; } @@ -243,16 +231,14 @@ bool DiskLabel::execute() const { ped_disk_clobber(pdevice); PedDisk *disk = ped_disk_new_fresh(pdevice, label); if(disk == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), - "disklabel: internal error creating new " + + output_error(pos, "disklabel: internal error creating new " + type_str + " disklabel on " + _block); return false; } res = ped_disk_commit(disk); if(res != 1) { - output_error("installfile:" + std::to_string(this->lineno()), - "disklabel: error creating disklabel on " + _block); + output_error(pos, "disklabel: error creating disklabel on " + _block); } return (res == 1); #else @@ -261,8 +247,8 @@ bool DiskLabel::execute() const { } -Key *Encrypt::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Encrypt::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { std::string::size_type sep = data.find(' '); std::string dev, pass; @@ -275,12 +261,11 @@ Key *Encrypt::parseFromData(const std::string &data, int lineno, int *errors, if(dev.size() < 6 || dev.compare(0, 5, "/dev/")) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "encrypt: expected path to block device"); + output_error(pos, "encrypt: expected path to block device"); return nullptr; } - return new Encrypt(script, lineno, dev, pass); + return new Encrypt(script, pos, dev, pass); } bool Encrypt::validate() const { @@ -396,8 +381,9 @@ bool parse_size_string(const std::string &in_size, uint64_t *out_size, } -Key *Partition::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Partition::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { std::string block, pno, size_str, typecode; std::string::size_type next_pos, last_pos; int part_no; @@ -408,8 +394,7 @@ Key *Partition::parseFromData(const std::string &data, int lineno, int *errors, long spaces = std::count(data.cbegin(), data.cend(), ' '); if(spaces < 2 || spaces > 3) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "partition: expected either 3 or 4 elements, got: " + + output_error(pos, "partition: expected either 3 or 4 elements, got: " + std::to_string(spaces), "syntax is: partition [block] [#] [size] ([type])"); return nullptr; @@ -420,8 +405,7 @@ Key *Partition::parseFromData(const std::string &data, int lineno, int *errors, if(block.compare(0, 4, "/dev")) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "partition: expected path to block device", + output_error(pos, "partition: expected path to block device", "'" + block + "' is not a valid block device path"); return nullptr; } @@ -432,8 +416,7 @@ Key *Partition::parseFromData(const std::string &data, int lineno, int *errors, part_no = std::stoi(pno); } catch(const std::exception &) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "partition: expected partition number, got", pno); + output_error(pos, "partition: expected partition number, got", pno); return nullptr; } last_pos = next_pos; @@ -446,8 +429,7 @@ Key *Partition::parseFromData(const std::string &data, int lineno, int *errors, } if(!parse_size_string(size_str, &size, &size_type)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "partition: invalid size", size_str); + output_error(pos, "partition: invalid size", size_str); return nullptr; } @@ -464,49 +446,44 @@ Key *Partition::parseFromData(const std::string &data, int lineno, int *errors, type = PReP; } else { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "partition: expected type code, got: " + typecode, + output_error(pos, "partition: expected type code, got: " + typecode, "valid type codes are: boot esp bios prep"); return nullptr; } } - return new Partition(script, lineno, block, part_no, size_type, size, type); + return new Partition(script, pos, block, part_no, size_type, size, type); } bool Partition::validate() const { #ifdef HAS_INSTALL_ENV if(script->options().test(InstallEnvironment)) { /* REQ: Runner.Validate.partition.Block */ - return is_block_device("partition", this->lineno(), this->device()); + return is_block_device("partition", where(), this->device()); } #endif /* HAS_INSTALL_ENV */ return true; } bool Partition::execute() const { - output_info("installfile:" + std::to_string(this->lineno()), - "partition: creating partition #" + std::to_string(_partno) + - " on " + _block); + output_info(pos, "partition: creating partition #" + + std::to_string(_partno) + " on " + _block); if(script->options().test(Simulate)) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: Not supported in Simulation mode"); + output_error(pos, "partition: Not supported in Simulation mode"); return true; } #ifdef HAS_INSTALL_ENV PedDevice *dev = ped_device_get(this->device().c_str()); if(dev == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: error opening device " + this->device()); + output_error(pos, "partition: error opening device " + this->device()); return false; } PedDisk *disk = ped_disk_new(dev); if(disk == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: error reading device " + this->device()); + output_error(pos, "partition: error reading device " + this->device()); return false; } @@ -516,8 +493,7 @@ bool Partition::execute() const { if(last == -1) last = 0; if(last != (this->partno() - 1)) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: consistency error on " + this->device(), + output_error(pos, "partition: consistency error on " + this->device(), "Partition #" + std::to_string(this->partno()) + " has been requested, but the disk has " + std::to_string(last) + " partitions"); @@ -531,8 +507,7 @@ bool Partition::execute() const { if(last > 0) { before = ped_disk_get_partition(disk, last); if(before == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: error reading partition table on " + + output_error(pos, "partition: error reading partition table on " + this->device()); ped_disk_destroy(disk); return false; @@ -558,8 +533,7 @@ bool Partition::execute() const { me = ped_partition_new(disk, PED_PARTITION_NORMAL, nullptr, start, start + size); if(me == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: error creating partition on " + + output_error(pos, "partition: error creating partition on " + this->device()); ped_disk_destroy(disk); return false; @@ -585,8 +559,7 @@ bool Partition::execute() const { int res = ped_disk_add_partition(disk, me, ped_constraint_any(dev)); if(res == 0) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: error adding partition to " + + output_error(pos, "partition: error adding partition to " + this->device()); ped_disk_destroy(disk); return false; @@ -594,8 +567,7 @@ bool Partition::execute() const { res = ped_disk_commit(disk); if(res != 1) { - output_error("installfile:" + std::to_string(this->lineno()), - "partition: error flushing changes to " + + output_error(pos, "partition: error flushing changes to " + this->device()); ped_disk_destroy(disk); return false; @@ -612,12 +584,12 @@ const static std::set<std::string> valid_fses = { }; -Key *Filesystem::parseFromData(const std::string &data, int lineno, - int *errors, int *, const Script *script) { +Key *Filesystem::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { if(std::count(data.begin(), data.end(), ' ') != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "fs: expected exactly two elements", + output_error(pos, "fs: expected exactly two elements", "syntax is: fs [device] [fstype]"); return nullptr; } @@ -629,8 +601,7 @@ Key *Filesystem::parseFromData(const std::string &data, int lineno, if(device.size() < 6 || device.compare(0, 5, "/dev/")) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "fs: element 1: expected device node", + output_error(pos, "fs: element 1: expected device node", "'" + device + "' is not a valid device node"); return nullptr; } @@ -640,8 +611,7 @@ Key *Filesystem::parseFromData(const std::string &data, int lineno, for(auto &&fs : valid_fses) fses += fs + " "; if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "fs: element 2: expected filesystem type", + output_error(pos, "fs: element 2: expected filesystem type", "valid filesystems are: " + fses); return nullptr; } @@ -662,7 +632,7 @@ Key *Filesystem::parseFromData(const std::string &data, int lineno, type = XFS; } - return new Filesystem(script, lineno, device, type); + return new Filesystem(script, pos, device, type); } bool Filesystem::validate() const { @@ -674,8 +644,7 @@ bool Filesystem::execute() const { std::string cmd; std::vector<std::string> args; - output_info("installfile:" + std::to_string(line), - "fs: creating new filesystem on " + _block); + output_info(pos, "fs: creating new filesystem on " + _block); switch(_type) { case Ext2: @@ -724,8 +693,7 @@ bool Filesystem::execute() const { #ifdef HAS_INSTALL_ENV if(run_command(cmd, args) != 0) { - output_error("installfile:" + std::to_string(line), - "fs: failed to create filesystem"); + output_error(pos, "fs: failed to create filesystem"); return false; } #endif /* HAS_INSTALL_ENV */ @@ -733,8 +701,8 @@ bool Filesystem::execute() const { } -Key *Mount::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Mount::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { std::string dev, where, opt; std::string::size_type where_pos, opt_pos; bool any_failure = false; @@ -743,8 +711,7 @@ Key *Mount::parseFromData(const std::string &data, int lineno, int *errors, if(spaces < 1 || spaces > 2) { if(errors) *errors += 1; /* Don't bother with any_failure, because this is immediately fatal. */ - output_error("installfile:" + std::to_string(lineno), - "mount: expected either 2 or 3 elements, got: " + + output_error(pos, "mount: expected either 2 or 3 elements, got: " + std::to_string(spaces), ""); return nullptr; } @@ -761,22 +728,20 @@ Key *Mount::parseFromData(const std::string &data, int lineno, int *errors, if(dev.compare(0, 4, "/dev")) { if(errors) *errors += 1; any_failure = true; - output_error("installfile:" + std::to_string(lineno), - "mount: element 1: expected device node", + output_error(pos, "mount: element 1: expected device node", "'" + dev + "' is not a valid device node"); } if(where[0] != '/') { if(errors) *errors += 1; any_failure = true; - output_error("installfile:" + std::to_string(lineno), - "mount: element 2: expected absolute path", + output_error(pos, "mount: element 2: expected absolute path", "'" + where + "' is not a valid absolute path"); } if(any_failure) return nullptr; - return new Mount(script, lineno, dev, where, opt); + return new Mount(script, pos, dev, where, opt); } bool Mount::validate() const { @@ -799,7 +764,7 @@ bool Mount::execute() const { else { fstype = blkid_get_tag_value(nullptr, "TYPE", this->device().c_str()); if(fstype == nullptr) { - output_error("installfile:" + std::to_string(this->lineno()), + output_error(pos, "mount: cannot determine filesystem type for device", this->device()); return false; @@ -807,8 +772,7 @@ bool Mount::execute() const { } #endif /* HAS_INSTALL_ENV */ - output_info("installfile:" + std::to_string(this->lineno()), - "mount: mounting " + this->device() + " on " + + output_info(pos, "mount: mounting " + this->device() + " on " + this->mountpoint()); if(script->options().test(Simulate)) { std::cout << "mount "; @@ -826,22 +790,20 @@ bool Mount::execute() const { if(!fs::exists(actual_mount, ec)) { fs::create_directory(actual_mount, ec); if(ec) { - output_error("installfile:" + std::to_string(this->lineno()), - "mount: failed to create target directory for " - + this->mountpoint(), ec.message()); + output_error(pos, "mount: failed to create target directory " + "for " + this->mountpoint(), ec.message()); return false; } } if(mount(this->device().c_str(), actual_mount.c_str(), fstype, 0, this->options().c_str()) != 0) { - output_warning("installfile:" + std::to_string(this->lineno()), - "mount: error mounting " + this->mountpoint() + + output_warning(pos, "mount: error mounting " + this->mountpoint() + "with options; retrying without", strerror(errno)); if(mount(this->device().c_str(), actual_mount.c_str(), fstype, 0, nullptr) != 0) { - output_error("installfile:" + std::to_string(this->lineno()), - "mount: error mounting " + this->mountpoint() + - "without options", strerror(errno)); + output_error(pos, "mount: error mounting " + + this->mountpoint() + "without options", + strerror(errno)); return false; } } @@ -851,8 +813,7 @@ bool Mount::execute() const { /* Handle fstab. We're guaranteed to have a /target since mount has * already ran and /target is the first mount done. */ - output_info("installfile:" + std::to_string(this->lineno()), - "mount: adding " + this->mountpoint() + " to /etc/fstab"); + output_info(pos, "mount: adding " + this->mountpoint() + " to /etc/fstab"); char pass = (this->mountpoint() == "/" ? '1' : '0'); const std::string fstab_opts = (this->options().empty() ? "defaults" : this->options()); @@ -872,8 +833,7 @@ bool Mount::execute() const { if(this->mountpoint() == "/") { fs::create_directory(script->targetDirectory() + "/etc", ec); if(ec) { - output_error("installfile:" + std::to_string(this->lineno()), - "mount: failed to create /etc for target", + output_error(pos, "mount: failed to create /etc for target", ec.message()); return false; } @@ -883,16 +843,14 @@ bool Mount::execute() const { #endif ec); if(ec) { - output_warning("installfile:" + std::to_string(this->lineno()), - "mount: failed to set permissions for target /etc", - ec.message()); + output_warning(pos, "mount: failed to set permissions for " + "target /etc", ec.message()); } } std::ofstream fstab_f(script->targetDirectory() + "/etc/fstab", std::ios::app); if(!fstab_f) { - output_error("installfile:" + std::to_string(this->lineno()), - "mount: failure opening /etc/fstab for writing"); + output_error(pos, "mount: failure opening /etc/fstab for writing"); return false; } fstab_f << this->device() << "\t" << this->mountpoint() << "\t" diff --git a/hscript/disk.hh b/hscript/disk.hh index e777c8f..2e3f18a 100644 --- a/hscript/disk.hh +++ b/hscript/disk.hh @@ -23,17 +23,17 @@ private: const std::string _block; const std::string _ident; - DiskId(const Script *_s, int _line, const std::string &my_block, - const std::string &my_i) : - Key(_s, _line), _block(my_block), _ident(my_i) {} + DiskId(const Script *_s, const ScriptLocation &_p, + const std::string &my_block, const std::string &my_i) : + Key(_s, _p), _block(my_block), _ident(my_i) {} public: /*! Retrieve the block device that this key identifies. */ const std::string device() const { return this->_block; } /*! Retrieve the identification for the block device. */ const std::string ident() const { return this->_ident; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; @@ -53,17 +53,17 @@ private: const std::string _block; const LabelType _type; - DiskLabel(const Script *_s, int _line, const std::string &_b, + DiskLabel(const Script *_s, const ScriptLocation &_p, const std::string &_b, const LabelType &_t) : - Key(_s, _line), _block(_b), _type(_t) {} + Key(_s, _p), _block(_b), _type(_t) {} public: /*! Retrieve the block device that this key identifies. */ const std::string device() const { return this->_block; } /*! Retrieve the type of disklabel for the block device. */ LabelType type() const { return this->_type; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; @@ -100,9 +100,10 @@ private: const uint64_t _size; const PartitionType _type; - Partition(const Script *_sc, int _line, const std::string &_b, const int _p, - const SizeType _st, const uint64_t _s, const PartitionType _pt) : - Key(_sc, _line), _block(_b), _partno(_p), _size_type(_st), _size(_s), + Partition(const Script *_sc, const ScriptLocation &_pos, + const std::string &_b, const int _p, const SizeType _st, + const uint64_t _s, const PartitionType _pt) : + Key(_sc, _pos), _block(_b), _partno(_p), _size_type(_st), _size(_s), _type(_pt) {} public: /*! Retrieve the block device that this key identifies. */ @@ -116,8 +117,8 @@ public: /*! Retrieve the Type Code of this partition, if any. */ PartitionType type() const { return this->_type; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; @@ -127,27 +128,27 @@ private: const std::string _block; const std::string _pw; - Encrypt(const Script *_s, int _line, const std::string &_b, - const std::string &_p) : Key(_s, _line), _block(_b), _pw(_p) {} + Encrypt(const Script *_s, const ScriptLocation &_pos, const std::string &_b, + const std::string &_p) : Key(_s, _pos), _block(_b), _pw(_p) {} public: /*! Retrieve the block device that this key encrypts. */ const std::string device() const { return this->_block; } /*! Retrieve the passphrase used to encrypt the block device. */ const std::string passphrase() const { return this->_pw; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; class LVMPhysical : public StringKey { private: - LVMPhysical(const Script *_s, int _line, const std::string &_d) : - StringKey(_s, _line, _d) {} + LVMPhysical(const Script *_s, const ScriptLocation &_p, + const std::string &_d) : StringKey(_s, _p, _d) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool execute() const override; }; @@ -156,17 +157,17 @@ private: const std::string _pv; const std::string _vgname; - LVMGroup(const Script *_s, int _line, const std::string &_p, - const std::string &_v) : - Key(_s, _line), _pv(_p), _vgname(_v) {} + LVMGroup(const Script *_s, const ScriptLocation &_pos, + const std::string &_p, const std::string &_v) : + Key(_s, _pos), _pv(_p), _vgname(_v) {} public: /*! Retrieve the physical volume where this volume group will reside. */ const std::string pv() const { return this->_pv; } /*! Retrieve the name of this volume group. */ const std::string name() const { return this->_vgname; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; /*! Determine if the PV passed is a real one. */ bool test_pv() const; @@ -180,9 +181,10 @@ private: const SizeType _size_type; const uint64_t _size; - LVMVolume(const Script *_sc, int _line, const std::string &_v, - const std::string &_n, SizeType _t, uint64_t _s) : - Key(_sc, _line), _vg(_v), _lvname(_n), _size_type(_t), _size(_s) {} + LVMVolume(const Script *_sc, const ScriptLocation &_pos, + const std::string &_v, const std::string &_n, SizeType _t, + uint64_t _s) : + Key(_sc, _pos), _vg(_v), _lvname(_n), _size_type(_t), _size(_s) {} public: /*! Retrieve the volume group to which this volume belongs. */ const std::string vg() const { return this->_vg; } @@ -193,8 +195,8 @@ public: /*! Retrieve the size of this volume. */ uint64_t size() const { return this->_size; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; @@ -214,16 +216,17 @@ private: const std::string _block; FilesystemType _type; - Filesystem(const Script *_s, int _line, const std::string &_b, - FilesystemType _t) : Key(_s, _line), _block(_b), _type(_t) {} + Filesystem(const Script *_s, const ScriptLocation &_pos, + const std::string &_b, FilesystemType _t) : + Key(_s, _pos), _block(_b), _type(_t) {} public: /*! Retrieve the block device on which to create the filesystem. */ const std::string device() const { return this->_block; } /*! Retreive the type of filesystem to create. */ FilesystemType fstype() const { return this->_type; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; @@ -234,9 +237,10 @@ private: const std::string _mountpoint; const std::string _opts; - Mount(const Script *_s, int _line, const std::string &my_block, - const std::string &my_mountpoint, const std::string &my_opts = "") : - Key(_s, _line), _block(my_block), _mountpoint(my_mountpoint), + Mount(const Script *_s, const ScriptLocation &_pos, + const std::string &my_block, const std::string &my_mountpoint, + const std::string &my_opts = "") : + Key(_s, _pos), _block(my_block), _mountpoint(my_mountpoint), _opts(my_opts) {} public: /*! Retrieve the block device to which this mount pertains. */ @@ -246,8 +250,8 @@ public: /*! Retrieve the mount options for this mount, if any. */ const std::string options() const { return this->_opts; } - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; diff --git a/hscript/disk_lvm.cc b/hscript/disk_lvm.cc index 077d533..e23f063 100644 --- a/hscript/disk_lvm.cc +++ b/hscript/disk_lvm.cc @@ -25,21 +25,20 @@ using namespace Horizon::Keys; bool parse_size_string(const std::string &, uint64_t *, SizeType *); -Key *LVMPhysical::parseFromData(const std::string &data, int lineno, - int *errors, int *, const Script *script) { +Key *LVMPhysical::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { if(data.size() < 6 || data.substr(0, 5) != "/dev/") { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_pv: expected an absolute path to a block device"); + output_error(pos, "lvm_pv: expected an absolute path to a device"); return nullptr; } - return new LVMPhysical(script, lineno, data); + return new LVMPhysical(script, pos, data); } bool LVMPhysical::execute() const { - output_info("installfile:" + std::to_string(line), - "lvm_pv: creating physical volume on " + _value); + output_info(pos, "lvm_pv: creating physical volume on " + _value); if(script->options().test(Simulate)) { std::cout << "pvcreate --force " << _value << std::endl; @@ -54,8 +53,7 @@ bool LVMPhysical::execute() const { } if(run_command("pvcreate", {"--force", _value}) != 0) { - output_error("installfile:" + std::to_string(line), - "lvm_pv: failed to create physical volume on " + _value); + output_error(pos, "lvm_pv: failed to create physical volume", _value); return false; } #endif /* HAS_INSTALL_ENV */ @@ -116,13 +114,12 @@ bool is_valid_lvm_lv_name(const std::string &name) { } -Key *LVMGroup::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *LVMGroup::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { std::string::size_type space = data.find_first_of(' '); if(space == std::string::npos || data.size() == space + 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_vg: expected exactly two elements", + output_error(pos, "lvm_vg: expected exactly two elements", "syntax is lvm_vg [pv_block] [name-of-vg]"); return nullptr; } @@ -132,19 +129,17 @@ Key *LVMGroup::parseFromData(const std::string &data, int lineno, int *errors, if(pv.length() < 6 || pv.substr(0, 5) != "/dev/") { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_vg: expected absolute path to block device"); + output_error(pos, "lvm_vg: expected absolute path to block device"); return nullptr; } if(!is_valid_lvm_name(name)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_vg: invalid volume group name"); + output_error(pos, "lvm_vg: invalid volume group name"); return nullptr; } - return new LVMGroup(script, lineno, pv, name); + return new LVMGroup(script, pos, pv, name); } bool LVMGroup::validate() const { @@ -173,20 +168,19 @@ bool LVMGroup::test_pv() const { /*! Determine if a named Volume Group currently exists on a LVM PV. * @param vg The name of the Volume Group. * @param pv The path to the LVM physical volume. - * @param line The installfile line number. + * @param pos The location of the line. * @param msgs Whether or not to print messages. * @returns true if +vg+ appears on +pv+; false otherwise. */ bool does_vg_exist_on_pv(const std::string &vg, const std::string &pv, - long line, bool msgs) { + const Horizon::ScriptLocation &pos, bool msgs) { bool success = false; const std::string pv_command("pvs --noheadings -o vg_name " + pv + " 2>/dev/null"); FILE *pvs = popen(pv_command.c_str(), "r"); if(pvs == nullptr) { - if(msgs) output_error("installfile:" + std::to_string(line), - "lvm_vg: can't determine if vg is duplicate"); + if(msgs) output_error(pos, "lvm_vg: can't determine if vg is duplicate"); return false; } @@ -201,9 +195,8 @@ bool does_vg_exist_on_pv(const std::string &vg, const std::string &pv, * also, use vg.size() to avoid comparing the terminating \n */ if(static_cast<unsigned long>(read_bytes) != vg.size() + 3 || strncmp(buf + 2, vg.c_str(), vg.size())) { - if(msgs) output_error("installfile:" + std::to_string(line), - "lvm_vg: volume group already exists and is " - "not using the specified physical volume"); + if(msgs) output_error(pos, "lvm_vg: volume group already exists and " + "is not using the specified physical volume"); } else { /* the VG already exists and uses the specified PV - we're good */ success = true; @@ -215,8 +208,7 @@ bool does_vg_exist_on_pv(const std::string &vg, const std::string &pv, #endif /* HAS_INSTALL_ENV */ bool LVMGroup::execute() const { - output_info("installfile:" + std::to_string(line), - "lvm_vg: creating volume group " + _vgname + " on " + _pv); + output_info(pos, "lvm_vg: creating volume group " + _vgname + " on " + _pv); if(script->options().test(Simulate)) { std::cout << "vgcreate " << _vgname << " " << _pv << std::endl; @@ -226,16 +218,15 @@ bool LVMGroup::execute() const { #ifdef HAS_INSTALL_ENV /* REQ: Runner.Execute.lvm_vg.Duplicate */ if(fs::exists("/dev/" + _vgname)) { - return does_vg_exist_on_pv(_vgname, _pv, line, true); + return does_vg_exist_on_pv(_vgname, _pv, pos, true); } if(run_command("vgcreate", {_vgname, _pv}) != 0) { - if(does_vg_exist_on_pv(_vgname, _pv, line, true)) { + if(does_vg_exist_on_pv(_vgname, _pv, pos, true)) { return true; } - output_error("installfile:" + std::to_string(line), - "lvm_vg: failed to create volume group " + _vgname); + output_error(pos, "lvm_vg: failed to create volume group " + _vgname); return false; } #endif /* HAS_INSTALL_ENV */ @@ -243,8 +234,9 @@ bool LVMGroup::execute() const { } -Key *LVMVolume::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *LVMVolume::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { std::string vg, name, size_str; std::string::size_type name_start, size_start; SizeType size_type; @@ -253,8 +245,7 @@ Key *LVMVolume::parseFromData(const std::string &data, int lineno, int *errors, long spaces = std::count(data.cbegin(), data.cend(), ' '); if(spaces != 2) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_lv: expected 3 elements, got: " + + output_error(pos, "lvm_lv: expected 3 elements, got: " + std::to_string(spaces), "syntax is: lvm_lv [vg] [name] [size]"); return nullptr; @@ -268,26 +259,23 @@ Key *LVMVolume::parseFromData(const std::string &data, int lineno, int *errors, if(!is_valid_lvm_name(vg)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_lv: invalid volume group name"); + output_error(pos, "lvm_lv: invalid volume group name"); return nullptr; } if(!is_valid_lvm_lv_name(name)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_lv: invalid volume name"); + output_error(pos, "lvm_lv: invalid volume name"); return nullptr; } if(!parse_size_string(size_str, &size, &size_type)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "lvm_lv: invalid size", size_str); + output_error(pos, "lvm_lv: invalid size", size_str); return nullptr; } - return new LVMVolume(script, lineno, vg, name, size_type, size); + return new LVMVolume(script, pos, vg, name, size_type, size); } bool LVMVolume::validate() const { @@ -295,8 +283,7 @@ bool LVMVolume::validate() const { } bool LVMVolume::execute() const { - output_info("installfile:" + std::to_string(line), - "lvm_lv: creating volume " + _lvname + " on " + _vg); + output_info(pos, "lvm_lv: creating volume " + _lvname + " on " + _vg); std::string param, size; switch(_size_type) { @@ -322,8 +309,7 @@ bool LVMVolume::execute() const { #ifdef HAS_INSTALL_ENV if(run_command("lvcreate", {param, size, "-n", _lvname, _vg}) != 0) { - output_error("installfile:" + std::to_string(line), - "lvm_lv: failed to create logical volume " + _lvname); + output_error(pos, "lvm_lv: failed to create logical volume " + _lvname); return false; } #endif /* HAS_INSTALL_ENV */ diff --git a/hscript/key.cc b/hscript/key.cc index a2d0342..49d095e 100644 --- a/hscript/key.cc +++ b/hscript/key.cc @@ -19,7 +19,7 @@ Horizon::Keys::Key::~Key() { } bool Horizon::Keys::BooleanKey::parse(const std::string &what, - const std::string &where, + const ScriptLocation &where, const std::string &key, bool *out) { std::string lower(what.size(), 0); std::transform(what.begin(), what.end(), lower.begin(), ::tolower); diff --git a/hscript/key.hh b/hscript/key.hh index 20a461e..5bccb89 100644 --- a/hscript/key.hh +++ b/hscript/key.hh @@ -28,14 +28,14 @@ protected: /*! The script that owns this Key. */ const Script *script; /*! The line number where this Key appeared. */ - long line; - Key(const Script *_s, long _line) : script(_s), line(_line) {} + const ScriptLocation pos; + Key(const Script *_s, const ScriptLocation &_p) : script{_s}, pos{_p} {} public: virtual ~Key(); /*! Create the Key object with the specified data as the entire value. * @param data The value associated with the key. - * @param lineno The line number where the key occurs. + * @param pos The location where the key occurs. * @param errors Output variable: if not nullptr, ++ on each error. * @param warnings Output variable: if not nullptr, ++ on each warning. * @returns nullptr if data is unparsable, otherwise a pointer to a Key. @@ -43,8 +43,9 @@ public: #define UNUSED __attribute__((unused)) /* LCOV_EXCL_START */ static Key *parseFromData(const std::string &data UNUSED, - int lineno UNUSED, int *errors UNUSED, - int *warnings UNUSED, const Script *s UNUSED) { + const ScriptLocation &pos UNUSED, + int *errors UNUSED, int *warnings UNUSED, + const Script *s UNUSED) { return nullptr; } /* LCOV_EXCL_STOP */ @@ -58,7 +59,7 @@ public: */ virtual bool execute() const = 0; - long lineno() const { return this->line; } + const ScriptLocation where() const { return this->pos; } }; @@ -71,8 +72,8 @@ public: class BooleanKey : public Key { protected: const bool value; - BooleanKey(const Script *_s, int _line, bool my_value) : Key(_s, _line), - value(my_value) {} + BooleanKey(const Script *_s, const ScriptLocation &_p, bool my_value) : + Key{_s, _p}, value{my_value} {} /*! Parse a string into a boolean. * @param what The string to attempt parsing. @@ -81,7 +82,7 @@ protected: * @param out Output variable: will contain the value. * @returns true if value is parsed successfully, false otherwise. */ - static bool parse(const std::string &what, const std::string &where, + static bool parse(const std::string &what, const ScriptLocation &where, const std::string &key, bool *out); public: /*! Determines if the Key is set or not. @@ -98,8 +99,8 @@ public: class StringKey : public Key { protected: const std::string _value; - StringKey(const Script *_s, int _line, const std::string &my_str) : - Key(_s, _line), _value(my_str) {} + StringKey(const Script *_s, const ScriptLocation &_p, + const std::string &my_str) : Key{_s, _p}, _value{my_str} {} public: /*! Retrieve the value of this key. */ diff --git a/hscript/meta.cc b/hscript/meta.cc index d43e58e..fd3e69b 100644 --- a/hscript/meta.cc +++ b/hscript/meta.cc @@ -25,17 +25,16 @@ using namespace Horizon::Keys; -Key *Hostname::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Hostname::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { std::string valid_chars("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_."); if(data.find_first_not_of(valid_chars) != std::string::npos) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "hostname: expected machine or DNS name", + output_error(pos, "hostname: expected machine or DNS name", "'" + data + "' is not a valid hostname"); return nullptr; } - return new Hostname(script, lineno, data); + return new Hostname(script, pos, data); } bool Hostname::validate() const { @@ -45,14 +44,12 @@ bool Hostname::validate() const { if(!isalpha(this->_value[0])) { any_failure = true; - output_error("installfile:" + std::to_string(this->lineno()), - "hostname: must start with alphabetical character"); + output_error(pos, "hostname: must start with alphabetical character"); } if(this->_value.size() > 320) { any_failure = true; - output_error("installfile:" + std::to_string(this->lineno()), - "hostname: value too long", + output_error(pos, "hostname: value too long", "valid host names must be less than 320 characters"); } @@ -63,8 +60,7 @@ bool Hostname::validate() const { } if(next_dot - last_dot > 64) { any_failure = true; - output_error("installfile:" + std::to_string(this->lineno()), - "hostname: component too long", + output_error(pos, "hostname: component too long", "each component must be less than 64 characters"); } last_dot = next_dot; @@ -89,8 +85,7 @@ bool Hostname::execute() const { } /* Runner.Execute.hostname. */ - output_info("installfile:" + std::to_string(this->lineno()), - "hostname: set hostname to '" + actual + "'"); + output_info(pos, "hostname: set hostname to '" + actual + "'"); if(script->options().test(Simulate)) { std::cout << "hostname " << actual << std::endl; } @@ -99,17 +94,15 @@ bool Hostname::execute() const { /* no-op; we don't want to set the image builder's hostname */ } 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))); + output_error(pos, "hostname: failed to set host name", + ::strerror(errno)); return false; } } #endif /* HAS_INSTALL_ENV */ /* Runner.Execute.hostname.Write. */ - output_info("installfile:" + std::to_string(this->lineno()), - "hostname: write '" + actual + "' to /etc/hostname"); + output_info(pos, "hostname: write '" + actual + "' to /etc/hostname"); if(script->options().test(Simulate)) { std::cout << "printf '%s' " << actual << " > " << script->targetDirectory() << "/etc/hostname" << std::endl; @@ -119,8 +112,7 @@ bool Hostname::execute() const { std::ofstream hostname_f(script->targetDirectory() + "/etc/hostname", std::ios_base::trunc); if(!hostname_f) { - output_error("installfile:" + std::to_string(this->lineno()), - "hostname: could not open /etc/hostname for writing"); + output_error(pos, "hostname: could not open /etc/hostname"); return false; } hostname_f << actual; @@ -131,8 +123,7 @@ bool Hostname::execute() const { * terminates the nodename. */ if(dot != std::string::npos && this->_value.length() > dot + 1) { const std::string domain(this->_value.substr(dot + 1)); - output_info("installfile:" + std::to_string(this->lineno()), - "hostname: set domain name '" + domain + "'"); + output_info(pos, "hostname: set domain name '" + domain + "'"); if(script->options().test(Simulate)) { std::cout << "mkdir -p " << script->targetDirectory() << "/etc/conf.d" << std::endl; @@ -147,17 +138,15 @@ bool Hostname::execute() const { fs::create_directory(script->targetDirectory() + "/etc/conf.d", ec); if(ec) { - output_error("installfile:" + std::to_string(line), - "hostname: could not create /etc/conf.d " + output_error(pos, "hostname: could not create /etc/conf.d " "directory", ec.message()); } } std::ofstream net_conf_f(script->targetDirectory() + "/etc/conf.d/net", std::ios_base::app); if(!net_conf_f) { - output_error("installfile:" + std::to_string(this->lineno()), - "hostname: could not open /etc/conf.d/net for " - "writing"); + output_error(pos, "hostname: could not open /etc/conf.d/net " + "for writing"); return false; } net_conf_f << "dns_domain_lo=\"" << domain << "\"" << std::endl; @@ -179,29 +168,26 @@ static std::set<std::string> valid_arches = { }; -Key *Arch::parseFromData(const std::string &data, int lineno, int *errors, - int *warnings, const Script *script) { +Key *Arch::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *warnings, const Script *script) { if(data.find_first_not_of("abcdefghijklmnopqrstuvwyxz1234567890_") != std::string::npos) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "arch: expected CPU architecture name", + output_error(pos, "arch: expected CPU architecture name", "'" + data + "' is not a valid CPU architecture name"); return nullptr; } if(valid_arches.find(data) == valid_arches.end()) { if(warnings) *warnings += 1; - output_warning("installfile:" + std::to_string(lineno), - "arch: unknown CPU architecture '" + data + "'"); + output_warning(pos, "arch: unknown CPU architecture '" + data + "'"); } - return new Arch(script, lineno, data); + return new Arch(script, pos, data); } bool Arch::execute() const { - output_info("installfile:" + std::to_string(line), - "arch: setting system CPU architecture to " + value()); + output_info(pos, "arch: setting system CPU architecture to " + value()); if(script->options().test(Simulate)) { std::cout << "printf '" << this->value() << "\\" << "n'" @@ -214,8 +200,7 @@ bool Arch::execute() const { std::ofstream arch_f(script->targetDirectory() + "/etc/apk/arch", std::ios_base::trunc); if(!arch_f) { - output_error("installfile:" + std::to_string(line), - "arch: cannot write target CPU architecture information"); + output_error(pos, "arch: could not write target CPU architecture"); return false; } @@ -228,9 +213,9 @@ bool Arch::execute() const { static std::regex valid_pkg("[0-9A-Za-z+_.-]*((>?<|[<>]?=|[~>])[0-9A-Za-z-_.]+)?"); -Key *PkgInstall::parseFromData(const std::string &data, int lineno, - int *errors, int *warnings, - const Script *script) { +Key *PkgInstall::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, + int *warnings, const Script *script) { std::string next_pkg; std::istringstream stream(data); std::set<std::string> all_pkgs; @@ -238,21 +223,19 @@ Key *PkgInstall::parseFromData(const std::string &data, int lineno, while(stream >> next_pkg) { if(!std::regex_match(next_pkg, valid_pkg)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "pkginstall: expected package name", + output_error(pos, "pkginstall: expected package name", "'" + next_pkg + "' is not a valid package or atom"); return nullptr; } if(all_pkgs.find(next_pkg) != all_pkgs.end()) { if(warnings) *warnings += 1; - output_warning("installfile:" + std::to_string(lineno), - "pkginstall: package '" + next_pkg + + output_warning(pos, "pkginstall: package '" + next_pkg + "' is already in the target package set"); continue; } all_pkgs.insert(next_pkg); } - return new PkgInstall(script, lineno, all_pkgs); + return new PkgInstall(script, pos, all_pkgs); } @@ -298,13 +281,12 @@ const std::set<std::string> valid_langs = { }; -Key *Language::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Language::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { if(data.length() < 2 || valid_langs.find(data.substr(0, 2)) == valid_langs.end()) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "language: invalid language specified", + output_error(pos, "language: invalid language specified", "language must be a valid ISO 639-1 language code"); return nullptr; } @@ -314,8 +296,7 @@ Key *Language::parseFromData(const std::string &data, int lineno, int *errors, /* data[1] is . if language is C.UTF-8 */ if(data[2] != '_' && data[1] != '.') { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "language: invalid language specified", + output_error(pos, "language: invalid language specified", "language must be a valid ISO 639-1 language code, " "optionally followed by '_' and a country code"); return nullptr; @@ -325,20 +306,17 @@ Key *Language::parseFromData(const std::string &data, int lineno, int *errors, std::string::size_type dot = data.find_first_of('.'); if(dot != std::string::npos && data.substr(dot+1, 5) != "UTF-8") { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "language: invalid language specified", + output_error(pos, "language: invalid language specified", "you cannot specify a non-UTF-8 codeset"); return nullptr; } } - return new Language(script, lineno, data); + return new Language(script, pos, data); } bool Language::execute() const { - output_info("installfile:" + std::to_string(this->lineno()), - "language: setting default system language to " + - this->value()); + output_info(pos, "language: setting default system language to " + _value); if(script->options().test(Simulate)) { std::cout << "printf '#!/bin/sh\\" << "nexport LANG=\"%s\"\\" << "n' " @@ -355,9 +333,7 @@ bool Language::execute() const { std::ofstream lang_f(lang_path, std::ios_base::trunc); error_code ec; if(!lang_f) { - output_error("installfile:" + std::to_string(this->lineno()), - "language: could not open /etc/profile.d/00-language.sh " - "for writing"); + output_error(pos, "language: could not open profile for writing"); return false; } lang_f << "#!/bin/sh" << std::endl << "export LANG=\"" @@ -366,9 +342,8 @@ bool Language::execute() const { fs::permissions(lang_path, rwxr_xr_x, ec); if(ec) { - output_error("installfile:" + std::to_string(this->lineno()), - "language: could not set /etc/profile.d/00-language.sh " - "as executable", ec.message()); + output_error(pos, "language: could not set profile script " + "executable", ec.message()); return false; } #endif /* HAS_INSTALL_ENV */ @@ -378,16 +353,15 @@ bool Language::execute() const { #include "util/keymaps.hh" -Key *Keymap::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Keymap::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { if(valid_keymaps.find(data) == valid_keymaps.end()) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "keymap: invalid keymap specified"); + output_error(pos, "keymap: invalid keymap specified"); return nullptr; } - return new Keymap(script, lineno, data); + return new Keymap(script, pos, data); } bool Keymap::validate() const { @@ -407,8 +381,7 @@ XKBOPTIONS=\n\ BACKSPACE=guess" ); - output_info("installfile:" + std::to_string(line), - "keymap: setting system keyboard map to " + _value); + output_info(pos, "keymap: setting system keyboard map to " + _value); if(script->options().test(Simulate)) { std::cout << "cat >" << script->targetDirectory() @@ -422,8 +395,7 @@ BACKSPACE=guess" std::ofstream keyconf(script->targetDirectory() + "/etc/default/keyboard", std::ios_base::trunc); if(!keyconf) { - output_error("installfile:" + std::to_string(line), - "keymap: cannot write target keyboard configuration"); + output_error(pos, "keymap: cannot write target keyboard configuration"); return false; } @@ -433,32 +405,29 @@ BACKSPACE=guess" } -Key *Firmware::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Firmware::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { bool value; - if(!BooleanKey::parse(data, "installfile:" + std::to_string(lineno), - "firmware", &value)) { + if(!BooleanKey::parse(data, pos, "firmware", &value)) { if(errors) *errors += 1; return nullptr; } if(value) { #ifdef NON_LIBRE_FIRMWARE - output_warning("installfile:" + std::to_string(lineno), - "firmware: You have requested non-libre firmware. " + output_warning(pos, "firmware: You have requested non-libre firmware. " "This may cause security issues, system instability, " "and many other issues. You should not enable this " "option unless your system absolutely requires it."); #else if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "firmware: You have requested non-libre firmware, " + output_error(pos, "firmware: You have requested non-libre firmware, " "but this version of Horizon does not support " "non-libre firmware.", "Installation cannot proceed."); return nullptr; #endif } - return new Firmware(script, lineno, value); + return new Firmware(script, pos, value); } /* LCOV_EXCL_START */ @@ -469,36 +438,32 @@ bool Firmware::execute() const { /* LCOV_EXCL_STOP */ -Key *Timezone::parseFromData(const std::string &data, int lineno, int *errors, - int *warnings, const Script *script) { +Key *Timezone::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *warnings, const Script *script) { if(data.find_first_of(" .\\") != std::string::npos || data[0] == '/') { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "timezone: invalid timezone name"); + output_error(pos, "timezone: invalid timezone name"); return nullptr; } if(access("/usr/share/zoneinfo", X_OK) != 0) { if(warnings) *warnings += 1; - output_warning("installfile:" + std::to_string(lineno), - "timezone: can't determine validity of timezone", + output_warning(pos, "timezone: can't determine validity of timezone", "zoneinfo data is missing or inaccessible"); } else { std::string zi_path = "/usr/share/zoneinfo/" + data; if(access(zi_path.c_str(), F_OK) != 0) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "timezone: unknown timezone '" + data + "'"); + output_error(pos, "timezone: unknown timezone '" + data + "'"); return nullptr; } } - return new Timezone(script, lineno, data); + return new Timezone(script, pos, data); } bool Timezone::execute() const { - output_info("installfile:" + std::to_string(this->lineno()), - "timezone: setting system timezone to " + this->value()); + output_info(pos, "timezone: setting system timezone to " + this->value()); if(script->options().test(Simulate)) { /* If the target doesn't have tzdata installed, copy the zoneinfo from @@ -523,8 +488,7 @@ bool Timezone::execute() const { fs::create_symlink(zi_path, target_lt, ec); if(ec) { - output_error("installfile:" + std::to_string(this->lineno()), - "timezone: failed to create symbolic link", + output_error(pos, "timezone: failed to create symbolic link", ec.message()); return false; } @@ -534,8 +498,7 @@ bool Timezone::execute() const { * file from the Horizon environment to the target. */ fs::copy_file(zi_path, target_lt, ec); if(ec) { - output_error("installfile:" + std::to_string(this->lineno()), - "timezone: failed to prepare target environment", + output_error(pos, "timezone: failed to prepare target environment", ec.message()); return false; } @@ -547,15 +510,15 @@ bool Timezone::execute() const { } -Key *Repository::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Repository::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { if(data.empty() || (data[0] != '/' && data.compare(0, 4, "http"))) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "repository: must be absolute path or HTTP(S) URL"); + output_error(pos, "repository: must be absolute path or HTTP(S) URL"); return nullptr; } - return new Repository(script, lineno, data); + return new Repository(script, pos, data); } bool Repository::validate() const { @@ -565,8 +528,7 @@ bool Repository::validate() const { bool Repository::execute() const { /* Runner.Execute.repository. */ - output_info("installfile:" + std::to_string(this->lineno()), - "repository: write '" + this->value() + + output_info(pos, "repository: write '" + this->value() + "' to /etc/apk/repositories"); if(script->options().test(Simulate)) { std::cout << "echo '" << this->value() @@ -579,8 +541,7 @@ bool Repository::execute() const { std::ofstream repo_f(script->targetDirectory() + "/etc/apk/repositories", std::ios_base::app); if(!repo_f) { - output_error("installfile:" + std::to_string(this->lineno()), - "repository: could not open /etc/apk/repositories " + output_error(pos, "repository: could not open /etc/apk/repositories " "for writing"); return false; } @@ -594,16 +555,16 @@ bool Repository::execute() const { } -Key *SigningKey::parseFromData(const std::string &data, int lineno, +Key *SigningKey::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, const Script *script) { if(data.empty() || (data[0] != '/' && data.compare(0, 8, "https://"))) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "signingkey: must be absolute path or HTTPS URL"); + output_error(pos, "signingkey: must be absolute path or HTTPS URL"); return nullptr; } - return new SigningKey(script, lineno, data); + return new SigningKey(script, pos, data); } bool SigningKey::validate() const { @@ -617,8 +578,7 @@ bool SigningKey::execute() const { const std::string target_dir(script->targetDirectory() + "/etc/apk/keys/"); const std::string target(target_dir + name); - output_info("installfile:" + std::to_string(line), - "signingkey: trusting " + name + " for repository signing"); + output_info(pos, "signingkey: trusting " + name + " for APK signing"); if(script->options().test(Simulate)) { std::cout << "mkdir -p " << target_dir << std::endl; @@ -635,9 +595,8 @@ bool SigningKey::execute() const { if(!fs::exists(target_dir)) { fs::create_directory(target_dir, ec); if(ec) { - output_error("installfile:" + std::to_string(line), - "signingkey: could not initialise target repository " - "keys directory", ec.message()); + output_error(pos, "signingkey: could not initialise target " + "repository keys directory", ec.message()); return false; } } @@ -645,8 +604,7 @@ bool SigningKey::execute() const { if(_value[0] == '/') { fs::copy_file(_value, target, fs_overwrite, ec); if(ec) { - output_error("installfile:" + std::to_string(line), - "signingkey: could not copy signing key to target", + output_error(pos, "signingkey: could not copy key to target", ec.message()); return false; } @@ -657,26 +615,25 @@ bool SigningKey::execute() const { return true; /* LCOV_EXCL_LINE */ } -Key *SvcEnable::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *SvcEnable::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { const static std::string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.-_"; if(data.find_first_not_of(valid_chars) != std::string::npos) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "svcenable: invalid service name", data); + output_error(pos, "svcenable: invalid service name", data); return nullptr; } - return new SvcEnable(script, lineno, data); + return new SvcEnable(script, pos, data); } bool SvcEnable::execute() const { const std::string target = script->targetDirectory() + "/etc/runlevels/default/" + _value; const std::string initd = "/etc/init.d/" + _value; - output_info("installfile:" + std::to_string(line), - "svcenable: enabling service " + _value); + output_info(pos, "svcenable: enabling service " + _value); if(script->options().test(Simulate)) { std::cout << "ln -s " << initd << " " << target << std::endl; @@ -686,14 +643,12 @@ bool SvcEnable::execute() const { #ifdef HAS_INSTALL_ENV error_code ec; if(!fs::exists(script->targetDirectory() + initd, ec)) { - output_warning("installfile:" + std::to_string(line), - "svcenable: service '" + _value + "' may be missing"); + output_warning(pos, "svcenable: missing service", _value); } fs::create_symlink(initd, target, ec); if(ec) { - output_error("installfile:" + std::to_string(line), - "svcenable: could not enable service " + _value, + output_error(pos, "svcenable: could not enable service " + _value, ec.message()); return false; } diff --git a/hscript/meta.hh b/hscript/meta.hh index d522bdf..b885e99 100644 --- a/hscript/meta.hh +++ b/hscript/meta.hh @@ -22,34 +22,34 @@ namespace Keys { class Hostname : public StringKey { private: - Hostname(const Script *_s, int _line, const std::string my_name) : - StringKey(_s, _line, my_name) {} + Hostname(const Script *_s, const ScriptLocation &_pos, + const std::string my_name) : StringKey(_s, _pos, my_name) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool validate() const override; bool execute() const override; }; class Arch : public StringKey { private: - Arch(const Script *_s, int _line, const std::string &arch) : - StringKey(_s, _line, arch) {} + Arch(const Script *_s, const ScriptLocation &_p, const std::string &arch) : + StringKey(_s, _p, arch) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool execute() const override; }; class PkgInstall : public Key { private: const std::set<std::string> _pkgs; - PkgInstall(const Script *_s, int _line, - const std::set<std::string> my_pkgs) : Key(_s, _line), + PkgInstall(const Script *_s, const ScriptLocation &_pos, + const std::set<std::string> my_pkgs) : Key(_s, _pos), _pkgs(my_pkgs) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); const std::set<std::string> packages() const { return _pkgs; } bool validate() const override; bool execute() const override; @@ -57,74 +57,74 @@ public: class Language : public StringKey { private: - Language(const Script *_s, int _line, const std::string &my_lang) : - StringKey(_s, _line, my_lang) {} + Language(const Script *_s, const ScriptLocation &_pos, + const std::string &my_lang) : StringKey(_s, _pos, my_lang) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool execute() const override; }; class Keymap : public StringKey { private: - Keymap(const Script *_s, int _line, const std::string &keymap) : - StringKey(_s, _line, keymap) {} + Keymap(const Script *_s, const ScriptLocation &_pos, + const std::string &keymap) : StringKey(_s, _pos, keymap) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool validate() const override; bool execute() const override; }; class Firmware : public BooleanKey { private: - Firmware(const Script *_s, int _line, bool _value) : - BooleanKey(_s, _line, _value) {} + Firmware(const Script *_s, const ScriptLocation &_pos, bool _value) : + BooleanKey(_s, _pos, _value) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool execute() const override; }; class Timezone : public StringKey { private: - Timezone(const Script *_s, int _line, const std::string &my_zone) : - StringKey(_s, _line, my_zone) {} + Timezone(const Script *_s, const ScriptLocation &_pos, + const std::string &my_zone) : StringKey(_s, _pos, my_zone) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool execute() const override; }; class Repository : public StringKey { private: - Repository(const Script *_s, int _line, const std::string &my_url) : - StringKey(_s, _line, my_url) {} + Repository(const Script *_s, const ScriptLocation &_pos, + const std::string &my_url) : StringKey(_s, _pos, my_url) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool validate() const override; bool execute() const override; }; class SigningKey : public StringKey { private: - SigningKey(const Script *_s, int _line, const std::string &_path) : - StringKey(_s, _line, _path) {} + SigningKey(const Script *_s, const ScriptLocation &_pos, + const std::string &_path) : StringKey(_s, _pos, _path) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool validate() const override; bool execute() const override; }; class SvcEnable : public StringKey { private: - SvcEnable(const Script *_s, int _line, const std::string &_svc) : - StringKey(_s, _line, _svc) {} + SvcEnable(const Script *_s, const ScriptLocation &_pos, + const std::string &_svc) : StringKey(_s, _pos, _svc) {} public: - static Key *parseFromData(const std::string &, int, int *, int *, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); bool execute() const override; }; diff --git a/hscript/network.cc b/hscript/network.cc index 445ccbb..1e915b3 100644 --- a/hscript/network.cc +++ b/hscript/network.cc @@ -30,15 +30,14 @@ using namespace Horizon::Keys; -Key *Network::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Network::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { bool value; - if(!BooleanKey::parse(data, "installfile:" + std::to_string(lineno), - "network", &value)) { + if(!BooleanKey::parse(data, pos, "network", &value)) { if(errors) *errors += 1; return nullptr; } - return new Network(script, lineno, value); + return new Network(script, pos, value); } bool Network::execute() const { @@ -47,7 +46,8 @@ bool Network::execute() const { } -Key *NetConfigType::parseFromData(const std::string &data, int lineno, +Key *NetConfigType::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, const Script *script) { std::string type = data; ConfigSystem system; @@ -60,13 +60,12 @@ Key *NetConfigType::parseFromData(const std::string &data, int lineno, system = ENI; } else { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netconfigtype: invalid or missing config type", + output_error(pos, "netconfigtype: invalid or missing config type", "one of 'netifrc', 'eni' required"); return nullptr; } - return new NetConfigType(script, lineno, system); + return new NetConfigType(script, pos, system); } bool NetConfigType::validate() const { @@ -80,8 +79,9 @@ bool NetConfigType::execute() const { } -Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *NetAddress::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { long elements = std::count(data.cbegin(), data.cend(), ' ') + 1; std::string::size_type type_pos, addr_pos, prefix_pos, gw_pos, next_end; std::string iface, type, addr, prefix, gw; @@ -90,8 +90,7 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, if(elements < 2) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: missing address type", + output_error(pos, "netaddress: missing address type", "one of 'dhcp', 'slaac', 'static' required"); return nullptr; } @@ -100,8 +99,7 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, iface = data.substr(0, type_pos); if(iface.length() > IFNAMSIZ) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: interface name '" + iface + "' is invalid", + output_error(pos, "netaddress: invalid interface name '" + iface + "'", "interface names must be 16 characters or less"); return nullptr; } @@ -117,27 +115,24 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, if(!type.compare("dhcp")) { if(elements > 2) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: address type 'dhcp' does not " + output_error(pos, "netaddress: address type 'dhcp' does not " "accept further elements"); return nullptr; } - return new NetAddress(script, lineno, iface, AddressType::DHCP, "", 0, + return new NetAddress(script, pos, iface, AddressType::DHCP, "", 0, ""); } else if(!type.compare("slaac")) { if(elements > 2) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: address type 'slaac' does not " + output_error(pos, "netaddress: address type 'slaac' does not " "accept further elements"); return nullptr; } - return new NetAddress(script, lineno, iface, AddressType::SLAAC, "", 0, + return new NetAddress(script, pos, iface, AddressType::SLAAC, "", 0, ""); } else if(type.compare("static")) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: invalid address type '" + type + "'", + output_error(pos, "netaddress: invalid address type '" + type + "'", "one of 'dhcp', 'slaac', 'static' required"); return nullptr; } @@ -145,16 +140,14 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, /* static address */ if(elements < 4) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: address type 'static' requires at least " - "an IP address and prefix length"); + output_error(pos, "netaddress: address type 'static' requires at " + "least an IP address and prefix length"); return nullptr; } if(elements > 5) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: too many elements to address type 'static'"); + output_error(pos, "netaddress: too many elements for static address"); return nullptr; } @@ -172,8 +165,7 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, /* IPv6 */ if(::inet_pton(AF_INET6, addr.c_str(), &addr_buf) != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: '" + addr + "' is not a valid IPv6 address", + output_error(pos, "netaddress: '" + addr + "' is not a valid IPv6 address", "hint: a ':' was found, indicating this address is IPv6"); return nullptr; } @@ -182,16 +174,14 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, real_prefix = std::stoi(prefix); } catch(const std::exception &) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: prefix length is not a number", + output_error(pos, "netaddress: prefix length is not a number", "prefix must be a decimal value between 1 and 128"); return nullptr; } if(real_prefix < 1 || real_prefix > 128) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: invalid IPv6 prefix length: " + prefix, + output_error(pos, "netaddress: invalid IPv6 prefix length: " + prefix, "prefix must be a decimal value between 1 and 128"); return nullptr; } @@ -199,21 +189,19 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, if(gw.size() > 0 && ::inet_pton(AF_INET6, gw.c_str(), &addr_buf) != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: '" + gw + + output_error(pos, "netaddress: '" + gw + "' is not a valid IPv6 gateway", "an IPv6 address must have an IPv6 gateway"); return nullptr; } - return new NetAddress(script, lineno, iface, AddressType::Static, addr, + return new NetAddress(script, pos, iface, AddressType::Static, addr, static_cast<uint8_t>(real_prefix), gw); } else if(addr.find('.') != std::string::npos) { /* IPv4 */ if(::inet_pton(AF_INET, addr.c_str(), &addr_buf) != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: '" + addr + "' is not a valid IPv4 address"); + output_error(pos, "netaddress: invalid IPv4 address", addr); return nullptr; } @@ -225,8 +213,7 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, real_prefix = std::stoi(prefix); } catch(const std::exception &) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: can't parse prefix length/mask", + output_error(pos, "netaddress: can't parse prefix length/mask", "a network mask or prefix length is required"); return nullptr; } @@ -234,29 +221,26 @@ Key *NetAddress::parseFromData(const std::string &data, int lineno, int *errors, if(real_prefix < 1 || real_prefix > 32) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: invalid IPv4 prefix length: " + prefix, - "prefix must be between 1 and 32"); + output_error(pos, "netaddress: invalid IPv4 prefix length: " + + prefix, "prefix must be between 1 and 32"); return nullptr; } if(gw.size() > 0 && ::inet_pton(AF_INET, gw.c_str(), &addr_buf) != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: '" + gw + + output_error(pos, "netaddress: '" + gw + "' is not a valid IPv4 gateway", "an IPv4 address must have an IPv4 gateway"); return nullptr; } - return new NetAddress(script, lineno, iface, AddressType::Static, addr, + return new NetAddress(script, pos, iface, AddressType::Static, addr, static_cast<uint8_t>(real_prefix), gw); } else { /* IPvBad */ if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netaddress: invalid address of unknown type", + output_error(pos, "netaddress: invalid address of unknown type", "an IPv4 or IPv6 address is required"); return nullptr; } @@ -272,8 +256,7 @@ bool NetAddress::validate() const { struct ifreq request; int my_sock = ::socket(AF_INET, SOCK_STREAM, 0); if(my_sock == -1) { - output_error("installfile:" + std::to_string(this->lineno()), - "netaddress: can't open socket", ::strerror(errno)); + output_error(pos, "netaddress: can't open socket", ::strerror(errno)); return false; } memset(&request, 0, sizeof(request)); @@ -281,12 +264,10 @@ bool NetAddress::validate() const { errno = 0; if(ioctl(my_sock, SIOCGIFFLAGS, &request) == -1) { if(errno == ENODEV) { - output_warning("installfile:" + std::to_string(this->lineno()), - "netaddress: specified interface does not exist"); + output_warning(pos, "netaddress: interface does not exist", _iface); return true; } - output_error("installfile:" + std::to_string(this->lineno()), - "netaddress: trouble communicating with interface", + output_error(pos, "netaddress: trouble communicating with interface", ::strerror(errno)); return false; } @@ -298,9 +279,8 @@ bool execute_address_netifrc(const NetAddress *addr) { std::ofstream config("/tmp/horizon/netifrc/config_" + addr->iface(), std::ios_base::app); if(!config) { - output_error("installfile:" + std::to_string(addr->lineno()), - "netaddress: couldn't write network configuration for " - + addr->iface()); + output_error(addr->where(), "netaddress: couldn't write network " + "configuration for " + addr->iface()); return false; } @@ -321,9 +301,8 @@ bool execute_address_netifrc(const NetAddress *addr) { std::ofstream route("/tmp/horizon/netifrc/routes_" + addr->iface(), std::ios_base::app); if(!route) { - output_error("installfile:" + std::to_string(addr->lineno()), - "netaddress: couldn't write route configuration for " - + addr->iface()); + output_error(addr->where(), "netaddress: couldn't write route " + "configuration for " + addr->iface()); return false; } route << "default via " << addr->gateway() << std::endl; @@ -336,9 +315,8 @@ bool execute_address_eni(const NetAddress *addr) { std::ofstream config("/tmp/horizon/eni/" + addr->iface(), std::ios_base::app); if(!config) { - output_error("installfile:" + std::to_string(addr->lineno()), - "netaddress: couldn't write network configuration for " - + addr->iface()); + output_error(addr->where(), "netaddress: couldn't write network " + "configuration for " + addr->iface()); return false; } @@ -375,8 +353,7 @@ bool execute_address_eni(const NetAddress *addr) { } bool NetAddress::execute() const { - output_info("installfile:" + std::to_string(this->lineno()), - "netaddress: adding configuration for " + _iface); + output_info(pos, "netaddress: adding configuration for " + _iface); NetConfigType::ConfigSystem system = NetConfigType::Netifrc; @@ -396,18 +373,17 @@ bool NetAddress::execute() const { } -Key *Nameserver::parseFromData(const std::string &data, int lineno, - int *errors, int *, const Script *script) { +Key *Nameserver::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { char addr_buf[16]; static const std::string valid_chars("1234567890ABCDEFabcdef:."); if(data.find_first_not_of(valid_chars) != std::string::npos) { if(errors) *errors += 0; - output_error("installfile:" + std::to_string(lineno), - "nameserver: expected an IP address"); + output_error(pos, "nameserver: expected an IP address"); if(data.find_first_of("[]") != std::string::npos) { - output_info("installfile:" + std::to_string(lineno), - "nameserver: hint: you don't have to enclose IPv6 " + output_info(pos, "nameserver: hint: you don't have to enclose IPv6 " "addresses in [] brackets"); } return nullptr; @@ -417,8 +393,7 @@ Key *Nameserver::parseFromData(const std::string &data, int lineno, /* IPv6 */ if(::inet_pton(AF_INET6, data.c_str(), &addr_buf) != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "nameserver: '" + data + "' is not a valid IPv6 " + output_error(pos, "nameserver: '" + data + "' is not a valid IPv6 " "address", "hint: a ':' was found, so an IPv6 " "address was expected"); return nullptr; @@ -427,14 +402,13 @@ Key *Nameserver::parseFromData(const std::string &data, int lineno, /* IPv4 */ if(::inet_pton(AF_INET, data.c_str(), &addr_buf) != 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "nameserver: '" + data + "' is not a valid IPv4 " + output_error(pos, "nameserver: '" + data + "' is not a valid IPv4 " "address"); return nullptr; } } - return new Nameserver(script, lineno, data); + return new Nameserver(script, pos, data); } bool Nameserver::execute() const { @@ -449,8 +423,7 @@ bool Nameserver::execute() const { std::ofstream resolvconf(script->targetDirectory() + "/etc/resolv.conf", std::ios_base::app); if(!resolvconf) { - output_error("installfile:" + std::to_string(line), - "nameserver: couldn't write configuration to target"); + output_error(pos, "nameserver: couldn't write configuration to target"); return false; } resolvconf << "nameserver " << _value << std::endl; @@ -459,8 +432,8 @@ bool Nameserver::execute() const { } -Key *NetSSID::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *NetSSID::parseFromData(const std::string &data, const ScriptLocation &p, + int *errors, int *, const Script *script) { std::string iface, ssid, secstr, passphrase; SecurityType type; std::string::size_type start, pos, next; @@ -472,32 +445,28 @@ Key *NetSSID::parseFromData(const std::string &data, int lineno, int *errors, if(start == std::string::npos) { /* ok this is just ridiculous then */ if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: at least three elements expected"); + output_error(p, "netssid: at least three elements expected"); return nullptr; } iface = data.substr(0, start); if(iface.length() > IFNAMSIZ) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: interface name '" + iface + "' is invalid", + output_error(p, "netssid: interface name '" + iface + "' is invalid", "interface names must be 16 characters or less"); return nullptr; } if(data[start + 1] != '"') { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: malformed SSID", "SSIDs must be quoted"); + output_error(p, "netssid: malformed SSID", "SSIDs must be quoted"); return nullptr; } pos = data.find_first_of('"', start + 2); if(pos == std::string::npos) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: unterminated SSID"); + output_error(p, "netssid: unterminated SSID"); return nullptr; } @@ -505,8 +474,7 @@ Key *NetSSID::parseFromData(const std::string &data, int lineno, int *errors, if(data.length() < pos + 5) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: security type expected"); + output_error(p, "netssid: security type expected"); return nullptr; } start = data.find_first_of(' ', pos + 1); @@ -524,8 +492,7 @@ Key *NetSSID::parseFromData(const std::string &data, int lineno, int *errors, type = SecurityType::WPA; } else { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: unknown security type '" + secstr + "'", + output_error(p, "netssid: unknown security type '" + secstr + "'", "expected one of 'none', 'wep', or 'wpa'"); return nullptr; } @@ -533,14 +500,13 @@ Key *NetSSID::parseFromData(const std::string &data, int lineno, int *errors, if(type != SecurityType::None) { if(pos == std::string::npos || data.length() < pos + 2) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "netssid: expected passphrase for security type '" + + output_error(p, "netssid: expected passphrase for security type '" + secstr + "'"); return nullptr; } passphrase = data.substr(pos + 1); } - return new NetSSID(script, lineno, iface, ssid, type, passphrase); + return new NetSSID(script, p, iface, ssid, type, passphrase); } bool NetSSID::validate() const { @@ -553,8 +519,7 @@ bool NetSSID::validate() const { struct iwreq request; int my_sock = ::socket(AF_INET, SOCK_STREAM, 0); if(my_sock == -1) { - output_error("installfile:" + std::to_string(this->lineno()), - "netssid: can't open socket", ::strerror(errno)); + output_error(pos, "netssid: can't open socket", ::strerror(errno)); return false; } memset(&request, 0, sizeof(request)); @@ -565,16 +530,13 @@ bool NetSSID::validate() const { switch(errno) { case EOPNOTSUPP: - output_warning("installfile:" + std::to_string(this->lineno()), - "netssid: specified interface is not wireless"); + output_warning(pos, "netssid: specified interface is not wireless"); return true; case ENODEV: - output_warning("installfile:" + std::to_string(this->lineno()), - "netssid: specified interface does not exist"); + output_warning(pos, "netssid: specified interface does not exist"); return true; default: - output_error("installfile:" + std::to_string(this->lineno()), - "netssid: error communicating with wireless device"); + output_error(pos, "netssid: error communicating with device"); return false; } } @@ -586,14 +548,12 @@ bool NetSSID::validate() const { } bool NetSSID::execute() const { - output_info("installfile:" + std::to_string(this->lineno()), - "netssid: configuring SSID " + _ssid); + output_info(pos, "netssid: configuring SSID " + _ssid); std::ofstream conf("/tmp/horizon/wpa_supplicant.conf", std::ios_base::app); if(!conf) { - output_error("installfile:" + std::to_string(this->lineno()), - "netssid: failed to write configuration"); + output_error(pos, "netssid: failed to write configuration"); return false; } diff --git a/hscript/network.hh b/hscript/network.hh index 415a337..18ffc34 100644 --- a/hscript/network.hh +++ b/hscript/network.hh @@ -21,11 +21,11 @@ namespace Keys { class Network : public BooleanKey { private: - Network(const Script *_s, int _line, bool _value) : - BooleanKey(_s, _line, _value) {} + Network(const Script *_s, const ScriptLocation &_p, bool _value) : + BooleanKey(_s, _p, _value) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &pos, + int*, int*, const Script *); bool execute() const override; }; @@ -38,11 +38,11 @@ public: private: ConfigSystem _sys; - NetConfigType(const Script *_sc, int _line, const ConfigSystem _s) : - Key(_sc, _line), _sys(_s) {} + NetConfigType(const Script *_sc, const ScriptLocation &_p, + const ConfigSystem _s) : Key(_sc, _p), _sys(_s) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the desired network configuration system. */ ConfigSystem type() const { return this->_sys; } @@ -69,14 +69,14 @@ private: const uint8_t _prefix; const std::string _gw; - NetAddress(const Script *_sc, const int _line, const std::string &_i, + NetAddress(const Script *sc, const ScriptLocation &p, const std::string &_i, const AddressType &_t, const std::string &_a, const uint8_t _p, - const std::string &_g) : Key(_sc, _line), _iface(_i), _type(_t), + const std::string &_g) : Key(sc, p), _iface(_i), _type(_t), _address(_a), _prefix(_p), _gw(_g) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the interface to which this 'netaddress' key is associated. */ const std::string iface() const { return this->_iface; } @@ -95,11 +95,11 @@ public: class Nameserver : public StringKey { private: - Nameserver(const Script *_s, int _line, const std::string &ns) : - StringKey(_s, _line, ns) {} + Nameserver(const Script *_s, const ScriptLocation &_pos, + const std::string &ns) : StringKey(_s, _pos, ns) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &_pos, + int*, int*, const Script *); bool execute() const override; }; @@ -117,12 +117,12 @@ private: const SecurityType _sec; const std::string _pw; - NetSSID(const Script *_sc, int _line, const std::string &_if, + NetSSID(const Script *_sc, const ScriptLocation &p, const std::string &_if, const std::string &_s, SecurityType _t, const std::string &_p) : - Key(_sc, _line), _iface(_if), _ssid(_s), _sec(_t), _pw(_p) {} + Key(_sc, p), _iface(_if), _ssid(_s), _sec(_t), _pw(_p) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the interface to which this 'netssid' key is associated. */ const std::string iface() const { return this->_iface; } diff --git a/hscript/script.cc b/hscript/script.cc index 5b261d2..f775629 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -20,6 +20,7 @@ #include "script.hh" #include "script_i.hh" +#include "script_l.hh" #include "disk.hh" #include "meta.hh" #include "network.hh" @@ -30,8 +31,9 @@ #define SCRIPT_LINE_MAX 512 -typedef Horizon::Keys::Key *(*key_parse_fn)(const std::string &, int, int*, - int*, const Horizon::Script *); +typedef Horizon::Keys::Key *(*key_parse_fn)(const std::string &, + const Horizon::ScriptLocation &, + int*, int*, const Horizon::Script*); using namespace Horizon::Keys; @@ -76,12 +78,12 @@ const std::map<std::string, key_parse_fn> valid_keys = { namespace Horizon { bool Script::ScriptPrivate::store_key(const std::string &key_name, Key *obj, - int lineno, int *errors, int *warnings, - const ScriptOptions &opts) { + const ScriptLocation &pos, int *errors, + int *warnings, const ScriptOptions &opts) { if(key_name == "network") { - return store_network(obj, lineno, errors, warnings, opts); + return store_network(obj, pos, errors, warnings, opts); } else if(key_name == "netconfigtype") { - return store_netconfig(obj, lineno, errors, warnings, opts); + return store_netconfig(obj, pos, errors, warnings, opts); } else if(key_name == "netaddress") { std::unique_ptr<NetAddress> addr(dynamic_cast<NetAddress *>(obj)); this->addresses.push_back(std::move(addr)); @@ -95,21 +97,21 @@ bool Script::ScriptPrivate::store_key(const std::string &key_name, Key *obj, this->ssids.push_back(std::move(ssid)); return true; } else if(key_name == "hostname") { - return store_hostname(obj, lineno, errors, warnings, opts); + return store_hostname(obj, pos, errors, warnings, opts); } else if(key_name == "pkginstall") { - return store_pkginstall(obj, lineno, errors, warnings, opts); + return store_pkginstall(obj, pos, errors, warnings, opts); } else if(key_name == "arch") { - return store_arch(obj, lineno, errors, warnings, opts); + return store_arch(obj, pos, errors, warnings, opts); } else if(key_name == "rootpw") { - return store_rootpw(obj, lineno, errors, warnings, opts); + return store_rootpw(obj, pos, errors, warnings, opts); } else if(key_name == "language") { - return store_lang(obj, lineno, errors, warnings, opts); + return store_lang(obj, pos, errors, warnings, opts); } else if(key_name == "keymap") { - return store_keymap(obj, lineno, errors, warnings, opts); + return store_keymap(obj, pos, errors, warnings, opts); } else if(key_name == "firmware") { - return store_firmware(obj, lineno, errors, warnings, opts); + return store_firmware(obj, pos, errors, warnings, opts); } else if(key_name == "timezone") { - return store_timezone(obj, lineno, errors, warnings, opts); + return store_timezone(obj, pos, errors, warnings, opts); } else if(key_name == "repository") { std::unique_ptr<Repository> repo(dynamic_cast<Repository *>(obj)); this->repos.push_back(std::move(repo)); @@ -119,17 +121,17 @@ bool Script::ScriptPrivate::store_key(const std::string &key_name, Key *obj, this->repo_keys.push_back(std::move(key)); return true; } else if(key_name == "svcenable") { - return store_svcenable(obj, lineno, errors, warnings, opts); + return store_svcenable(obj, pos, errors, warnings, opts); } else if(key_name == "username") { - return store_username(obj, lineno, errors, warnings, opts); + return store_username(obj, pos, errors, warnings, opts); } else if(key_name == "useralias") { - return store_useralias(obj, lineno, errors, warnings, opts); + return store_useralias(obj, pos, errors, warnings, opts); } else if(key_name == "userpw") { - return store_userpw(obj, lineno, errors, warnings, opts); + return store_userpw(obj, pos, errors, warnings, opts); } else if(key_name == "usericon") { - return store_usericon(obj, lineno, errors, warnings, opts); + return store_usericon(obj, pos, errors, warnings, opts); } else if(key_name == "usergroups") { - return store_usergroups(obj, lineno, errors, warnings, opts); + return store_usergroups(obj, pos, errors, warnings, opts); } else if(key_name == "diskid") { std::unique_ptr<DiskId> diskid(dynamic_cast<DiskId *>(obj)); this->diskids.push_back(std::move(diskid)); @@ -188,23 +190,22 @@ Script *Script::load(const std::string &path, const ScriptOptions &opts) { return nullptr; } - return Script::load(file, opts); + return Script::load(file, opts, path); } -Script *Script::load(std::istream &sstream, const ScriptOptions &opts) { +Script *Script::load(std::istream &sstream, const ScriptOptions &opts, + const std::string &name) { #define PARSER_ERROR(err_str) \ errors++;\ - output_error("installfile:" + std::to_string(lineno),\ - err_str, "");\ + output_error(pos, err_str, "");\ if(!opts.test(ScriptOptionFlags::KeepGoing)) {\ break;\ } #define PARSER_WARNING(warn_str) \ warnings++;\ - output_warning("installfile:" + std::to_string(lineno),\ - warn_str, ""); + output_warning(pos, warn_str, ""); using namespace Horizon::Keys; @@ -223,6 +224,7 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts) { continue; } + const ScriptLocation pos(name, lineno, false); const std::string line(nextline); std::string key; std::string::size_type start, key_end, value_begin; @@ -254,14 +256,14 @@ Script *Script::load(std::istream &sstream, const ScriptOptions &opts) { continue; } - Key *key_obj = valid_keys.at(key)(line.substr(value_begin), lineno, + Key *key_obj = valid_keys.at(key)(line.substr(value_begin), pos, &errors, &warnings, the_script); if(!key_obj) { PARSER_ERROR("value for key '" + key + "' was invalid") continue; } - if(!the_script->internal->store_key(key, key_obj, lineno, &errors, + if(!the_script->internal->store_key(key, key_obj, pos, &errors, &warnings, opts)) { PARSER_ERROR("stopping due to prior errors") continue; diff --git a/hscript/script.hh b/hscript/script.hh index c874bd3..9befb41 100644 --- a/hscript/script.hh +++ b/hscript/script.hh @@ -18,6 +18,8 @@ #include <memory> #include <bitset> +#include <hscript/script_l.hh> + namespace Horizon { namespace Keys { @@ -71,10 +73,12 @@ public: /*! Load a HorizonScript from the specified stream. * @param stream The stream to load from. * @param options Options to use for parsing, validation, and execution. + * @param name The name of the stream to use in diagnostic messages. * @return true if the Script could be loaded; false otherwise. */ static Script *load(std::istream &stream, - const ScriptOptions &options = 0); + const ScriptOptions &options = 0, + const std::string &name = "installfile"); /*! Determines if the HorizonScript is valid. */ bool validate() const; diff --git a/hscript/script_i.hh b/hscript/script_i.hh index b91e60f..b6fc371 100644 --- a/hscript/script_i.hh +++ b/hscript/script_i.hh @@ -19,6 +19,8 @@ #include <string> #include <vector> +#include "script_l.hh" + #include "disk.hh" #include "meta.hh" #include "network.hh" @@ -104,23 +106,26 @@ struct Script::ScriptPrivate { /*! 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, int lineno, - int *errors, int *warnings, const ScriptOptions &opts); + 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) \ std::string err_str("previous value was ");\ err_str += OLD_VAL;\ - err_str += " at installfile:" + std::to_string(OBJ->lineno());\ + err_str += " at " + OBJ->where().name;\ + err_str += ":" + std::to_string(OBJ->where().line);\ if(errors) *errors += 1;\ - output_error("installfile:" + std::to_string(line),\ - "duplicate value for key '" + std::string(KEY) + "'",\ + output_error(pos, "duplicate value for key '" + std::string(KEY) + "'",\ err_str); - bool store_network(Key* obj, int line, int *errors, int *, ScriptOptions) { + bool store_network(Key* obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(network) { DUPLICATE_ERROR(network, "network", network->test() ? "true" : "false") @@ -131,7 +136,8 @@ struct Script::ScriptPrivate { return true; } - bool store_netconfig(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_netconfig(Key *obj, const ScriptLocation &pos, int *errors, + int *, const ScriptOptions &) { if(netconfig) { DUPLICATE_ERROR(netconfig, "netconfigtype", netconfig->type()); @@ -142,7 +148,8 @@ struct Script::ScriptPrivate { return true; } - bool store_hostname(Key* obj, int line, int *errors, int *, ScriptOptions) { + bool store_hostname(Key* obj, const ScriptLocation &pos, int *errors, + int *, const ScriptOptions &) { if(hostname) { DUPLICATE_ERROR(hostname, "hostname", hostname->value()) return false; @@ -152,14 +159,13 @@ struct Script::ScriptPrivate { return true; } - bool store_pkginstall(Key* obj, int line, int *, int *warnings, - ScriptOptions opts) { + bool store_pkginstall(Key* obj, const ScriptLocation &pos, int *, + int *warnings, const 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 + + output_warning(pos, "pkginstall: package '" + pkg + "' has already been specified"); continue; } @@ -169,7 +175,8 @@ struct Script::ScriptPrivate { return true; } - bool store_arch(Key* obj, int line, int *errors, int *, ScriptOptions) { + bool store_arch(Key* obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(arch) { DUPLICATE_ERROR(arch, "arch", arch->value()) return false; @@ -179,7 +186,8 @@ struct Script::ScriptPrivate { return true; } - bool store_rootpw(Key* obj, int line, int *errors, int *, ScriptOptions) { + bool store_rootpw(Key* obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(rootpw) { DUPLICATE_ERROR(rootpw, "rootpw", "an encrypted passphrase") return false; @@ -189,7 +197,8 @@ struct Script::ScriptPrivate { return true; } - bool store_firmware(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_firmware(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { std::unique_ptr<Firmware> f(dynamic_cast<Firmware *>(obj)); #ifdef NON_LIBRE_FIRMWARE if(firmware) { @@ -205,7 +214,8 @@ struct Script::ScriptPrivate { #endif } - bool store_lang(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_lang(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(lang) { DUPLICATE_ERROR(lang, "language", lang->value()) return false; @@ -215,7 +225,8 @@ struct Script::ScriptPrivate { return true; } - bool store_keymap(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_keymap(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(keymap) { DUPLICATE_ERROR(keymap, "keymap", keymap->value()) return false; @@ -225,7 +236,8 @@ struct Script::ScriptPrivate { return true; } - bool store_timezone(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_timezone(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(tzone) { DUPLICATE_ERROR(tzone, "timezone", tzone->value()) return false; @@ -235,13 +247,13 @@ struct Script::ScriptPrivate { return true; } - bool store_svcenable(Key *obj, int line, int *, int *warn, ScriptOptions) { + bool store_svcenable(Key *obj, const ScriptLocation &pos, int *, int *warn, + const ScriptOptions &) { std::unique_ptr<SvcEnable> svc(dynamic_cast<SvcEnable *>(obj)); for(const auto &s : svcs_enable) { if(s->value() == svc->value()) { if(warn) *warn += 1; - output_warning("installfile:" + std::to_string(line), - "svcenable: service already enabled", + output_warning(pos, "svcenable: service already enabled", s->value()); return true; } @@ -251,11 +263,11 @@ struct Script::ScriptPrivate { return true; } - bool store_username(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_username(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { if(accounts.size() >= 255) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(line), - "username: too many users", + output_error(pos, "username: too many users", "you may only specify 255 users"); return false; } @@ -275,14 +287,14 @@ struct Script::ScriptPrivate { #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");\ + 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, int line, int *errors, int *, ScriptOptions) { + bool store_useralias(Key* obj, const ScriptLocation &pos, int *errors, + int *, const ScriptOptions &) { std::unique_ptr<UserAlias> alias(dynamic_cast<UserAlias *>(obj)); GET_USER_DETAIL(alias, "useralias") /* REQ: Runner.Validate.useralias.Unique */ @@ -294,7 +306,8 @@ struct Script::ScriptPrivate { return true; } - bool store_userpw(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_userpw(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { std::unique_ptr<UserPassphrase> pw(dynamic_cast<UserPassphrase *>(obj)); GET_USER_DETAIL(pw, "userpw") /* REQ: Runner.Validate.userpw.Unique */ @@ -307,7 +320,8 @@ struct Script::ScriptPrivate { return true; } - bool store_usericon(Key *obj, int line, int *errors, int *, ScriptOptions) { + bool store_usericon(Key *obj, const ScriptLocation &pos, int *errors, int *, + const ScriptOptions &) { std::unique_ptr<UserIcon> icon(dynamic_cast<UserIcon *>(obj)); GET_USER_DETAIL(icon, "usericon") /* REQ: Runner.Validate.usericon.Unique */ @@ -319,8 +333,8 @@ struct Script::ScriptPrivate { return true; } - bool store_usergroups(Key* obj, int line, int *errors, int *, - ScriptOptions) { + bool store_usergroups(Key* obj, const ScriptLocation &pos, int *errors, + int *, const ScriptOptions &) { std::unique_ptr<UserGroups> grp(dynamic_cast<UserGroups *>(obj)); GET_USER_DETAIL(grp, "usergroups") detail->groups.push_back(std::move(grp)); diff --git a/hscript/script_l.hh b/hscript/script_l.hh new file mode 100644 index 0000000..1f048c6 --- /dev/null +++ b/hscript/script_l.hh @@ -0,0 +1,35 @@ +/* + * script_l.hh - Definition of the ScriptLocation structure + * libhscript, the HorizonScript library for + * Project Horizon + * + * Copyright (c) 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 SCRIPT_L_HH +#define SCRIPT_L_HH + +#include <string> + +namespace Horizon { + +/*! Defines a location within a script. */ +struct ScriptLocation { + /*! The filename of the script. */ + std::string name; + /*! The line number of the current location. */ + int line; + /*! Whether this script is nested or the original script. */ + bool nested; + + ScriptLocation(std::string _n, int _l, bool _nest = false) : + name{_n}, line{_l}, nested{_nest} {}; +}; + +} + +#endif /* !SCRIPT_L_HH */ diff --git a/hscript/script_v.cc b/hscript/script_v.cc index 3622365..8fd1327 100644 --- a/hscript/script_v.cc +++ b/hscript/script_v.cc @@ -59,8 +59,7 @@ int validate_one_account(const std::string &name, UserDetail *detail) { /* REQ: Runner.Validate.userpw.None */ if(!detail->passphrase) { - long line = detail->name->lineno(); - output_warning("installfile:" + to_string(line), + output_warning(detail->name->where(), "username: " + name + " has no set passphrase", "This account will not be able to log in."); } @@ -81,7 +80,7 @@ int validate_one_account(const std::string &name, UserDetail *detail) { return seen_groups.find(elem) == seen_groups.end(); }) ) { - output_error("installfile:" + to_string(group->lineno()), + output_error(group->where(), "usergroups: duplicate group name specified"); failures++; } @@ -110,7 +109,7 @@ bool add_default_repos(std::vector<std::unique_ptr<Repository>> &repos, const Script *s, bool firmware = false) { Repository *sys_key = dynamic_cast<Repository *>( Repository::parseFromData( - "https://distfiles.adelielinux.org/adelie/stable/system", 0, + "https://distfiles.adelielinux.org/adelie/stable/system", {"", 0}, nullptr, nullptr, s ) ); @@ -122,7 +121,7 @@ bool add_default_repos(std::vector<std::unique_ptr<Repository>> &repos, repos.push_back(std::move(sys_repo)); Repository *user_key = dynamic_cast<Repository *>( Repository::parseFromData( - "https://distfiles.adelielinux.org/adelie/stable/user", 0, + "https://distfiles.adelielinux.org/adelie/stable/user", {"", 0}, nullptr, nullptr, s ) ); @@ -164,8 +163,8 @@ bool add_default_repo_keys(std::vector<std::unique_ptr<SigningKey>> &keys, const Script *s, bool firmware = false) { SigningKey *key = dynamic_cast<SigningKey *>( SigningKey::parseFromData( - "/etc/apk/keys/packages@adelielinux.org.pub", 0, nullptr, nullptr, - s) + "/etc/apk/keys/packages@adelielinux.org.pub", {"", 0}, + nullptr, nullptr, s) ); if(!key) { output_error("internal", "failed to create default repository signing key"); @@ -179,7 +178,7 @@ bool add_default_repo_keys(std::vector<std::unique_ptr<SigningKey>> &keys, if(firmware) { SigningKey *fkey = dynamic_cast<SigningKey *>(SigningKey::parseFromData( "/etc/apk/keys/packages@pleroma.apkfission.net-5ac0b300.rsa.pub", - 0, nullptr, nullptr, s) + {"", 0}, nullptr, nullptr, s) ); if(!fkey) { output_error("internal", "failed to create firmware signing key"); @@ -189,7 +188,7 @@ bool add_default_repo_keys(std::vector<std::unique_ptr<SigningKey>> &keys, keys.push_back(std::move(fw_key)); fkey = dynamic_cast<SigningKey *>(SigningKey::parseFromData( "/etc/apk/keys/packages@pleroma.apkfission.net-5ac04808.rsa.pub", - 0, nullptr, nullptr, s)); + {"", 0}, nullptr, nullptr, s)); if(fkey) { std::unique_ptr<SigningKey> fw_key2(fkey); keys.push_back(std::move(fw_key2)); @@ -230,9 +229,8 @@ bool Horizon::Script::validate() const { 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"); + output_error(address->where(), "netaddress: interface '" + + address->iface() + "' has too many addresses"); } } } @@ -242,8 +240,7 @@ bool Horizon::Script::validate() const { if(!ns->validate()) failures++; } if(internal->nses.size() > MAXNS) { - output_warning("installfile:" + - to_string(internal->nses[MAXNS]->lineno()), + output_warning(internal->nses[MAXNS]->where(), "nameserver: more nameservers are defined than usable", to_string(MAXNS) + " nameservers are allowed"); } @@ -276,7 +273,7 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Execute.timezone */ if(!internal->tzone) { Timezone *utc = dynamic_cast<Timezone *> - (Timezone::parseFromData("UTC", 0, &failures, nullptr, this)); + (Timezone::parseFromData("UTC", {"", 0}, &failures, nullptr, this)); if(!utc) { output_error("internal", "failed to create default timezone"); return false; @@ -305,7 +302,7 @@ bool Horizon::Script::validate() const { } if(internal->repos.size() > 10) { failures++; - output_error("installfile:" + to_string(internal->repos[11]->lineno()), + output_error(internal->repos[11]->where(), "repository: too many repositories specified", "You may only specify up to 10 repositories."); } @@ -327,8 +324,7 @@ bool Horizon::Script::validate() const { } if(internal->repo_keys.size() > 10) { failures++; - output_error("installfile:" + - to_string(internal->repo_keys[11]->lineno()), + output_error(internal->repo_keys[11]->where(), "signingkey: too many keys specified", "You may only specify up to 10 repository keys."); } @@ -351,9 +347,8 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.diskid.Unique */ if(seen_diskids.find(diskid->device()) != seen_diskids.end()) { failures++; - output_error("installfile:" + to_string(diskid->lineno()), - "diskid: device " + diskid->device() + - " has already been identified"); + output_error(diskid->where(), "diskid: device " + + diskid->device() + " has already been identified"); } seen_diskids.insert(diskid->device()); } @@ -365,9 +360,8 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.disklabel.Unique */ if(seen_labels.find(label->device()) != seen_labels.end()) { failures++; - output_error("installfile:" + to_string(label->lineno()), - "disklabel: device " + label->device() + - " already has a label queued"); + output_error(label->where(), "disklabel: device " + + label->device() + " already has a label queued"); } else { seen_labels.insert(label->device()); } @@ -383,9 +377,9 @@ bool Horizon::Script::validate() const { std::string name = dev + maybe_p + to_string(part->partno()); if(seen_parts.find(name) != seen_parts.end()) { failures++; - output_error("installfile:" + to_string(part->lineno()), - "partition: partition #" + to_string(part->partno()) + - " already exists on device " + part->device()); + output_error(part->where(), "partition: partition #" + + to_string(part->partno()) + " already exists on " + + part->device()); } else { seen_parts.insert(name); } @@ -398,9 +392,8 @@ bool Horizon::Script::validate() const { /* We don't actually have a requirement, but... */ if(seen_pvs.find(pv->value()) != seen_pvs.end()) { failures++; - output_error("installfile:" + to_string(pv->lineno()), - "lvm_pv: a physical volume already exists on device " - + pv->value()); + output_error(pv->where(), "lvm_pv: a physical volume already " + "exists on device " + pv->value()); } else { seen_pvs.insert(pv->value()); } @@ -411,8 +404,7 @@ bool Horizon::Script::validate() const { if(!fs::exists(pv->value(), ec) && seen_parts.find(pv->value()) == seen_parts.end()) { failures++; - output_error("installfile:" + to_string(pv->lineno()), - "lvm_pv: device " + pv->value() + + output_error(pv->where(), "lvm_pv: device " + pv->value() + " does not exist"); } #endif /* HAS_INSTALL_ENV */ @@ -425,8 +417,7 @@ bool Horizon::Script::validate() const { if(seen_vg_names.find(vg->name()) != seen_vg_names.end()) { failures++; - output_error("installfile:" + to_string(vg->lineno()), - "lvm_vg: duplicate volume group name specified", + output_error(vg->where(), "lvm_vg: duplicate volume group name", vg->name() + " already given"); } else { seen_vg_names.insert(vg->name()); @@ -434,9 +425,8 @@ bool Horizon::Script::validate() const { if(seen_vg_pvs.find(vg->pv()) != seen_vg_pvs.end()) { failures++; - output_error("installfile:" + to_string(vg->lineno()), - "lvm_vg: a volume group already exists on " + - vg->pv()); + output_error(vg->where(), "lvm_vg: a volume group already exists " + "on " + vg->pv()); } else { seen_vg_pvs.insert(vg->pv()); } @@ -449,16 +439,14 @@ bool Horizon::Script::validate() const { #ifdef HAS_INSTALL_ENV if(!vg->test_pv()) { failures++; - output_error("installfile:" + to_string(vg->lineno()), - "lvm_vg: a physical volume does not exist on " - + vg->pv()); + output_error(vg->where(), "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:" + to_string(vg->lineno()), - "lvm_vg: please ensure an LVM physical volume " - "already exists at " + vg->pv()); + output_warning(vg->where(), "lvm_vg: please ensure an LVM " + "physical volume already exists at " + vg->pv()); } } } @@ -471,9 +459,9 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.lvm_lv.Name */ if(seen_lvs.find(lvpath) != seen_lvs.end()) { failures++; - output_error("installfile:" + to_string(lv->lineno()), - "lvm_lv: a volume with the name " + lv->name() + - " already exists on the volume group " + lv->vg()); + output_error(lv->where(), "lvm_lv: a volume with the name " + + lv->name() + " already exists on the volume group " + + lv->vg()); } else { seen_lvs.insert(lvpath); } @@ -485,22 +473,20 @@ bool Horizon::Script::validate() const { #ifdef HAS_INSTALL_ENV if(!fs::exists("/dev/" + lv->vg())) { failures++; - output_error("installfile:" + to_string(lv->lineno()), - "lvm_lv: volume group " + lv->vg() + - " does not exist"); + output_error(lv->where(), "lvm_lv: volume group " + + lv->vg() + " does not exist"); } #endif /* HAS_INSTALL_ENV */ } } } -#define CHECK_EXIST_PART_LV(device, key, line) \ +#define CHECK_EXIST_PART_LV(device, key, where) \ 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:" + to_string(line),\ - std::string(key) + ": device " + device +\ + output_error(where, std::string(key) + ": device " + device +\ " does not exist");\ } @@ -511,8 +497,7 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.encrypt.Unique */ if(seen_luks.find(crypt->device()) != seen_luks.end()) { failures++; - output_error("installfile:" + to_string(crypt->lineno()), - "encrypt: encryption is already scheduled for " + + output_error(crypt->where(), "encrypt: encryption already enabled", crypt->device()); } else { seen_luks.insert(crypt->device()); @@ -521,7 +506,7 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.encrypt.Block */ if(opts.test(InstallEnvironment)) { #ifdef HAS_INSTALL_ENV - CHECK_EXIST_PART_LV(crypt->device(), "encrypt", crypt->lineno()) + CHECK_EXIST_PART_LV(crypt->device(), "encrypt", crypt->where()) #endif /* HAS_INSTALL_ENV */ } } @@ -533,16 +518,15 @@ bool Horizon::Script::validate() const { /* 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()); + output_error(fs->where(), "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()) + CHECK_EXIST_PART_LV(fs->device(), "fs", fs->where()) #endif /* HAS_INSTALL_ENV */ } } @@ -554,10 +538,9 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.mount.Unique */ if(seen_mounts.find(mount->mountpoint()) != seen_mounts.end()) { failures++; - output_error("installfile:" + to_string(mount->lineno()), - "mount: mountpoint " + mount->mountpoint() + - " has already been specified; " + mount->device() + - " is a duplicate"); + output_error(mount->where(), "mount: mountpoint " + + mount->mountpoint() + " has already been specified; " + + mount->device() + " is a duplicate"); } else { seen_mounts.insert(mount->mountpoint()); } @@ -565,7 +548,7 @@ bool Horizon::Script::validate() const { /* REQ: Runner.Validate.mount.Block */ if(opts.test(InstallEnvironment)) { #ifdef HAS_INSTALL_ENV - CHECK_EXIST_PART_LV(mount->device(), "mount", mount->lineno()) + CHECK_EXIST_PART_LV(mount->device(), "mount", mount->where()) #endif /* HAS_INSTALL_ENV */ } } diff --git a/hscript/user.cc b/hscript/user.cc index 0dc4eba..ae2b99c 100644 --- a/hscript/user.cc +++ b/hscript/user.cc @@ -106,14 +106,14 @@ static bool is_valid_name (const char *name) /*! Determine if a string is a valid crypt passphrase * @param pw The string to test for validity. * @param key The name of key being validated ('rootpw', 'userpw', ...) - * @param lineno The line number where the key occurs. + * @param pos The location where the key occurs. * @returns true if +pw+ is a valid crypt passphrase; false otherwise. */ static bool string_is_crypt(const std::string &pw, const std::string &key, - int lineno) { + const Horizon::ScriptLocation &pos) { if(pw.size() < 5 || pw[0] != '$' || (pw[1] != '2' && pw[1] != '6') || pw[2] != '$') { - output_error("installfile:" + std::to_string(lineno), + output_error(pos, key + ": value is not a crypt-style encrypted passphrase"); return false; } @@ -121,13 +121,14 @@ static bool string_is_crypt(const std::string &pw, const std::string &key, } -Key *RootPassphrase::parseFromData(const std::string &data, int lineno, +Key *RootPassphrase::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, const Script *script) { - if(!string_is_crypt(data, "rootpw", lineno)) { + if(!string_is_crypt(data, "rootpw", pos)) { if(errors) *errors += 1; return nullptr; } - return new RootPassphrase(script, lineno, data); + return new RootPassphrase(script, pos, data); } bool RootPassphrase::validate() const { @@ -138,8 +139,7 @@ bool RootPassphrase::execute() const { const std::string root_line = "root:" + this->_value + ":" + std::to_string(time(nullptr) / 86400) + ":0:::::"; - output_info("installfile:" + std::to_string(this->lineno()), - "rootpw: setting root passphrase"); + output_info(pos, "rootpw: setting root passphrase"); if(script->options().test(Simulate)) { std::cout << "(printf '" << root_line << "\\" << "n'; " @@ -159,8 +159,7 @@ bool RootPassphrase::execute() const { /* This was tested on gwyn during development. */ std::ifstream old_shadow(script->targetDirectory() + "/etc/shadow"); if(!old_shadow) { - output_error("installfile:" + std::to_string(this->lineno()), - "rootpw: cannot open existing shadow file"); + output_error(pos, "rootpw: cannot open existing shadow file"); return false; } @@ -182,8 +181,7 @@ bool RootPassphrase::execute() const { std::ofstream new_shadow(script->targetDirectory() + "/etc/shadow", std::ios_base::trunc); if(!new_shadow) { - output_error("installfile:" + std::to_string(this->lineno()), - "rootpw: cannot replace target shadow file"); + output_error(pos, "rootpw: cannot replace target shadow file"); return false; } new_shadow << shadow_stream.str(); @@ -194,29 +192,26 @@ bool RootPassphrase::execute() const { } -Key *Username::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *Username::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { if(!is_valid_name(data.c_str())) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "username: invalid username specified"); + output_error(pos, "username: invalid username specified"); return nullptr; } /* REQ: Runner.Validate.username.System */ if(system_names.find(data) != system_names.end()) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "username: " + data + " is a reserved system username"); + output_error(pos, "username: reserved system username", data); return nullptr; } - return new Username(script, lineno, data); + return new Username(script, pos, data); } bool Username::execute() const { - output_info("installfile:" + std::to_string(line), - "username: creating account " + _value); + output_info(pos, "username: creating account " + _value); if(script->options().test(Simulate)) { std::cout << "useradd -c \"Adélie User\" -m -R " @@ -230,8 +225,7 @@ bool Username::execute() const { "-R", script->targetDirectory(), "-U", _value}) != 0) { - output_error("installfile:" + std::to_string(line), - "username: failed to create user account"); + output_error(pos, "username: failed to create user account", _value); return false; } #endif /* HAS_INSTALL_ENV */ @@ -239,19 +233,19 @@ bool Username::execute() const { } -Key *UserAlias::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *UserAlias::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { /* REQ: Runner.Validate.useralias.Validity */ const std::string::size_type sep = data.find_first_of(' '); if(sep == std::string::npos || data.length() == sep + 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "useralias: alias is required", + output_error(pos, "useralias: alias is required", "expected format is: useralias [username] [alias...]"); return nullptr; } - return new UserAlias(script, lineno, data.substr(0, sep), + return new UserAlias(script, pos, data.substr(0, sep), data.substr(sep + 1)); } @@ -260,8 +254,7 @@ bool UserAlias::validate() const { } bool UserAlias::execute() const { - output_info("installfile:" + std::to_string(line), - "useralias: setting GECOS name for " + _username); + output_info(pos, "useralias: setting GECOS name for " + _username); if(script->options().test(Simulate)) { std::cout << "usermod -c \"" << _alias << "\" " @@ -273,8 +266,7 @@ bool UserAlias::execute() const { #ifdef HAS_INSTALL_ENV if(run_command("usermod", {"-c", _alias, "-R", script->targetDirectory(), _username}) != 0) { - output_error("installfile:" + std::to_string(line), - "useralias: failed to change GECOS of user " + _username); + output_error(pos, "useralias: failed to change GECOS for " + _username); return false; } #endif /* HAS_INSTALL_ENV */ @@ -282,25 +274,25 @@ bool UserAlias::execute() const { } -Key *UserPassphrase::parseFromData(const std::string &data, int lineno, +Key *UserPassphrase::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, const Script *script) { /* REQ: Runner.Validate.userpw.Validity */ const std::string::size_type sep = data.find_first_of(' '); if(sep == std::string::npos || data.length() == sep + 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "userpw: passphrase is required", + output_error(pos, "userpw: passphrase is required", "expected format is: userpw [username] [crypt...]"); return nullptr; } std::string passphrase = data.substr(sep + 1); - if(!string_is_crypt(passphrase, "userpw", lineno)) { + if(!string_is_crypt(passphrase, "userpw", pos)) { if(errors) *errors += 1; return nullptr; } - return new UserPassphrase(script, lineno, data.substr(0, sep), + return new UserPassphrase(script, pos, data.substr(0, sep), data.substr(sep + 1)); } @@ -310,8 +302,7 @@ bool UserPassphrase::validate() const { } bool UserPassphrase::execute() const { - output_info("installfile:" + std::to_string(line), - "userpw: setting passphrase for " + _username); + output_info(pos, "userpw: setting passphrase for " + _username); if(script->options().test(Simulate)) { std::cout << "usermod -p '" << _passphrase << "' " @@ -324,8 +315,7 @@ bool UserPassphrase::execute() const { if(run_command("usermod", {"-p", _passphrase, "-R", script->targetDirectory(), _username}) != 0) { - output_error("installfile:" + std::to_string(line), - "userpw: failed to set passphrase for " + _username); + output_error(pos, "userpw: failed to set passphrase for " + _username); return false; } #endif /* HAS_INSTALL_ENV */ @@ -333,14 +323,13 @@ bool UserPassphrase::execute() const { } -Key *UserIcon::parseFromData(const std::string &data, int lineno, int *errors, - int *, const Script *script) { +Key *UserIcon::parseFromData(const std::string &data, const ScriptLocation &pos, + int *errors, int *, const Script *script) { /* REQ: Runner.Validate.usericon.Validity */ const std::string::size_type sep = data.find_first_of(' '); if(sep == std::string::npos || data.length() == sep + 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "usericon: icon is required", + output_error(pos, "usericon: icon is required", "expected format is: usericon [username] [path|url]"); return nullptr; } @@ -348,12 +337,11 @@ Key *UserIcon::parseFromData(const std::string &data, int lineno, int *errors, std::string icon_path = data.substr(sep + 1); if(icon_path[0] != '/' && !is_valid_url(icon_path)) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "usericon: path must be absolute path or valid URL"); + output_error(pos, "usericon: path must be absolute path or valid URL"); return nullptr; } - return new UserIcon(script, lineno, data.substr(0, sep), icon_path); + return new UserIcon(script, pos, data.substr(0, sep), icon_path); } bool UserIcon::validate() const { @@ -367,8 +355,7 @@ bool UserIcon::execute() const { const std::string face_path(script->targetDirectory() + "/home/" + _username + "/.face"); - output_info("installfile:" + std::to_string(line), - "usericon: setting avatar for " + _username); + output_info(pos, "usericon: setting avatar for " + _username); if(script->options().test(Simulate)) { if(_icon_path[0] == '/') { @@ -391,43 +378,39 @@ bool UserIcon::execute() const { if(_icon_path[0] == '/') { fs::copy_file(_icon_path, as_path, ec); if(ec) { - output_error("installfile:" + std::to_string(line), - "usericon: failed to copy icon", ec.message()); + output_error(pos, "usericon: failed to copy icon", ec.message()); return false; } } else { if(!download_file(_icon_path, as_path)) { - output_error("installfile:" + std::to_string(line), - "usericon: failed to download icon"); + output_error(pos, "usericon: failed to download icon"); return false; } } fs::copy_file(as_path, face_path + ".icon", ec); if(ec) { - output_error("installfile:" + std::to_string(line), - "usericon: failed to copy icon to home", ec.message()); + output_error(pos, "usericon: failed to copy icon home", ec.message()); return false; } fs::create_symlink(".face.icon", face_path, ec); if(ec) { - output_warning("installfile:" + std::to_string(line), - "usericon: failed to create legacy symlink"); + output_warning(pos, "usericon: failed to create legacy symlink"); } #endif /* HAS_INSTALL_ENV */ return true; /* LCOV_EXCL_LINE */ } -Key *UserGroups::parseFromData(const std::string &data, int lineno, +Key *UserGroups::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, const Script *script) { /* REQ: Runner.Validate.usergroups.Validity */ const std::string::size_type sep = data.find_first_of(' '); if(sep == std::string::npos || data.length() == sep + 1) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "usergroups: at least one group is required", + output_error(pos, "usergroups: at least one group is required", "expected format is: usergroups [user] [group(,...)]"); return nullptr; } @@ -440,8 +423,7 @@ Key *UserGroups::parseFromData(const std::string &data, int lineno, /* REQ: Runner.Validate.usergroups.Group */ if(system_groups.find(group) == system_groups.end()) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "usergroups: group name '" + group + "' is invalid", + output_error(pos, "usergroups: invalid group name '" + group + "'", "group is not a recognised system group"); return nullptr; } @@ -450,13 +432,12 @@ Key *UserGroups::parseFromData(const std::string &data, int lineno, /* REQ: Runner.Validate.usergroups.Group */ if(stream.fail() && !stream.eof()) { if(errors) *errors += 1; - output_error("installfile:" + std::to_string(lineno), - "usergroups: group name exceeds maximum length", + output_error(pos, "usergroups: group name exceeds maximum length", "groups may only be 16 characters or less"); return nullptr; } - return new UserGroups(script, lineno, data.substr(0, sep), group_set); + return new UserGroups(script, pos, data.substr(0, sep), group_set); } bool UserGroups::validate() const { @@ -465,8 +446,7 @@ bool UserGroups::validate() const { } bool UserGroups::execute() const { - output_info("installfile:" + std::to_string(line), - "usergroups: setting group membership for " + _username); + output_info(pos, "usergroups: setting group membership for " + _username); std::string groups; for(auto &grp : _groups) { @@ -486,8 +466,7 @@ bool UserGroups::execute() const { if(run_command("usermod", {"-a", "-G", groups, "-R", script->targetDirectory(), _username}) != 0) { - output_error("installfile:" + std::to_string(line), - "usergroups: failed to add groups to " + _username); + output_error(pos, "usergroups: failed to add groups to " + _username); return false; } #endif /* HAS_INSTALL_ENV */ diff --git a/hscript/user.hh b/hscript/user.hh index 8a093fb..dfda46d 100644 --- a/hscript/user.hh +++ b/hscript/user.hh @@ -22,22 +22,22 @@ namespace Keys { class RootPassphrase : public StringKey { private: - RootPassphrase(const Script *_s, int _line, const std::string &my_pw) : - StringKey(_s, _line, my_pw) {} + RootPassphrase(const Script *_s, const ScriptLocation &_p, + const std::string &my_pw) : StringKey(_s, _p, my_pw) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool validate() const override; bool execute() const override; }; class Username : public StringKey { private: - Username(const Script *_s, int _line, const std::string &name) : - StringKey(_s, _line, name) {} + Username(const Script *_s, const ScriptLocation &_p, + const std::string &name) : StringKey(_s, _p, name) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); bool execute() const override; }; @@ -46,12 +46,11 @@ private: const std::string _username; const std::string _alias; - UserAlias(const Script *_s, int _line, const std::string &_n, - const std::string &_a) : - Key(_s, _line), _username(_n), _alias(_a) {} + UserAlias(const Script *_s, const ScriptLocation &_p, const std::string &_n, + const std::string &_a) : Key(_s, _p), _username(_n), _alias(_a) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the username for this alias. */ const std::string &username() const { return this->_username; } @@ -67,12 +66,12 @@ private: const std::string _username; const std::string _passphrase; - UserPassphrase(const Script *_s, int _line, const std::string &_n, - const std::string &_p) : - Key(_s, _line), _username(_n), _passphrase(_p) {} + UserPassphrase(const Script *_s, const ScriptLocation &_pos, + const std::string &_n, const std::string &_p) : + Key(_s, _pos), _username(_n), _passphrase(_p) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the username for this passphrase. */ const std::string &username() const { return this->_username; } @@ -88,12 +87,12 @@ private: const std::string _username; const std::string _icon_path; - UserIcon(const Script *_s, int _line, const std::string &_n, + UserIcon(const Script *_s, const ScriptLocation &_p, const std::string &_n, const std::string &_i) : - Key(_s, _line), _username(_n), _icon_path(_i) {} + Key(_s, _p), _username(_n), _icon_path(_i) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the username for this icon. */ const std::string &username() const { return this->_username; } @@ -109,12 +108,12 @@ private: const std::string _username; const std::set<std::string> _groups; - UserGroups(const Script *_s, int _line, const std::string &_n, - const std::set<std::string> &_g) : - Key(_s, _line), _username(_n), _groups(_g) {} + UserGroups(const Script *_s, const ScriptLocation &_pos, + const std::string &_n, const std::set<std::string> &_g) : + Key(_s, _pos), _username(_n), _groups(_g) {} public: - static Key *parseFromData(const std::string &, int, int*, int*, - const Script *); + static Key *parseFromData(const std::string &, const ScriptLocation &, + int*, int*, const Script *); /*! Retrieve the username for this group set. */ const std::string &username() const { return this->_username; } |