From 04c42f34bc67a628189de6d72167ce536cd4a4e7 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Tue, 5 Jul 2016 17:45:46 +0200 Subject: [PATCH] app/fetchurl: libcURL frontend Fixes #19 --- run/fetchurl.run | 90 ++++++++++++++++++++ src/app/fetchurl/README | 13 +++ src/app/fetchurl/component.cc | 152 ++++++++++++++++++++++++++++++++++ src/app/fetchurl/target.mk | 3 + 4 files changed, 258 insertions(+) create mode 100644 run/fetchurl.run create mode 100644 src/app/fetchurl/README create mode 100644 src/app/fetchurl/component.cc create mode 100644 src/app/fetchurl/target.mk diff --git a/run/fetchurl.run b/run/fetchurl.run new file mode 100644 index 0000000..c545545 --- /dev/null +++ b/run/fetchurl.run @@ -0,0 +1,90 @@ +# +# \brief Test of 'fetchurl +# \author Emery Hemingway +# \date 2016-06-05 +# + +set build_components { + core init + app/fetchurl + drivers/nic + drivers/timer +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +append config { + + + + + + + + + + + + + + + } + +append_platform_drv_config + +append config { + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# generic modules +set boot_modules { + core init ld.lib.so + curl.lib.so + fetchurl + libc.lib.so + libc.lib.so + libcrypto.lib.so + libm.lib.so + libssh.lib.so + libssl.lib.so + lwip.lib.so + nic_drv + timer + zlib.lib.so +} + +# platform-specific modules +append_platform_drv_boot_modules + +build_boot_image $boot_modules + +append qemu_args " -nographic -net nic,model=e1000 -net user" + +run_genode_until {child "fetchurl" exited with exit value 0} 120 diff --git a/src/app/fetchurl/README b/src/app/fetchurl/README new file mode 100644 index 0000000..e231f89 --- /dev/null +++ b/src/app/fetchurl/README @@ -0,0 +1,13 @@ +A small frontend to the libcURL library. + +Configuration +------------- + +! +! +! +! +! +! +! +! diff --git a/src/app/fetchurl/component.cc b/src/app/fetchurl/component.cc new file mode 100644 index 0000000..07db3f0 --- /dev/null +++ b/src/app/fetchurl/component.cc @@ -0,0 +1,152 @@ +/* + * \brief Native fetchurl utility for Nix + * \author Emery Hemingway + * \date 2016-03-08 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* cURL includes */ +#include + +/* Libc includes */ +#include +#include +#include + +struct Stats +{ + Timer::Connection &timer; + unsigned long next = 0; + unsigned long remain = 0; + char const *url; + + Stats(Timer::Connection &t, char const *u) + : timer(t), url(u) { } +}; + +static size_t write_callback(char *ptr, + size_t size, + size_t nmemb, + void *userdata) +{ + int *fd = (int*)userdata; + return write(*fd, ptr, size*nmemb); +} + + +int progress_callback(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow) +{ + Stats *stats = (Stats*)clientp; + + unsigned long now = stats->timer.elapsed_ms(); + unsigned long remain = dltotal-dlnow; + + if ((now > stats->next) && (remain != stats->remain)) { + stats->next = now + 1000; + stats->remain = remain; + Genode::log(stats->url, ": ", remain, " bytes remain"); + } + return CURLE_OK; +} + +void Component::construct(Genode::Env &env) +{ + Genode::Attached_rom_dataspace config(env, "config"); + + Timer::Connection timer(env); + /* wait for DHCP */ + timer.msleep(4000); + + Genode::String<256> url; + Genode::Path<256> path; + CURLcode res = CURLE_OK; + + curl_global_init(CURL_GLOBAL_DEFAULT); + + Genode::Xml_node config_node = config.xml(); + + bool verbose = config_node.attribute_value("verbose", false); + + config_node.for_each_sub_node("fetch", [&] (Genode::Xml_node node) { + + if (res != CURLE_OK) return; + try { + node.attribute("url").value(&url); + node.attribute("path").value(path.base(), path.capacity()); + } catch (...) { Genode::error("error reading 'fetch' node"); return; } + + char const *out_path = path.base(); + + int fd = open(out_path, O_CREAT | O_RDWR); + if (fd == -1) { + switch (errno) { + case EACCES: + Genode::error("permission denied at ", out_path); break; + case EEXIST: + Genode::error(out_path, " already exists"); break; + case EISDIR: + Genode::error(out_path, " is a directory"); break; + case ENOSPC: + Genode::error("cannot create ", out_path, ", out of space"); break; + } + env.parent().exit(errno); + return; + } + + CURL *curl = curl_easy_init(); + if (!curl) { + Genode::error("failed to initialize libcurl"); + res = CURLE_FAILED_INIT; + return; + } + + Stats stats(timer, url.string()); + + curl_easy_setopt(curl, CURLOPT_URL, url.string()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, verbose); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fd); + + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); + curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &stats); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + + res = curl_easy_perform(curl); + close(fd); + if (res != CURLE_OK) + Genode::error(curl_easy_strerror(res)); + + curl_easy_cleanup(curl); + }); + + curl_global_cleanup(); + + Genode::warning("SSL certificates not verified"); + + env.parent().exit(res ^ CURLE_OK); +} + diff --git a/src/app/fetchurl/target.mk b/src/app/fetchurl/target.mk new file mode 100644 index 0000000..cce4d49 --- /dev/null +++ b/src/app/fetchurl/target.mk @@ -0,0 +1,3 @@ +TARGET = fetchurl +LIBS += curl lwip libc_lwip libc_lwip_nic_dhcp libc +SRC_CC = component.cc \ No newline at end of file