From patchwork Mon Nov 20 11:10:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefano Babic X-Patchwork-Id: 839522 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:400c:c0c::237; helo=mail-wr0-x237.google.com; envelope-from=swupdate+bncbcxploxj6ikrbohqzliakgqeyeb3w4q@googlegroups.com; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b="DL2xosa/"; dkim-atps=neutral Received: from mail-wr0-x237.google.com (mail-wr0-x237.google.com [IPv6:2a00:1450:400c:c0c::237]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ygQxk3dZjz9s4s for ; Mon, 20 Nov 2017 22:10:50 +1100 (AEDT) Received: by mail-wr0-x237.google.com with SMTP id v8sf5793627wrd.21 for ; Mon, 20 Nov 2017 03:10:50 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1511176248; cv=pass; d=google.com; s=arc-20160816; b=jYnk0oBLGGiR+NeD8mUOl10Wr2B8MkV7GGZghd94r2iV9OiXyPc9X1I79EwMw9FtVt IZK4MfSeyjqjZaHHfYrGq65vPJ5fjtPdEUim79zWQ/zM9/wpfKI1/6eBiOBcxNHcnKBv L77y4msyJCDbflchHxuE/rUGnH71Gm/ob9fBqF2Fn7vumB2EaMUV4f+A/+8CSOqwXtI9 E+gyPP4Br3nRIYf73b0c2LRVmZxP1GNZQ3XEDuNSyPj8J66J4RqgoIf/u+ZG725h6C20 XV74MetgQi6Nxfpl+rX4RMO6Qx1vjVbdO0FaqNhIy76RcbtArQwA+Dx92mpjbqtR14P3 +BaQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:arc-authentication-results :arc-message-signature:mime-version:sender:dkim-signature :arc-authentication-results; bh=RzApW62yklpDriIRgmguVmtj0/zF0o/cRtJg5tokoq0=; b=PDwpaqJ1wTUfFoGY8YYIRG2J2i1vCfqDE1JbDtlp1SYSN8ri4i/bryO1npm1bxYErd JsF+PZnipdHdmy7mYYlormB9+J7hv4J2etDg26UZk0nttUjNVECz6xUw0V4mh745s5oI /RhVqFwJ+DP0WC4iVdX/tuU6F4E9IpDciYgMBJfTx0MvCdG1mbFIXWlrDWWqELgknimj ct80uvh2/jv0RUTN/vdKtVjT2rvn4j/x49aRt0Ol1qEcTBlTiblggrJn9pFQki8cJIXA xo+X9B2xWcb6CM0XtwxszUHIbmlC30+fFznSpwL6Ei7s09s5g+U4PO6M1WFWW2X6CBpx iIsw== ARC-Authentication-Results: i=2; gmr-mx.google.com; spf=neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20161025; h=sender:mime-version:from:to:cc:subject:date:message-id:in-reply-to :references:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=RzApW62yklpDriIRgmguVmtj0/zF0o/cRtJg5tokoq0=; b=DL2xosa/FfqLFDlt8CtsN4Eh6zqglozJPXU1LZaZc6OC+4B8g8lrV3dGVrsYkLQzZA GOVoBo+R91kkjCkjh48uG+q2XwVKRTWhp1B2qrmC51cvQ53S1Q+2mFIGaTl7NIikGCbs FK3xu+XBwRFeESnonFPE0DaLzWHSL2+ET+dOpD8Qa3ezcrtdEHvv5T6Oqsojv0p9Z45Z 9nBVmS7vGlJ7OcTHrqFPAyg6TXF56bWEurOywOSCCA3CKJWR7bDvA21ZwEWgijfN/NxC 4QBJT3zSOa2bLd7MuqEvswoNAJSQwXgZ26GP+lBJi3I7p5zZTEotpWihFa0d23s2CcBd sekA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=sender:x-gm-message-state:mime-version:from:to:cc:subject:date :message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=RzApW62yklpDriIRgmguVmtj0/zF0o/cRtJg5tokoq0=; b=Z0Xor2njX1lm88wiMReSNyXBEGdH5y0F7t+gMipu4ssV9U0lQktyIgXzLekmXUeUeF pSq1v7/BYCcRXQjPAfa772y3OMGmlHHoPRH//k7wk9CzbUuIBYz02VKjjThZBf3Vs88d jXpMcsXsuq5o6g6yhtF7qD6xKnQtfmRbTpWJsPMMWS1YYoNoNyJv8/mE71QUKtnch/rk 8RH+YXHWXljNmIUkfbFJX3mpnIT9sxmXDHi4wSkmN2k3xqQkdkiXOCZVfexmBoPeFHHf UVHDmkIHd2IXPj6rtOn7A+AF2LLkatjCRGxqVI/3hNxSOXx5yeiiNXDZfxtnF80S3RgQ WbCA== Sender: swupdate@googlegroups.com X-Gm-Message-State: AJaThX6zvq0ST5qy6+BYe9PniQ4uBithAfGuZCVg2lWYbd8UWAui1aFA tX/Sldhv5eFFL94kaomHwvE= X-Google-Smtp-Source: AGs4zMaOkdfGIVwc/derhHYqdt3P8RI5sYq6ORHYfWJ7AX8wYSxSeQE6bNBGrRcuVVXPyaBjYCKX4w== X-Received: by 10.28.0.2 with SMTP id 2mr89569wma.3.1511176248302; Mon, 20 Nov 2017 03:10:48 -0800 (PST) MIME-Version: 1.0 X-BeenThere: swupdate@googlegroups.com Received: by 10.28.120.3 with SMTP id t3ls892472wmc.5.canary-gmail; Mon, 20 Nov 2017 03:10:47 -0800 (PST) X-Received: by 10.28.132.74 with SMTP id g71mr1241701wmd.15.1511176247725; Mon, 20 Nov 2017 03:10:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1511176247; cv=none; d=google.com; s=arc-20160816; b=ZysOhNo1ZOkw5p2rDUAp2rMsx53wxK1e3uzwdvf5Wih6b2Z4Im1AFziLuAybQwQGtQ gAD9pdw3ieNi1vReiGcgZcAkVDm45J9XvDudcEoEmvCR0sJVp8g35i7SgjiGzCEKDDBy JzEWOglnt2oquEFk/fTmOFbB5RD1vaPGMyQo7ElysPe2TKYa0Z79yUqAEsjhYLkoXd0/ 4Qmw5tQ97uoYKOtd18O2uj5gx47iL6EqMDNPex/MgellhZjUmDOrokoMnv9Js1XCwCpe W7jcWyG/7aHFDPfNGTQOBkPK3toPLCPF6b9qtiebNsmc/OIx9oLFZQxfK0KqvCn9wtdk nQUA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=SdyJPotmIXKladHJaT6aGL5mh3nmqg6d4KubEYVfURI=; b=mTxkczMnSjcolVMLrm1XVFNbbV/h5OI/3JQGO0JH4ZiCiVIhsvn23SaM/EGIaNMrCn FDe0hg0xcHKLN8RsmcVB0jWiGzgLmV0Yd/f1JwgRFR99hd3AcH6NzrszSFcRGYJ+PeB9 wvMU86iqY5sWCSvS34DZgj186KrwgslUGdz/g7ZWpQ33ibzq2V4/CU7dzx9qQsZ7SXIq 3n8P3jIQRePddnSwduXqoWbKwcfwIVcjGYFjrtMPOk0/QBB0VkMCx8l6Udxsr5w6DHVK baUUYANWU1ewLfp/+u9yak9324zgewFvYDwpzunhvXgWQi0RMCxJkzbK8TdpAYas4KQN RJnw== ARC-Authentication-Results: i=1; gmr-mx.google.com; spf=neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de Received: from mail-out.m-online.net (mail-out.m-online.net. [212.18.0.9]) by gmr-mx.google.com with ESMTPS id t37si824130wrc.3.2017.11.20.03.10.47 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 20 Nov 2017 03:10:47 -0800 (PST) Received-SPF: neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) client-ip=212.18.0.9; Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 3ygQxg3nSjz1qsSX; Mon, 20 Nov 2017 12:10:47 +0100 (CET) Received: from localhost (dynscan1.mnet-online.de [192.168.6.70]) by mail.m-online.net (Postfix) with ESMTP id 3ygQxg3cD4z1qqkZ; Mon, 20 Nov 2017 12:10:47 +0100 (CET) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.182]) by localhost (dynscan1.mail.m-online.net [192.168.6.70]) (amavisd-new, port 10024) with ESMTP id lXdcHfup7Zcy; Mon, 20 Nov 2017 12:10:46 +0100 (CET) Received: from babic.homelinux.org (host-88-217-136-221.customer.m-online.net [88.217.136.221]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPS; Mon, 20 Nov 2017 12:10:46 +0100 (CET) Received: from localhost (mail.babic.homelinux.org [127.0.0.1]) by babic.homelinux.org (Postfix) with ESMTP id 6E8914540378; Mon, 20 Nov 2017 12:10:45 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at babic.homelinux.org Received: from babic.homelinux.org ([127.0.0.1]) by localhost (mail.babic.homelinux.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id WpC8LhnA979c; Mon, 20 Nov 2017 12:10:42 +0100 (CET) Received: from papero.fritz.box (papero.fritz.box [192.168.178.132]) by babic.homelinux.org (Postfix) with ESMTP id 95CEF454067B; Mon, 20 Nov 2017 12:10:20 +0100 (CET) From: Stefano Babic To: swupdate@googlegroups.com Cc: Stefano Babic Subject: [swupdate] [PATCH 16/17] handler: swupdate forwarder Date: Mon, 20 Nov 2017 12:10:09 +0100 Message-Id: <1511176210-28928-16-git-send-email-sbabic@denx.de> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511176210-28928-1-git-send-email-sbabic@denx.de> References: <1511176210-28928-1-git-send-email-sbabic@denx.de> X-Original-Sender: sbabic@denx.de X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , Add a handler to forward SWU images. A global SWU can contain other SWU that are forwarded to other devices (or subsystems) running SWUpdate as well. The handler transfers an embedded SWU to all destinations assigned into sw-description at the same time (streaming works with the handler). The destinations are defined in properties for the entry: { filename = "image.swu"; type = "swuforward"; properties: ({ name = "url"; value = "http://:8080"; } ); } The handler assumes that the destinations are running SWUpdate with activated Webserver and uses the REST-API to send the image. Signed-off-by: Stefano Babic --- handlers/Config.in | 15 ++ handlers/Makefile | 1 + handlers/swuforward_handler.c | 465 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 481 insertions(+) create mode 100644 handlers/swuforward_handler.c diff --git a/handlers/Config.in b/handlers/Config.in index 5264f0d..596f069 100644 --- a/handlers/Config.in +++ b/handlers/Config.in @@ -161,6 +161,21 @@ config REMOTE_HANDLER comment "remote handler needs zeromq" depends on !HAVE_LIBZEROMQ +config SWUFORWARDER_HANDLER + bool "SWU forwarder" + depends on HAVE_LIBCURL + select CURL + select JSON + default n + help + This allows to build a chain of updater. A + SWU can contains other SWUs for other systems. + The handler takes a list of URLs and forward the + embedded SWU to the other devices using the + Webserver REST API. + +comment "SWU forwarder requires libcurl" + depends on !HAVE_LIBCURL config BOOTLOADERHANDLER bool "bootloader" diff --git a/handlers/Makefile b/handlers/Makefile index b51e3ec..f89bbad 100644 --- a/handlers/Makefile +++ b/handlers/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SHELLSCRIPTHANDLER) += shell_scripthandler.o obj-$(CONFIG_RAW) += raw_handler.o obj-$(CONFIG_REMOTE_HANDLER) += remote_handler.o obj-$(CONFIG_BOOTLOADERHANDLER) += boot_handler.o +obj-$(CONFIG_SWUFORWARDER_HANDLER) += swuforward_handler.o diff --git a/handlers/swuforward_handler.c b/handlers/swuforward_handler.c new file mode 100644 index 0000000..b59de98 --- /dev/null +++ b/handlers/swuforward_handler.c @@ -0,0 +1,465 @@ +/* + * (C) Copyright 2017 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + */ + +/* + * This handler allows to create a mesh of devices using SWUpdate + * as agent. The handler is called if an artifact is a SWU image + * and sends it to the devices provided in sw-description. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bsdqueue.h" +#include "channel_curl.h" +#include "channel.h" +#include "parselib.h" + +/* + * The Webserver in SWUpdate expets a custom header + * with the filename + */ +#define CUSTOM_HEADER "X_FILENAME: " +#define MAX_WAIT_MS 30000 +#define POST_URL "/handle_post_request" +#define STATUS_URL "/getstatus.json" + +/* + * The hzandler checks if a remote update was successful + * asking for the status. It is supposed that the boards go on + * until they report a success or failure. + * Following timeout is introduced in case boards answer, but they do not + * go out for some reasons from the running state. + */ +#define TIMEOUT_GET_ANSWER_SEC 900 /* 15 minutes */ +#define POLLING_TIME_REQ_STATUS 50 /* in mSec */ + +void swuforward_handler(void); + +/* + * Track each connection + * The handler maintains a list of connections and sends the SWU + * to all of them at once. + */ +struct curlconn { + CURL *curl_handle; /* CURL handle for posting image */ + const void *buffer; /* temprary buffer to transfer image */ + unsigned int nbytes; /* bytes to be transferred per iteration */ + size_t total_bytes; /* size of SWU image */ + char *url; /* URL for forwarding */ + bool gotMsg; /* set if the remote board has sent a new msg */ + RECOVERY_STATUS SWUpdateStatus; /* final status of update */ + LIST_ENTRY(curlconn) next; +}; +LIST_HEAD(listconns, curlconn); + +/* + * global handler data + * + */ +struct hnd_priv { + CURLM *cm; /* libcurl multi handle */ + unsigned int maxwaitms; /* maximum time in CURL wait */ + size_t size; /* size of SWU */ + struct listconns conns; /* list of connections */ +}; + +/* + * CURL callback when posting data + * Read from connection buffer and copy to CURL buffer + */ +static size_t curl_read_data(void *buffer, size_t size, size_t nmemb, void *userp) +{ + struct curlconn *conn = (struct curlconn *)userp; + size_t nbytes; + + if (!nmemb) + return 0; + if (!userp) { + ERROR("Failure IPC stream file descriptor \n"); + return -EFAULT; + } + + if (conn->nbytes > (nmemb * size)) + nbytes = nmemb * size; + else + nbytes = conn->nbytes; + + memcpy(buffer, conn->buffer, nbytes); + + conn->nbytes -= nbytes; + + return nmemb; +} + +/* + * This is the copyimage's callback. When called, + * there is a buffer to be passed to curl connections + */ +static int swu_forward_data(void *data, const void *buf, unsigned int len) +{ + struct hnd_priv *priv = (struct hnd_priv *)data; + int ret, still_running = 0; + + struct curlconn *conn; + LIST_FOREACH(conn, &priv->conns, next) { + conn->nbytes += len; + conn->buffer = buf; + } + + do { + int ready = 1; + + LIST_FOREACH(conn, &priv->conns, next) { + if (conn->nbytes > 0) { + ready = 0; + break; + } + } + + /* + * Buffer transferred to all connections, + * just returns and wait for next + */ + if (ready) + break; + + int numfds=0; + ret = curl_multi_wait(priv->cm, NULL, 0, priv->maxwaitms, &numfds); + if (ret != CURLM_OK) { + ERROR("curl_multi_wait() returns %d", ret); + return FAILURE; + } + + curl_multi_perform(priv->cm, &still_running); + } while (still_running); + + if (!still_running) { + LIST_FOREACH(conn, &priv->conns, next) { + /* check if the buffer was transfered */ + if (conn->nbytes) { + ERROR("Connection lost, data not transferred"); + } + conn->total_bytes += len - conn->nbytes; + if (conn->total_bytes != priv->size) { + ERROR("Connection lost, SWU not transferred"); + return -EIO; + } + } + ERROR("Connection lost, skipping data"); + } + + return 0; +} + +/* + * Send a GET to retrieve all traces from the connected board + */ +static int get_answer(struct curlconn *conn, RECOVERY_STATUS *result, bool ignore) +{ + channel_data_t channel_cfg = { + .debug = false, + .retries = 0, + .retry_sleep = 0, + .usessl = false}; + channel_op_res_t response; + channel_t *channel = channel_new(); + + /* + * Open a curl channel, do not connect yet + */ + if (channel->open(channel, &channel_cfg) != CHANNEL_OK) { + return -EIO; + } + + if (asprintf(&channel_cfg.url, "%s/%s", + conn->url, STATUS_URL) < 0) { + ERROR("Out of memory.\n"); + return -ENOMEM; + } + + /* Retrieve last message */ + response = channel->get(channel, (void *)&channel_cfg); + + if (response != CHANNEL_OK) { + channel->close(channel); + free(channel); + free(channel_cfg.url); + return -1; + } + + json_object *json_data = json_get_path_key( + channel_cfg.json_reply, + (const char *[]){"Status", NULL}); + if (json_data == NULL) { + ERROR("Got malformed JSON: Could not find Status"); + DEBUG("Got JSON: %s\n", json_object_to_json_string(json_data)); + return -1; + } + int status = json_object_get_int(json_data); + + json_data = json_get_path_key( + channel_cfg.json_reply, + (const char *[]){"Msg", NULL}); + const char *msg = json_object_get_string(json_data); + + json_data = json_get_path_key( + channel_cfg.json_reply, + (const char *[]){"LastResult", NULL}); + if (json_data == NULL) { + ERROR("Got malformed JSON: Could not find Last Result"); + DEBUG("Got JSON: %s\n", json_object_to_json_string(json_data)); + return -1; + } + int lastResult = json_object_get_int(json_data); + + if (strlen(msg) > 0) + conn->gotMsg = (strlen(msg) > 0) ? true : false; + + if (!ignore) { + if (strlen(msg)) { + TRACE("Update to %s : %s", conn->url, msg); + } + if (status == IDLE) { + TRACE("Update to %s : %s", conn->url, + (lastResult == SUCCESS) ? "SUCCESS !" : "FAILURE"); + } + } + + free(channel_cfg.url); + channel->close(channel); + free(channel); + + *result = lastResult; + + return status; +} + +static int retrieve_msgs(struct hnd_priv *priv, bool ignore) +{ + struct curlconn *conn; + int ret; + int result = 0; + + LIST_FOREACH(conn, &priv->conns, next) { + int count = 0; + do { + ret = get_answer(conn, &conn->SWUpdateStatus, ignore); + if (!conn->gotMsg) { + usleep(POLLING_TIME_REQ_STATUS * 1000); + count++; + } else + count = 0; + if (count > ((TIMEOUT_GET_ANSWER_SEC * 1000) / + POLLING_TIME_REQ_STATUS)) { + ret = -ETIMEDOUT; + } + } while (ret > 0); + if (ret != 0 || (conn->SWUpdateStatus != SUCCESS)) { + ERROR("Update to %s was NOT successful !", conn->url); + result = -1; + } + } + + return result; +} + +static int install_remote_swu(struct img_type *img, + void __attribute__ ((__unused__)) *data) +{ + struct hnd_priv priv; + struct curlconn *conn; + int ret, still_running = 0; + struct dict_entry *url; + struct curl_slist *headerlist; + CURLMsg *msg = NULL; + + /* + * A single SWU can contains encrypted artifacts, + * but the SWU itself canot be encrypted. + * Raise an error if the encrypted attribute is set + */ + + if (img->is_encrypted) { + ERROR("SWU to be forwarded cannot be encrypted"); + return -EINVAL; + } + + /* Reset list of connections */ + LIST_INIT(&priv.conns); + + /* initialize CURL */ + ret = curl_global_init(CURL_GLOBAL_DEFAULT); + if (ret != CURLE_OK) { + ret = FAILURE; + goto handler_exit; + } + priv.cm = curl_multi_init(); + priv.maxwaitms = MAX_WAIT_MS; + priv.size = img->size; + + /* + * Parse handler properties to get URLs for destination + * + */ + LIST_FOREACH(url, &img->properties, next) { + char curlheader[SWUPDATE_GENERAL_STRING_SIZE + strlen(CUSTOM_HEADER)]; + + if (!url->varname || !url->value || strcmp(url->varname, "url")) + continue; + + conn = (struct curlconn *)calloc(1, sizeof(struct curlconn)); + if (!conn) { + ERROR("FAULT: no memory"); + ret = -ENOMEM; + goto handler_exit; + } + + headerlist = NULL; + + conn->curl_handle = curl_easy_init(); + conn->url = url->value; + + if (!conn->curl_handle) { + /* something very bad, it should never happen */ + ERROR("FAULT: no handle from libcurl"); + return FAILURE; + } + + snprintf(curlheader, sizeof(curlheader), "%s%s", CUSTOM_HEADER, img->fname); + headerlist = curl_slist_append(headerlist, curlheader); + + if ((curl_easy_setopt(conn->curl_handle, CURLOPT_POST, 1L) != CURLE_OK) || + (curl_easy_setopt(conn->curl_handle, CURLOPT_READFUNCTION, + curl_read_data) != CURLE_OK) || + (curl_easy_setopt(conn->curl_handle, CURLOPT_READDATA, + conn) !=CURLE_OK) || + (curl_easy_setopt(conn->curl_handle, CURLOPT_USERAGENT, + "libcurl-agent/1.0") != CURLE_OK) || + (curl_easy_setopt(conn->curl_handle, CURLOPT_POSTFIELDSIZE, + img->size)!=CURLE_OK) || + (curl_easy_setopt(conn->curl_handle, CURLOPT_HTTPHEADER, + headerlist) != CURLE_OK)) { + ERROR("curl set_option was not successful"); + ret = FAILURE; + goto handler_exit; + } + + /* get verbose debug output please */ + curl_easy_setopt(conn->curl_handle, CURLOPT_VERBOSE, 1L); + + char *posturl = NULL; + posturl = (char *)malloc(strlen(conn->url) + strlen(POST_URL) + 1); + sprintf(posturl, "%s%s", conn->url, POST_URL); + + /* Set URL */ + if (curl_easy_setopt(conn->curl_handle, CURLOPT_URL, posturl) != CURLE_OK) { + ERROR("Cannot set URL in libcurl"); + free(posturl); + ret = FAILURE; + goto handler_exit; + } + free(posturl); + curl_multi_add_handle(priv.cm, conn->curl_handle); + LIST_INSERT_HEAD(&priv.conns, conn, next); + } + + retrieve_msgs(&priv, true); + + curl_multi_perform(priv.cm, &still_running); + + ret = copyimage(&priv, img, swu_forward_data); + + if (ret) { + ERROR("Transferring SWU image was not successful"); + goto handler_exit; + } + + /* + * Now checks if transfer was successful + */ + int msgs_left = 0; + while ((msg = curl_multi_info_read(priv.cm, &msgs_left))) { + CURL *eh = NULL; + int http_status_code=0; + if (msg->msg != CURLMSG_DONE) { + ERROR("curl_multi_info_read(), CURLMsg=%d\n", msg->msg); + ret = FAILURE; + break; + } + LIST_FOREACH(conn, &priv.conns, next) { + if (conn->curl_handle == msg->easy_handle) { + eh = conn->curl_handle; + break; + } + } + + if (!eh) { + ERROR("curl handle not found in connections"); + ret = FAILURE; + break; + } + + curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &http_status_code); + + if (http_status_code != 200) { + ERROR("Sending %s to %s failed with %d", + img->fname, conn->url, http_status_code); + ret = FAILURE; + break; + } + } + + /* + * Now check if remote updates were successful + */ + if (!ret) { + ret = retrieve_msgs(&priv, false); + } + +handler_exit: + LIST_FOREACH(conn, &priv.conns, next) { + LIST_REMOVE(conn, next); + curl_multi_remove_handle(priv.cm, conn->curl_handle); + curl_easy_cleanup(conn->curl_handle); + free(conn); + } + + curl_multi_cleanup(priv.cm); + + return ret; +} + +__attribute__((constructor)) +void swuforward_handler(void) +{ + register_handler("swuforward", install_remote_swu, + IMAGE_HANDLER, NULL); +}