summaryrefslogtreecommitdiff
path: root/fetch
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-23 21:35:28 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-23 21:35:28 -0500
commit36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80 (patch)
tree4551d1a806975dc54e296f697069d325ba6abbf8 /fetch
parent4c2eb0b5744d5301e3910b549debd7ddf6027cdc (diff)
downloadhorizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.tar.gz
horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.tar.bz2
horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.tar.xz
horizon-36d2505e1a4845cf2eeb4ce9bc2be5ce4d38ee80.zip
fetch: Add new executable
Diffstat (limited to 'fetch')
-rw-r--r--fetch/CMakeLists.txt19
-rw-r--r--fetch/fetch.cc159
2 files changed, 178 insertions, 0 deletions
diff --git a/fetch/CMakeLists.txt b/fetch/CMakeLists.txt
new file mode 100644
index 0000000..7f83a36
--- /dev/null
+++ b/fetch/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(FETCH_SRCS
+ fetch.cc
+)
+add_executable(hscript-fetch ${FETCH_SRCS})
+
+install(TARGETS hscript-fetch DESTINATION bin)
+
+IF(RSPEC_EXECUTABLE)
+add_test(NAME "RSpecFetch"
+ COMMAND ${RSPEC_EXECUTABLE} spec/fetch_spec.rb
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
+set_property(TEST "RSpecFetch"
+ PROPERTY ENVIRONMENT "PATH=$ENV{PATH}:${CMAKE_CURRENT_BINARY_DIR}")
+ENDIF(RSPEC_EXECUTABLE)
+
+IF(VALGRIND)
+add_test(NAME "ValgrindFetch"
+ COMMAND ${VALGRIND_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/hscript-fetch https://horizon.adelielinux.org/example.installfile)
+ENDIF(VALGRIND)
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;
+}