diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2019-10-23 21:35:28 -0500 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2019-10-23 21:35:28 -0500 |
commit | 36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80 (patch) | |
tree | 4551d1a806975dc54e296f697069d325ba6abbf8 /fetch/fetch.cc | |
parent | 4c2eb0b5744d5301e3910b549debd7ddf6027cdc (diff) | |
download | horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.tar.gz horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.tar.bz2 horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.tar.xz horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.zip |
fetch: Add new executable
Diffstat (limited to 'fetch/fetch.cc')
-rw-r--r-- | fetch/fetch.cc | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/fetch/fetch.cc b/fetch/fetch.cc new file mode 100644 index 0000000..941051f --- /dev/null +++ b/fetch/fetch.cc @@ -0,0 +1,159 @@ +/* + * fetch.cc - Implementation of the HorizonScript Locator + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include <algorithm> /* transform */ +#include <cstdlib> /* EXIT_* */ +#include <fstream> +#include <map> +#include <string> +#include <unistd.h> /* access */ +#include "util/output.hh" + +static const char *IFILE_PATH = "/etc/horizon/installfile"; +static const int SCRIPT_LINE_MAX = 512; + +bool pretty = true; + +/*! Process a local path and copy it to the proper location. + * @param path The local path. + * @returns An exit code. + */ +int process_local(const std::string &path) { + /* if we can't read it, don't even bother */ + if(access(path.c_str(), R_OK) != 0) { + output_error("process_local", + std::string(path) + " does not exist or is not readable"); + return EXIT_FAILURE; + } + + std::ofstream output(IFILE_PATH, std::ios_base::trunc); + if(!output) { + output_error("process_local", + std::string(IFILE_PATH) + + " could not be opened for writing"); + return EXIT_FAILURE; + } + std::ifstream input(path, std::ios_base::in); + if(!input) { + output_error("process_local", + path + " could not be opened for reading"); + return EXIT_FAILURE; + } + char buffer[SCRIPT_LINE_MAX]; + + while(input.getline(buffer, SCRIPT_LINE_MAX)) { + output << buffer << std::endl; + } + + if(input.fail() && !input.eof()) { + output_error("process_local", "line length error reading " + path); + return EXIT_FAILURE; + } + if(input.bad() && !input.eof()) { + output_error("process_local", "I/O error reading " + path); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +/*! Download an installfile using cURL. + * @param path The remote path to download. + */ +int process_curl(const std::string &path) { + return EXIT_FAILURE; +} + + +/*! The signature of a protocol dispatch function */ +typedef int (*proto_dispatch_fn)(const std::string &); + + +/*! Protocol handlers */ +static const std::map<std::string, proto_dispatch_fn> protos = { + {"http", process_curl}, + {"https", process_curl}, + {"tftp", process_curl} +}; + + +/*! Dispatch handling of +path+ to a protocol handler, or return failure. + * @param path The remote path to download to IFILE_PATH. + * @returns EXIT_SUCCESS if the file was downloaded; EXIT_FAILURE otherwise. + * @note Currently, it is ambiguous whether EXIT_FAILURE means invalid proto + * or error downloading from the URL. + */ +int process_maybe_remote(const std::string &path) { + std::string proto = path.substr(0, path.find_first_of(":")); + std::transform(proto.begin(), proto.end(), proto.begin(), ::tolower); + + if(protos.find(proto) != protos.end()) { + return (*protos.at(proto))(path); + } + + std::string support = "This build of Horizon supports: "; + for(auto &proto_pair : protos) { + support += proto_pair.first + " "; + } + + output_error("argv", proto + " is not a valid protocol", support); + return EXIT_FAILURE; +} + + +/* + * The goal of the Locator is to find the HorizonScript for this computer, + * which is the target, and copy it to the well-known path /etc/horizon + * as 'installfile'. + */ +int main(int argc, char *argv[]) { + std::string installfile; + + if(argc < 1 || argc > 2) { + std::cerr << "usage: " << std::endl; + std::cerr << "\thscript-fetch [path|url]" << std::endl; + return EXIT_FAILURE; + } + + bold_if_pretty(std::cout); + std::cout << "HorizonScript Locator version " << VERSTR; + reset_if_pretty(std::cout); + std::cout << std::endl; + std::cout << "Copyright (c) 2019 Adélie Linux and contributors. AGPL-3.0 license." << std::endl; + std::cout << std::endl; + + if(argc == 1) { + if(access(IFILE_PATH, F_OK) == 0) { + /* That was easy™ */ + std::cout << "HorizonScript already present at " << IFILE_PATH + << ". Quitting." << std::endl; + return EXIT_SUCCESS; + } + + output_error("internal", "Fully Remote HorizonScript downloading " + "is not yet implemented"); + return EXIT_FAILURE; + } + + std::string path(argv[1]); + + if(path[0] == '/') { + return process_local(path); + } + if(path.find("://") != std::string::npos) { + return process_maybe_remote(path); + } + + output_error("argv", "Unrecognised protocol or invalid URL", + "An absolute path or URL is required"); + return EXIT_FAILURE; +} |