From patchwork Thu Jun 2 09:11:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638261 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=GyPdmoHx; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::13a; helo=mail-lf1-x13a.google.com; envelope-from=swupdate+bncbdd6bwv65qpbb3p54gkamgqe4wwgw5y@googlegroups.com; receiver=) Received: from mail-lf1-x13a.google.com (mail-lf1-x13a.google.com [IPv6:2a00:1450:4864:20::13a]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDKz63sYlz9s0w for ; Thu, 2 Jun 2022 19:12:18 +1000 (AEST) Received: by mail-lf1-x13a.google.com with SMTP id c25-20020ac25f79000000b00478f05f8f49sf2245797lfc.20 for ; Thu, 02 Jun 2022 02:12:18 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161134; cv=pass; d=google.com; s=arc-20160816; b=buD+nm1Oayl/ebC0O3RuGJ5SVnUWcbX6s6ovioE3AsoIrks0msRJekbMk9AIqm5sX7 sgsOBQrvZU9M2NkEP5gcY5i8VeKgiuLMMXYkcGFcB3v4l+0qoU2Qew/n4vgZx9iBKCtN ClSjQ1vXPyGdHxa2wRO1PRc04ipTpHTZBRabdIwOmjIbWMn9Iorsjy2F/GiAaluVOnKC fp0pNRyyTassqPeSJ+hIK7DFr/xbQNxpKPg6M4YqnVDs0ChbQeaatEd5bZQvRmZmfV+J pdj88soirO0C/jvqXivqFe8y+Daj+CGLsD6tVztJ+gXVbHpL/jzhSTr5wUSbbbpC3cMu 6g/A== ARC-Message-Signature: i=3; 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:mime-version:message-id:date :subject:cc:to:from:sender:dkim-signature; bh=MglTw9LkO9qtxJo0gnIeKDs9W/pmgzBsbQyRXDDucvc=; b=jj2J9HXnCUBIEbrIu5LIWW0eyUpvfy0wRdYiQ8sTf1krmoyoYKtjbfSHRL/ZFFGexE qB272MK92Pwm9jdcNH0AQU18NNKMONrz3+9yJ0XfexJbF8NGYhbv+PI9fJJcyiX9BweF jzPIcN0xor32nn04zro3Q0ce/LapQk1tAvNYD0ofPWeR18v73CqHugGJDiL02JWWTmVX RMv/xXoMmgbT1t/fzG0ICAgk3KwGpg+BOAq0pBXO9DJmJ0u7B7OMOO12ql+0HLBcqLhP 0rXbyAAmNoXeFzygX9kThfjU/Hj06hAW2K+P5QNudcAneL1Ojb/lHP+y+k9pf7Rtbavi uh0g== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=WStpLaCG; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe09::61b as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:mime-version :x-original-sender:x-original-authentication-results:precedence :mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=MglTw9LkO9qtxJo0gnIeKDs9W/pmgzBsbQyRXDDucvc=; b=GyPdmoHx/BZ6Lou8K2na8eM13qbl38q6Qb0Hl9E11psz+ZUpJJbnXCzt8ZhXfYbDL9 yuxOY6jMCgSCRxljOgAXLPPVZRVDzKLXH4GRwRbRwY0B5Y1qFyvzqK8TDBmLqZqTeRfs vMBnHtBDe/6RCMERxMAAI/sM0iAyKZetL0LQdHuxIy7E8B8M+YNM5uTcZZRx3D4CghE9 QNUECOOKpdJmL35+YS6ydO6Nu6lhzcmP9cKFSTmDKHRpkpWSeklQQdsBsHvQdpG1gio+ f0fpga7XhVncJO4sjxWcSydU76WISXk8EV6/VJN1ildlhNphO7+Duq2sXSHLTuNiLWYY b8Vg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :mime-version: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=MglTw9LkO9qtxJo0gnIeKDs9W/pmgzBsbQyRXDDucvc=; b=xk8hIyf74MnTd73LXUvEXvPkPuagJ9gqMdQQu9iHPPjugMNI/rZmwJjosamAoau47L oP1Hyf8vCdb8oEu11NLV5Mas+8Uw87212p66XFrWIS/nXH5nIezG6AvzJEr9OFpzS7EY 2WWAg3bN5k9nsDu19ootmEF93NM2P5t6+OozqB+6Dkia+0zDth+AykKYggJ125LwGYwB +fhVi1E6TnNXWrVFsPEj+Ude5xZapH+sbKB7iKxSqbzOqnRg51Mk2UoEiLmUYHNXD+PL E/sVO+DWwW9YJLdhCNuZSnsqtN2Pi7+ki35ohBQcgiK1eLzSUtQfbqcH/wYYa6c50bs/ vjcQ== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM533yN5cRDvhpq6SKlqqYw1sNdr1cCc9L2j29lHcpbNKpSjOIqIbK 5Nxs0jSKtl+xaYF15PE9NxE= X-Google-Smtp-Source: ABdhPJzkuUGVyE7qSCsQ9P4feCwHxYyZOhIqpXdFgEjhpqfM/TmfR5RkDCWeHmsmAsYRuKgVtV7jDA== X-Received: by 2002:a05:6512:6c9:b0:478:dc36:b25b with SMTP id u9-20020a05651206c900b00478dc36b25bmr16078046lff.369.1654161134025; Thu, 02 Jun 2022 02:12:14 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:6512:1693:b0:448:3742:2320 with SMTP id bu19-20020a056512169300b0044837422320ls393208lfb.1.gmail; Thu, 02 Jun 2022 02:12:12 -0700 (PDT) X-Received: by 2002:ac2:4e0a:0:b0:478:9a7d:95a2 with SMTP id e10-20020ac24e0a000000b004789a7d95a2mr31353530lfr.257.1654161132791; Thu, 02 Jun 2022 02:12:12 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161132; cv=pass; d=google.com; s=arc-20160816; b=ndxHVUx5myry0BT9Uo8MostsJfkDhvAjTjjJmdDlHPi4OySqgQu1okLJ6EdA6BPz15 ojnOaQmBsiRiSJQPWxLrgYkzunlWPvspOjZO6khZm+ysYlAThT/vDp1I1zIY2j8F0iJ2 Ft93LhPegCnESSUGGX86WgrZViVgviLal1M/FDpRwu2jiOM/EgxUnPiZcNHJlVzVPNMz ApWkrcrG5ScWm7YYjE1GagTMS+lka9oRC+FR9D9ItauLVcegZYht2QyznDII8R/doBk4 wq5uyZhnCk9O9UzOD6RdlhPeWuuda/58B3PSAPXpmHGx+OPEu3TjwbSQEru3Zq2zyGhj +aeQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:dkim-signature; bh=56jaWPhOQ5q+ZeBBLX0YfPG4mt4Y4QLTXhWYhSn6oXk=; b=jxik+qr6WCeZK1QKosz6F+pxXb0u4pMTY4gkRiq2pBtR2yFGvJzE+97hFvp6ZcKO8w JCsTcdi2/HgoOAGBe6xdlrYP1wdol9MDD64cmJWLpq6B8CxBwy3iIzpEtxqyusJ1p9+8 36tmHu3Nfjqe52sarPJV+dEIbF37GzXvZFXGBCdkkW/nqufbWBjNNvud7TxUy0jo+LBP tFiCZV/gr34nH4vAfgV38YdiiXwoizkAlQ0jV6v9XZijGsg2y9fwrs8TRHwZYwKQgMiM Avib8laiH3QuwVYjQueFeKNVpJM9PMBY+HnqISnZuqucXLr+yoS8p+LA1F4Utu5VBEjs d3BA== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=WStpLaCG; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe09::61b as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR03-VE1-obe.outbound.protection.outlook.com (mail-ve1eur03on061b.outbound.protection.outlook.com. [2a01:111:f400:fe09::61b]) by gmr-mx.google.com with ESMTPS id u23-20020a05651c131700b0024eee872899si202142lja.0.2022.06.02.02.12.12 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:12:12 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe09::61b as permitted sender) client-ip=2a01:111:f400:fe09::61b; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Gvx30X5IkKDqy6zVfpBP2IqURzFPBYAYmUfhT40iFat7j2bmDMwzfdfVlnMHafd30uRTMviLjnVmBMyK9ZE1lMvId6aG0z9srBFueIcvTfBv94bq+5qG2ZPUOV3FirUAIq6OMOKZHN2SBkEhtyPylro+FP1QN831Lm5dj/GoK7gRi61Bfcy6ujhRv1le6S2m8f3nCsVMnJktxmKyjSGmvKbv1sIxQ/C1/LGXHQ4bU9N190ScSX/b21noNyqHxhSeRkH3IfitV1vsSV2QsuRaBZOZhZgMoDwMq+MIMxNtclk8sDjC5iPJ71sQi9ZtEZu3Pl5eRm4NLLtHfBnEnGSkjQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=56jaWPhOQ5q+ZeBBLX0YfPG4mt4Y4QLTXhWYhSn6oXk=; b=QgtEL6XAmR3GJ5rfk2xdhjBlYnGVxM11XMng/KgUECTd9/DfNWZcxLu6ffQVKVoEVkS+/t9AO8JwvRAHfWIdd9mCacuTOl3qeSRtZzZzqjYNL87eft0YqskvqWilVp6qi/My9sMitFjOcIyfz1ZLCkozG3k7NFEEZg0Hloq+wWrikJ+hWbQ3FtYaiR7sjtN3Zouk6suWJcwAKTzy5bfTBmTxlWIaVGmkN2Gfrlo4jN1YcgjmgFzd1XQGW61DYAn1TyJM4TnSaePNmXTiA0xfkCcwtHudgTaa0QiJvVwGPKEh+DlIWfaf5yifnx9+zIw0U6jCRYUJ6J7o9YM54qD2fQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.72) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from AS8PR04CA0177.eurprd04.prod.outlook.com (2603:10a6:20b:331::32) by AM6PR10MB2439.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:20b:48::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5293.16; Thu, 2 Jun 2022 09:12:09 +0000 Received: from VE1EUR01FT016.eop-EUR01.prod.protection.outlook.com (2603:10a6:20b:331:cafe::ef) by AS8PR04CA0177.outlook.office365.com (2603:10a6:20b:331::32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:09 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.72) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.72 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.72; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.72) by VE1EUR01FT016.mail.protection.outlook.com (10.152.2.227) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:09 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SMA.ad011.siemens.net (194.138.21.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:12:09 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:12:08 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 1/7] channel_curl: Map response code for file:// protocol Date: Thu, 2 Jun 2022 11:11:41 +0200 Message-ID: <20220602091147.53323-1-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC89XA.ad011.siemens.net (139.25.226.103) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 20a4ed51-9ec7-4d7c-22ac-08da4477f902 X-MS-TrafficTypeDiagnostic: AM6PR10MB2439:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 8dDQ56J82VZimuMlI/sVNnWGg6igRKH1rVrJYnLx5BAVWdK5Pn5qRP/S2pKe1Selg0/yEQe6bphtdGD4oqZdfm20qeRZYFMSpmAFQNnPbSgjEwhVWsc0hkGBePp7JIq06fsmMqzM7eaRW7QxMMcB6ybszT150Spg7J9I9MRWdOjRZG67eo3aIImGHxgpUgeSUA6BOEuZnxXUUe47TKQLKNa0brxt82vpmMtGViWhHAuo77cY0kQKfoeWG6uHUOAqfnvC70Ap3nFXNz0hgkTVDY5wtyLTd4HfiXsiU2X3fSFHS/rqueErJ5p7y0wlNaFpStL8sFQR6QMdqqtb7+TppfNXKh2ptu9h+0fjey75Vn4kxmBP20ZO7A5q1SBYpQ442Mx1HXKFCpGPf8iz4sdssH7/PUi1q4MfnhKg6SwNF30uZnxPAY/lnN0Ota5J4gFOlTyRVoFNmtcc4+zP6Wcs2RzTDQyo24RTx6pFaYofZld6LVnng4EhSV+BxRigBVmvRxHrqKU+x7QuwyzdaOWcFgD1PfM4/nbz7B7la/XylNuQXlHHtaahtKOUfJmfquWgmPNNY2IMrs/1xnoXJNPWALaA1DjEx+tqbXl6Ua9CnIQs8X+Dw8VLPDxVk0aMCbX8Y4bpLLUuuZ1KbMyf5DVIB9/4SLmVeq2E8yrY+rVPn4L1fNujysC2uFSHi5H97NGbAIdcM1CfDKCXRlhbLEfPWw== X-Forefront-Antispam-Report: CIP:194.138.21.72;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230001)(4636009)(40470700004)(36840700001)(46966006)(1076003)(81166007)(36860700001)(316002)(107886003)(956004)(2616005)(44832011)(508600001)(4326008)(82310400005)(36756003)(70206006)(8676002)(8936002)(26005)(6916009)(70586007)(86362001)(6666004)(16526019)(2906002)(82960400001)(186003)(356005)(83380400001)(47076005)(336012)(40460700003)(5660300002)(36900700001);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:12:09.5286 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 20a4ed51-9ec7-4d7c-22ac-08da4477f902 X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.72];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR01FT016.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM6PR10MB2439 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=WStpLaCG; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe09::61b as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , When using the file:// protocol with curl, there's naturally no HTTP response code. Hence, map it to success to not fail the update. Signed-off-by: Christian Storm --- corelib/channel_curl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c index 87a104a..1426390 100644 --- a/corelib/channel_curl.c +++ b/corelib/channel_curl.c @@ -299,6 +299,7 @@ char *channel_get_redirect_url(channel_t *this) channel_op_res_t channel_map_http_code(channel_t *this, long *http_response_code) { char *url = NULL; + long protocol; channel_curl_t *channel_curl = this->priv; CURLcode curlrc = curl_easy_getinfo(channel_curl->handle, CURLINFO_RESPONSE_CODE, @@ -312,7 +313,12 @@ channel_op_res_t channel_map_http_code(channel_t *this, long *http_response_code return CHANNEL_EINIT; } switch (*http_response_code) { - case 0: /* libcURL: no server response code has been received yet */ + case 0: /* libcURL: no server response code has been received yet or file:// protocol */ + curlrc = curl_easy_getinfo(channel_curl->handle, CURLINFO_PROTOCOL, + &protocol); + if (curlrc == CURLE_OK && protocol == CURLPROTO_FILE) { + return CHANNEL_OK; + } DEBUG("No HTTP response code has been received yet!"); return CHANNEL_EBADMSG; case 401: /* Unauthorized. The request requires user authentication. */ From patchwork Thu Jun 2 09:11:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638264 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=c4KbbDSt; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::13d; helo=mail-lf1-x13d.google.com; envelope-from=swupdate+bncbdd6bwv65qpbbep64gkamgqeoikbjka@googlegroups.com; receiver=) Received: from mail-lf1-x13d.google.com (mail-lf1-x13d.google.com [IPv6:2a00:1450:4864:20::13d]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDKzn12zpz9s0w for ; Thu, 2 Jun 2022 19:12:52 +1000 (AEST) Received: by mail-lf1-x13d.google.com with SMTP id g11-20020a05651222cb00b0047872568226sf2274107lfu.3 for ; Thu, 02 Jun 2022 02:12:52 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161169; cv=pass; d=google.com; s=arc-20160816; b=DAD2v74Dpn219oZI2US/mNpcJ9Ia4/LinKwkDy2Kgw0Hx4Syd8w3vhsS3MVZhuZs2h PGct0RtkkhuX8mr1zOHO1upa9GXXO12nMDNqLEeX1rHByeRiVg7brbetcJ9A6d5KhUvF zzzJOhP+iFP56B+GgUuiLnQOSyqCtmC7cSL6iBRaR5YHD7AUpf+0WrIOT/jP/t3JDCVd nu9b5aKPF6L1jLdtkS5yqQTymlVweMjdrI+QcKrAN+l86FQwSliED/SZvQ3geV6m4oiA 0S2BGUaWPFSvn3k+GVXTmWhluzYsY/OpGuuSWdypbD9r7uCHFlB1PAvqQn+UiwGhyET7 4OKw== ARC-Message-Signature: i=3; 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:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:dkim-signature; bh=UtR3JRCuB0R+PfQFX6uxf8eFR88v3g6rI8lPNKHbRGo=; b=W8EmYwnDA7218LtOuyICv+KcWO0hvQSRPwsmwLXReZaYloWq2mz8B96WRBEraWQDmo azmCdY102VwH95x5KQoQ2pp/kamwasHv8EEA4XDFu5ct9GPPmgxfSMK7V5Iu9wr3x+qs ezx4yE3Z7vkO/4ifnsSexif134vTMHCzFU1044IcO5pM0CjV5WotwOiAy/AHI5SdLWjG zCUjUdcsAOBPJ5y1fVl0tflx5CneQNCEbem4VL8bCVVVtrgzaKdFtZ4IL6Jvr6z/AS8f ARyjl4o9+x9ApgzZGYpxO+zKUbfZea+0Uap5Y/UTmAmelJVg3vq1y1+Q8ZUIykeVrGWj 6I3w== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=BCB1pZOQ; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe0c::60f as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=UtR3JRCuB0R+PfQFX6uxf8eFR88v3g6rI8lPNKHbRGo=; b=c4KbbDStsos8mfJq8zthsetLNzmfAJSDkS24rZib7uDgITPwa7AZV9BEBQbXOfpjar 1GK2l+BGw93G/lDXgadw6paqV31fmBkLlxtYINny4Gr4rrcjqIVqaQeulGw14qGHGAtZ rwL+YVV/VGctYfdbtMJnNIfAJu9nt0TJVknQVvzgZjO3MaLSns98O2lFSUsgWVKvMAbT 1/nkMWkUo3aNgBbKCC0ns90Db09RB6TTL7vabaLusYGA1Xz9x2Xw8rhDy7G0xx3bK2xY o8W9eXFW5JRoCJ30Q7mRzy4WMb0vOyhH6C9RJuexytVWKsizE/9HhXBblbleLO22vqrU 5WLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version: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=UtR3JRCuB0R+PfQFX6uxf8eFR88v3g6rI8lPNKHbRGo=; b=pw/lXOGzGXazn59+espLvyc94wKBezJIQRMqC+AqiYkx3QdhY8foq7qZ3SHaqao5Qw waLHoIeyjUhJmoE2BGOiEZXqDT+TOqnRrk/beJlcFcxk09fZypzhPySlYO0pvl4Tsvn1 o5SssLx8kelbEUy/F7PEL03T9SVH8PYRfRol609zfK93R8p+4YrPN9XwJOeySr0x6UdY zKeCDM5WfBeMLwwCtOUcJt451xYx8vjEcuyRZUqFLwCAplywN/+sS48wmJKHKhd69Xg2 IWbMVXkFQWq1EuFIv5DkmUM/bgwhQCniGbtvPBR8BJOmN3JtnPr98x/n0TovEG6HkQt8 CYdA== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM532UG2tfa2/d/sWteOKmbjOUNl9RRkInChScXlUI6yGGltHv3Nkn ucnaIQuY0eDSc/FmCLM2hB8= X-Google-Smtp-Source: ABdhPJzmHM7RyVZF6aJWz4p3fmNLBTmGMn/ZiB05ToZDxeaeaKl895DrOGljQfJIUeYM40vYOcpDBA== X-Received: by 2002:a2e:6d09:0:b0:255:7443:6f28 with SMTP id i9-20020a2e6d09000000b0025574436f28mr1606998ljc.316.1654161169658; Thu, 02 Jun 2022 02:12:49 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:6512:10cb:b0:478:7256:822a with SMTP id k11-20020a05651210cb00b004787256822als394022lfg.3.gmail; Thu, 02 Jun 2022 02:12:48 -0700 (PDT) X-Received: by 2002:a05:6512:234f:b0:479:100e:1ce9 with SMTP id p15-20020a056512234f00b00479100e1ce9mr1465493lfu.192.1654161168516; Thu, 02 Jun 2022 02:12:48 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161168; cv=pass; d=google.com; s=arc-20160816; b=A8nEWPELEuWx1ArNG4xPW9+WOSWd713/i8GbiFzHylHaURrl0RQFfZA9fhNnaixkpJ bSAY71UpK+P2AnmeUnwtx7PC3idnkLNDqqqAqQkTMMD6CA/JPQnH0QnotCgfVIG4lbfZ tdsZE+aNw7EgsH8Al/V7ZPveRB2pXRhQdnW0cc69/l6S/flE1UZT7lOAZZr3/OwlSMNH 4chCfI/I3y2Osh7/b95URh8UkiWW7335jhFpkjyTDYJYbJ2/BMnUdmfBartlepTVh6Gz pwiQjXGX2ZrJQnrCffhUT2oEMZwgGQo02pkY21rMgayQZZIhBmwhU0v3rsiREzX/PCaW PydQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=znL3cwmmH2m7mf5rZ9w7Wkc/hJdlri351E5Fz1IF4n4=; b=yTvjZjLwsnkMIgoMAVL95hJbuhnRPLOcBSMlmjNwhG6BQZe/N3t3T5R9a6My3NW2Le apOm3pNCVnYl5Rg5tzz2tBbLoa00o53SYCnFcp+orXuSAxhY8T+NlYxBukCbGD2GIvCG H3917xx4ZHO7AnhUvZv6JIzg8B/9PdIT1ivt3dgO07Ghq61AtngY1S+fkANdZbOEifI4 Rh24Y04nm6dYE5kffFtFnmKYAvumc1VSI4tjWDEj2mNlF7E19G8MS+i3iOfgUePelDQW QTtj+aHkrmuaexUimP7TGcV4OlJgMSjCRjIfZCxNbFQ/Dt6fMLP+MSLp2AP/Xu2oI/4c 4OeQ== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=BCB1pZOQ; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe0c::60f as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR04-DB3-obe.outbound.protection.outlook.com (mail-db3eur04on060f.outbound.protection.outlook.com. [2a01:111:f400:fe0c::60f]) by gmr-mx.google.com with ESMTPS id h9-20020a2ea489000000b0024e33a076e7si165454lji.2.2022.06.02.02.12.48 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:12:48 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe0c::60f as permitted sender) client-ip=2a01:111:f400:fe0c::60f; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=es0NDzw3wXKU7spZQZT9VrBTgtluWsd/V/amMSCrFUX/LRI2ELscPEGICiUIfciW/Lfp/1v1F9qhDWSAkYUlBXtIXFiN5UseU24NjHb2Z1iFeFDUr/HB2/HbTO5RTBcT2rCjuG42/56fqpPeYtW9/X/JzfZvc0Kv5IdKWKBLyNwbJKkwrzeaf1JY2jfQgIdFdNn4oPW6+hcdItWjvGqkgmtQY1kPM26X+/OWwXOa8WqGKRk4Ekq7EiWJebwKGsnOVdwuTR14aOAexPw09ucZ7v7dAW99EHUrFWrMkBTJ6ppkvriv4Toifn2DVH1uPyXFichIXJQ0vMGB4Ug0mv8bmA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=znL3cwmmH2m7mf5rZ9w7Wkc/hJdlri351E5Fz1IF4n4=; b=dj3xdvfUKw2lhhEdAF8qJwF2xxqH4CKRbn+gnMVdHQcttxFrW+I9Gsq6CSm9YRsXHexBaXPN63sTxjBhYBGTad+zNgFA7UxPi85mKO5q7bVqNMmezAM1vsK4/wP/NFYtE+En/FAc97ALHpFTClPNtwaqIRENbHJaoX68B/h8XmuhJTnKx4FA6wrmrdIeb75z95TU6BN6vwUV2yEJOzyTofRfo7B5eHBs3MrKuoVXTC19RihhNlW7oloVyMqTVhxg6ZBzAypt3Rv8uH8KWYiFiDGrWIy+YEfxbFNnorhwhU+QyPdQ7q0aC7UtcaKAtdnTyIXdwiYvOQ3wJgA2/VEJBg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.70) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from AM3PR05CA0133.eurprd05.prod.outlook.com (2603:10a6:207:3::11) by AM4PR1001MB1364.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:200:96::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5293.18; Thu, 2 Jun 2022 09:12:47 +0000 Received: from VE1EUR01FT018.eop-EUR01.prod.protection.outlook.com (2603:10a6:207:3:cafe::5f) by AM3PR05CA0133.outlook.office365.com (2603:10a6:207:3::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:47 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.70) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.70 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.70; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.70) by VE1EUR01FT018.mail.protection.outlook.com (10.152.2.221) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:46 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SJA.ad011.siemens.net (194.138.21.70) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:12:16 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:12:16 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 2/7] Lua: Publicize getversion() Date: Thu, 2 Jun 2022 11:11:42 +0200 Message-ID: <20220602091147.53323-2-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220602091147.53323-1-christian.storm@siemens.com> References: <20220602091147.53323-1-christian.storm@siemens.com> MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC89XA.ad011.siemens.net (139.25.226.103) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 8dbecd09-7e29-4f0d-26fd-08da44780f3d X-MS-TrafficTypeDiagnostic: AM4PR1001MB1364:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: WBofe6d5UtFjHSDoIKsp9irdiOI2ih1lQ99+UR1tljqTqp09dDJv1JD1LzQPzzyKkT//Ik1x1L9ddjqRyGXAMNGwgmSSWGOvJWxh/3gGsF2JWqHaQ6GNzKIztoqQ5KHLNfIqiPTWELpmCbhvlB176L3bWKGAdFay8Roq7twVy8RulH5Rl6aB/6rcWqQ+HNLF8ufYECbC/XpGt4b37cuNsf4nECpuE3MjTrjqC9KWyrtpszS/qnpfyKD4TV3bHGlgMKt69t/+VmiXFUZI7eeNsB7xq8LhZlMTjE7D6KKEZE4wvgq42XRUhfO2FWhg0VabsJln9y9zKmwSUxncR/0fMSbnE+RIW3RIHN9eD72C6tp4wCiuAy7VO9sAGRP5AVsSFLcYvZCTa+3y2aVz8GRzjBxQcJufO+X3ZKaN8OM3YgImfocGvWvJbhMiUaFOLmQ/hl6uCfHJ5xxdVPZoKhKfrO/9YMGpRIYMRdgcp4PnEiH2TYC0afy/KtOxZitjxfGZSJOIMHUFvpZEl5qvWGpz7uuVYOCnO4tt1cXiFpuQz5YAl4YSfDv087369dL8NpdyEBPiJv40xyo2ZzIhG0V/Pp7ny7UzrmCEcUYYvzCuThMS8DoC2T4qrLo1E3iK6jSy3wHF308LEI58erk3syX3ZY1bY8fwdYFU9E6BSsRYA6DPSIm7bkIlvI/+lZZo7O4m X-Forefront-Antispam-Report: CIP:194.138.21.70;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:hybrid.siemens.com;CAT:NONE;SFS:(13230001)(4636009)(40470700004)(36840700001)(46966006)(107886003)(36860700001)(44832011)(956004)(2616005)(7596003)(356005)(7636003)(508600001)(5660300002)(8936002)(82960400001)(83380400001)(40460700003)(186003)(16526019)(1076003)(47076005)(2906002)(6666004)(86362001)(336012)(8676002)(82310400005)(70586007)(70206006)(4326008)(6916009)(316002)(36756003)(26005);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:12:46.8307 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 8dbecd09-7e29-4f0d-26fd-08da44780f3d X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.70];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR01FT018.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM4PR1001MB1364 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=BCB1pZOQ; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe0c::60f as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , Commit 0f38ff1 introduced swupdate.getversion() to Lua handlers. Make it public so that Lua suricatta modules can make use of it as well. Signed-off-by: Christian Storm Acked-by: Stefano Babic --- corelib/lua_interface.c | 4 ++-- include/lua_util.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c index dd1be9e..b1ad514 100644 --- a/corelib/lua_interface.c +++ b/corelib/lua_interface.c @@ -952,7 +952,7 @@ static void lua_push_enum(lua_State *L, const char *name, int value) lua_settable(L, -3); } -static int l_getversion(lua_State *L) +int lua_get_swupdate_version(lua_State *L) { unsigned int version = 0, patchlevel = 0; /* Deliberately ignore sublevel and extraversion. */ @@ -1002,7 +1002,7 @@ static const luaL_Reg l_swupdate[] = { { "mount", l_mount }, { "umount", l_umount }, { "getroot", l_getroot }, - { "getversion", l_getversion }, + { "getversion", lua_get_swupdate_version }, { "progress", l_notify_progress }, { NULL, NULL } }; diff --git a/include/lua_util.h b/include/lua_util.h index 81da802..13cffc0 100644 --- a/include/lua_util.h +++ b/include/lua_util.h @@ -34,6 +34,8 @@ int lua_notify_info(lua_State *L); int lua_notify_warn(lua_State *L); int lua_notify_debug(lua_State *L); +int lua_get_swupdate_version(lua_State *L); + #define lua_parser_exit(L) lua_close((lua_State *)L) #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501 From patchwork Thu Jun 2 09:11:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638262 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=hubq/+kl; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::137; helo=mail-lf1-x137.google.com; envelope-from=swupdate+bncbdd6bwv65qpbb7h54gkamgqeaqpr7gq@googlegroups.com; receiver=) Received: from mail-lf1-x137.google.com (mail-lf1-x137.google.com [IPv6:2a00:1450:4864:20::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDKzN083nz9s0w for ; Thu, 2 Jun 2022 19:12:31 +1000 (AEST) Received: by mail-lf1-x137.google.com with SMTP id b2-20020a0565120b8200b00477a4532448sf2256977lfv.22 for ; Thu, 02 Jun 2022 02:12:31 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161149; cv=pass; d=google.com; s=arc-20160816; b=nC7YCgqdWzIanV7nJOhcVojVNdDIMMgGlWsqLpmOVMRtx/xobFwCNi/PqdgzOpUrmJ 91SB7qWeNvieJbbAD/h/eZvf9V1EdLYq5BmQXlOlbJWPbFseF9gTZd8RDrXT66Rm+fuV FkbIVZaGQJkveLmTaVWtY2zoGjBDH1jnwIo7jpuWRJT8fW0oMLwD6/MvoyrwLNYEoicJ 5syXHUP3FMvAI+ROkf+6C3U1dmHG3P95jYLuge1864W7hKW27wMiJ98YKlllHrbfU4jg Jvh4pwfi9O1KtUAW+4SrIx0/6cC59143+O/EgU/QCf8RuQx0+a3VKQV2BMAIoX0r0GNz h3gw== ARC-Message-Signature: i=3; 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:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:dkim-signature; bh=GdGB/g9X/3Y7ryIRaHS1TKc+9HeFbvKdnn9xDzBx6LI=; b=oGPXiNz4/N5X9r+g34H6wXNMwKyCrWo/FJHNoU+8tUqPL2IZQue3u+dKZ/DEyOX7ck JNQ0sesD13jnPGYlkoCYcSqiI/OVa7EzbIhTWr2dbBvXmjRlY33TcGfk6xmjPhcd6oiZ DkBJ+ui8xxxs63mKdlsvM0sttfb7v3iZtzWkpeGRAeqa31jCWKIZrbPgxAb8SWD9GulS TzVX6R1fARg+66l19brjWY5OCV678Kw4HJjg5iD4sr0970nyx0v35I9Gfz+7E5WpobAX +N5qG1t9D3gelN84l/+mRsi4dfPKSBJrpp5qZdoD7gyP7EPl/RjfJekuanxRb0SSDgM2 anKw== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=FT8WPhTs; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::608 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=GdGB/g9X/3Y7ryIRaHS1TKc+9HeFbvKdnn9xDzBx6LI=; b=hubq/+klmtm+7yAn9kHHAwkZLa7xzCZGVWjH1OiPU1Pbn07qpKjIUw4rT5PchkLtfn W95Tc5TPxfvhxxGF+C7CptzUSAuBaYr75WZ0aSZkUSPOA0ooFRGf8mk+amyt7EFmo0T8 qbvZdkRabFXOTNFHittMXIP2ik6U9xY/MS0hFcuyN9zDkGdB/secs/oD9uqWeaBPnPQ+ 8C8G7rliFFLf6xgAoezl9p8rrs29Zf9YJfTpJZN5XqnJ9d8Q48aqLR3PpJbtdCiYPsCf F1DJo3cio/cQiYT7d6tR22uUMG5Xy0huFn5ta1HUt4jKQQLAywP137bK3yBah6AsL6pI o0ig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version: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=GdGB/g9X/3Y7ryIRaHS1TKc+9HeFbvKdnn9xDzBx6LI=; b=4Ev0W2hcYmvS4914r9EXQ0OIbynFJonN7ojgm9vJM5KpOolyP5XkCIq4K4l3OdPWIi Cd60voGVAODlufVS1drhVFQUe1dwVCS9obun8yIv1FM6UWBrU8L4sZ9wip61C8DXjPSU zGd34rtI6MQz21S2aCiNuA3AJYMR8ONxKUX6/bIYu8WBzO6RM9E9b02oEgVu+SBn4ll2 r1URI4Sq4B0sEplE2hvrVdEbUGUO8p9wS5ZGsVi2tb3BhOeuVpFpBsBpwudcrM6FLb// DOxMvC3ehu2LpMk/barlKZI/FEc0DSJRiDPv/g/OUKhBjvCb3qUQn6dCsYBWslDzfvJA CmPw== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM533geOqsgb6gVgPvFMwbZ3M1JeLZLa30LkTL2UKksnnqQhdhqXQf +ThYaWWeJlQ3XCQyWGkwBfA= X-Google-Smtp-Source: ABdhPJwXnH8MSvELs4g9iKZJ2jtl6P+uB6pQNJW3d6gKSd7hMn1weqePoBp6dvHoASaDeh/YetvKDw== X-Received: by 2002:a2e:93c8:0:b0:24e:eabd:bf6e with SMTP id p8-20020a2e93c8000000b0024eeabdbf6emr39700853ljh.347.1654161148925; Thu, 02 Jun 2022 02:12:28 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:651c:1a25:b0:255:4ab8:5c45 with SMTP id by37-20020a05651c1a2500b002554ab85c45ls953584ljb.5.gmail; Thu, 02 Jun 2022 02:12:27 -0700 (PDT) X-Received: by 2002:a2e:824c:0:b0:253:e12d:21d2 with SMTP id j12-20020a2e824c000000b00253e12d21d2mr34342216ljh.153.1654161147884; Thu, 02 Jun 2022 02:12:27 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161147; cv=pass; d=google.com; s=arc-20160816; b=Mw/wipWJ6hflpII/7NVMK8DIXUO99rB2qQ0yFYXD1XuvPwZqFx+EcrQoMrI6Ogq23s MppoNJCxa/O82NipynJYo+fWxt8RkvqY+WOuYgH2efVNwl41k8y+7jhtO1ZeFlHHhySv w0h7OXjMciL23kmokW9hdvqUkbtgBJygzfVlua53EziOCC8LRmG//Skh1WOP1a+y4HcI RDylwFqroC3m5pJZ768K1Z+hBNqi9DQU+3W1ONRnxfUWDdE5pz01jIPYQk+dwRM6Dd0c qVfu/auEWEuzpDFwSa0wG/KZOCDDu89yCM5fw3Fo0+9/xcJBTswBd9wrPMZBs80UE9Ew i9fA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=RLgIcWyZcHaOICsHbXyPPGtOHGFxKRruWrqlxvYM19Q=; b=x1Oabe19m3MrUBy2TbReXuSnuG8Dy/kz69Q7u/iujrU+M5KoMN+OoRAcYqcS0/39zo mOenXGsaIhSHhZaX4O9ExiMXC85zxk9MUOZkNHM/FkkwR9uuziwh4IotEFgPYn6wedkX q32Y1J1WJ2p7CyQODNMRvTRt0UFgFtAhR3wmIacPDCMsgpx/EQt8JT9pncKtbKF6o4IP /NWyG99zv8DiiYGMcNfsckFZhoStUs1+9cEHHcX6iu5l010REBN9q/pBJ7evkmHa6y07 RCUQpnRdtdcSdqXwBnz8tx3f7nYVIJwZOlJesa3p2D8MtG7yCfPkjaZgYeF/14vWxzd0 A9kQ== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=FT8WPhTs; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::608 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on20608.outbound.protection.outlook.com. [2a01:111:f400:7d00::608]) by gmr-mx.google.com with ESMTPS id u23-20020a05651c131700b0024eee872899si202173lja.0.2022.06.02.02.12.27 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:12:27 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::608 as permitted sender) client-ip=2a01:111:f400:7d00::608; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=aZVWqI1Fd9T8fDL8KnCykSb2C81P05aYKc9TUDQ+0n8TZ8nsg89k74pllY09z0fgNs2V+X4zSwZ87lm2L4ZguHebXBHtfDiCbhxPdDHpiKW4cufCi65FCam2pyUBJA/viaDgABFHNsu93hvmNNIe/R18rGPdlb5tMAcj4B7o7FBOyu3APfhUfGz61Fa1AAxDVUmFf+4rnCK1Mx32uhgdE5zX9rI/dSlF3PUCrcTZwC5+AmMCUzx2wNdh1qH4do/abx93RYHVgUi1G9/O/9v5qxr/RdXJXryYJoWoU7yTdE/83RV/sJ3I/lPp0JxpTt+2kY72QGfjJeq5UZTFW2p6Cw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=RLgIcWyZcHaOICsHbXyPPGtOHGFxKRruWrqlxvYM19Q=; b=C3it3q8eP6iXZk1bhW/9oVETZETPofSzEB7zrs3shfDyjbREQk6humFqtaW8uX/LawXfYZ4mjV7fJUREeFtKfRPmbGpqlT0yfDp8sAjYI68tCo+x/jJ+NxU5fAU7VzMwVLcbtL3wdbphHvFK+NUc1ggbI775u68tPmWFProGBerF00+ioSpKf8su7Yi9cGmbz7SCnNUzKcZKJJHDVLPTSaMomcUYrVKewte7MvwHTq7HEHA3NDgcvFBXO742YcMPZJlWug+XKdDufpbjmTD0tBKeH0ogvvb6roDCLHD4yLBJZ3MhlxwlhEoyvipgCnkyiFx0hRlxg1W5A9J+XEzW3g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.72) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from AS8P250CA0023.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:330::28) by DB7PR10MB2185.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:10:45::31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.12; Thu, 2 Jun 2022 09:12:26 +0000 Received: from VE1EUR01FT105.eop-EUR01.prod.protection.outlook.com (2603:10a6:20b:330:cafe::2) by AS8P250CA0023.outlook.office365.com (2603:10a6:20b:330::28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.12 via Frontend Transport; Thu, 2 Jun 2022 09:12:26 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.72) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.72 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.72; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.72) by VE1EUR01FT105.mail.protection.outlook.com (10.152.3.156) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:25 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SMA.ad011.siemens.net (194.138.21.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:12:25 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:12:25 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 3/7] Lua: Publicize notify_progress() Date: Thu, 2 Jun 2022 11:11:43 +0200 Message-ID: <20220602091147.53323-3-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220602091147.53323-1-christian.storm@siemens.com> References: <20220602091147.53323-1-christian.storm@siemens.com> MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC89XA.ad011.siemens.net (139.25.226.103) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: b08a4c95-5158-4be7-62c6-08da447802af X-MS-TrafficTypeDiagnostic: DB7PR10MB2185:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: y/jJ5DU1VbshJVXIim40PllV3S8hNTLjsGxna1OCwaT2XdnWqCHpXFLX6GuISSoCOqqzaWnUD29KxS0YGo92s7Otfu4SeD6MO8EH1l5XDrNOhiZzQo5cUSRZjxBEqVqUuTCE8rLCZOPO0WwRCEqoQ8Yf98xfYm9qDyiCboxSKiSTPDXMIpISKMkLpkfKn3cdGZkFVwAaYKHnl6YlxDEp3agj42yyUFTsoqUzr6zvXvBOVDFIm6oujYeCfqcPLcwLMsFUu7JyFrfvg7QP91DqiTJlVaN/3qXJjp1iOyaIpEWHzQi9Rl5nbbv+kbOwVoOSEao74CWAWH+RaozOkIxKgXecz6iRV3OCPWfJSlctZPIVIeX00RjuSHq2MBY/RrbPWZtau57bSI+xa3aPtsDQg5Tn+E++Ot5Ph3z6I2+TPkA7LfcvzzokUch8ivXQw0Iy1PY5af8G+JxsccMupdUpyHgq96qkZc4A0xee2Dj78LhUa+afvG8QWWFsSdEOZ0AjFSCJNgF2zsLdrvZveGFHlH//09S5O8fFv2ORVEhJ3BsvlJbqphhWnWlNKoyi/KpTpyrqKurP3C5fhYp3Q8q5DS9LbA95dmEmlSh2pA+R6MMjKKGuupDgOvy1Ee0EF2w65s2ytTvXZPsj4QS4zf/3MSW0jjRisDTmqQ/sT3kbulksTylNqY77i4uEDz5qNpKa5ClTch2pv2ypDKH7kh2DUA== X-Forefront-Antispam-Report: CIP:194.138.21.72;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230001)(4636009)(46966006)(36840700001)(40470700004)(1076003)(5660300002)(26005)(86362001)(82310400005)(4326008)(8676002)(8936002)(36860700001)(83380400001)(508600001)(70206006)(16526019)(40460700003)(81166007)(70586007)(36756003)(44832011)(6916009)(956004)(107886003)(82960400001)(2616005)(6666004)(356005)(186003)(336012)(316002)(2906002)(47076005)(36900700001);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:12:25.7690 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: b08a4c95-5158-4be7-62c6-08da447802af X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.72];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR01FT105.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB7PR10MB2185 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=FT8WPhTs; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::608 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , Commit 2a85ebd introduced swupdate.progress() to Lua handlers. Make it public so that Lua suricatta modules can make use of it as well. Signed-off-by: Christian Storm Reviewed-by: Stefano Babic --- corelib/lua_interface.c | 4 ++-- include/lua_util.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c index b1ad514..b7cc175 100644 --- a/corelib/lua_interface.c +++ b/corelib/lua_interface.c @@ -978,7 +978,7 @@ int lua_get_swupdate_version(lua_State *L) * @param [Lua] Message to dispatch to progress interface. * @return [Lua] nil. */ -static int l_notify_progress(lua_State *L) { +int lua_notify_progress(lua_State *L) { /* * NOTE: level is INFOLEVEL for the sake of specifying a level. * It is unused in core/notifier.c :: progress_notifier() as the @@ -1003,7 +1003,7 @@ static const luaL_Reg l_swupdate[] = { { "umount", l_umount }, { "getroot", l_getroot }, { "getversion", lua_get_swupdate_version }, - { "progress", l_notify_progress }, + { "progress", lua_notify_progress }, { NULL, NULL } }; diff --git a/include/lua_util.h b/include/lua_util.h index 13cffc0..4974eeb 100644 --- a/include/lua_util.h +++ b/include/lua_util.h @@ -33,6 +33,7 @@ int lua_notify_error(lua_State *L); int lua_notify_info(lua_State *L); int lua_notify_warn(lua_State *L); int lua_notify_debug(lua_State *L); +int lua_notify_progress(lua_State *L); int lua_get_swupdate_version(lua_State *L); From patchwork Thu Jun 2 09:11:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638265 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=FB4UN64t; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::43b; helo=mail-wr1-x43b.google.com; envelope-from=swupdate+bncbdd6bwv65qpbbf764gkamgqecjjxxti@googlegroups.com; receiver=) Received: from mail-wr1-x43b.google.com (mail-wr1-x43b.google.com [IPv6:2a00:1450:4864:20::43b]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDKzw6jc6z9s0w for ; Thu, 2 Jun 2022 19:12:59 +1000 (AEST) Received: by mail-wr1-x43b.google.com with SMTP id o17-20020a5d4091000000b002102fe310dcsf646401wrp.20 for ; Thu, 02 Jun 2022 02:12:59 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161176; cv=pass; d=google.com; s=arc-20160816; b=PVqVyz+tE7//0jnj/L8NSeqOvEAYitDmf6hsMcUMTWDQgRIsn1s6q+AAf9Lg/c0V+m YQ+Ib0pigHf7YYKasm/ffbSaE/mRja0tsXLv9quRKixnQHmHDbKdgt2Fyaism+5/ERyi g2S0YQk/5K5NN6OxDCm6IFFet9zv6b0z9Op8at0kzNr2G2lxQN63O/QmoZ8PlWxAhKkz fo6gEJtrZEfcQ/BOYuKdmcE3Y1/LHyUGz+Ijpnf0OK3Ic2Wb32sYwXKo6X8O6+ASa9DO P5Et6zzFAAiPIItQotI7Bsx3qjj3Sfj3VooMxnAmJ6DLszFLqW1t8tITIs+cUwq713Yb XZZw== ARC-Message-Signature: i=3; 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:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:sender:dkim-signature; bh=OMJE85AjxzrCcTgERva0lNoJj9ucCW1/difaJtOdE1k=; b=rcIsxK1n+TdTyJhaFbhxPD9VvA07wZUyh1X61lTUDn63+bKxC21/e+q+JeG6i9vJMK scyKNQ5gEHSjMPWBtLlH44MLcrtzBDjzrP14DI08zaSlD/i/Nl1tPNIygZRkb7SPWIpW jt497RWxg6ue0npQcaOs62lkgwFJfz0xzqJPJQtWiKxsx6vhBHJe3+bqQHTONWu7Zim/ vf97v8GoH6ZveFJngfth7oOlpCtHqwQDretMBbTLXADhl9/dDXXTH8C9QAjS1P0d829D 7AdpbJqXHPn8U/X6seVvSB9Sk6nrAVacsHjVCnOc4D2zqcuYFdAZzUQH1NapxE7ILoPH EM9g== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=s0EAo6Kt; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::601 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=OMJE85AjxzrCcTgERva0lNoJj9ucCW1/difaJtOdE1k=; b=FB4UN64tu8EYyplcllMVdyXZgBc5eEblPH3uGgWlwse15F642/I77ynDmx5AdlFfub yBNXg62XM0v3LWryqDk51BdxvRELWM4TadueizE9JT6KXAoC9vPLIYLu8q0gr1qxWIah 66cWStBY0i/7JRRsWaZ3zSD4oUtYWogc/W3PalS2bK0KwDxvsTFKlL2Y3/qwZcfAm3Or Km742tSgAOyBj/kfPj6/jmKLfBVK6t8dLBHZ8esv+/0umv66qQW8cUj3VzpwQLkEjT8A PKOvpH3QQnVLFGxS/9TuJ8+J5H24QSrsQPvNwFCk0nSIGYV8CAw0P1rVS9WzfTDQ55bZ uD4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding :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=OMJE85AjxzrCcTgERva0lNoJj9ucCW1/difaJtOdE1k=; b=7dDFGRnxexgy6jQE6L4O47Hu+KoBV7LppDdLToSs/UnFtZsuJ6GyK1R7M9L8H5gXzA X+lVzbgtgT391gRMUk0hlMfRyF/ywk41iK4t1AazZml9YLoP5TQ+Ij/lVeWYo9ubUtPi hQBtfocuoA5S04MwW5bbg/YJJPRcvgvTjTEtAvH3QsXefaiutdK/Yz33F9fAkSr71fSm LVCP7Lc7amlD5gNI+nQ6Y3zVC+TU8nmkUyVf62bnL9PIxGHkIVMxL7zwqSIHs1FG2ZVc Wzv9Jwwmt9Zuz5cNMEex+be7/J9CTuJ5o/9T/kvxlFYK9GWBtNI1fFv20MyXHDHQI3Sa GEvQ== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM533oClUpJ3CUNlO+SsZR9p+G+/0hmC1exsU67vAHsI6HeV3Cx0m7 TBnDOAZ6CCHAh6YSRRbD664= X-Google-Smtp-Source: ABdhPJzvFBudJgNCVHHRXMnAJO3aquyCXlFkUcoMGigkjxXWBE/n23tTChtq0hLSLDATO9oDdTo+RQ== X-Received: by 2002:a05:6000:362:b0:210:2297:45f0 with SMTP id f2-20020a056000036200b00210229745f0mr2837133wrf.44.1654161175794; Thu, 02 Jun 2022 02:12:55 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:6000:1e0b:b0:20e:7a8a:8c81 with SMTP id bj11-20020a0560001e0b00b0020e7a8a8c81ls8481914wrb.1.gmail; Thu, 02 Jun 2022 02:12:54 -0700 (PDT) X-Received: by 2002:a05:6000:156c:b0:210:4a6a:16bb with SMTP id 12-20020a056000156c00b002104a6a16bbmr2848265wrz.245.1654161174494; Thu, 02 Jun 2022 02:12:54 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161174; cv=pass; d=google.com; s=arc-20160816; b=MYJ57CXdxEMn1OqYs22JcOZf/LHgM5LW6s+B0eUroT0GUiDzRPv4n41cap16IyrAFb fFeNsQULWenHu3pTEATBiEg0SS4IJiYkzM1qRIE1qgHxyJauHvBbBMZ57xsVHTvDxgcE bc4WAHTIywMq7hCFU2ObC8uYQxhl+1hbE7Oimuyi/uDqHSUJJA3PpnlSQEo6AY2jRLAb DZ2muHhXg//mw/TBoKViBcACudoi5LWU/jkRqVcLojhUbb8RlfPBRExTjEz9pj6VEylm ZcajCfCuTKINAB1YeYUFddEenIACq9rtTcg8s/j9bF/sxeUQhN3FB0RjRsGKMGKHUShF klJw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=D01le/6Qi7sUs8L8LdqLbCdLPUHRebNKhj2jlWB1myQ=; b=yR6jToPK10PXmhmCSS3r7A0102X2oyMH+NlDmqCK7nxfYl3aF60eNnppJNifzAQ0i6 F0pf8xqml9N1QMTqWcnUTPp44BAEFRIZ6He21G7mViST+ib6vIpohBu5APAfFcNYdScw R1QMWPxuCwORIlIj43CedieGrj2ib2RWsC43P/OiUSUV8mD+NfUCoZ/Ula8zSRefDUU5 7GBiKzIQ+fFDdLMfaq4dOBeSUKgSIx33pPcLXp2evUZ+Y3+9n7e7xD+3y5E4lsHkWt+s /j7Cy8TB2d/O4+1UfRD6UKZFs4qgOF8i4XQ/KB6H+K3mA9V31NPnFgjv8bY7YfoKKlQ2 VwUw== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=s0EAo6Kt; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::601 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR02-AM5-obe.outbound.protection.outlook.com (mail-am5eur02on0601.outbound.protection.outlook.com. [2a01:111:f400:fe07::601]) by gmr-mx.google.com with ESMTPS id s13-20020a5d424d000000b002102a7531cesi173763wrr.2.2022.06.02.02.12.54 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:12:54 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::601 as permitted sender) client-ip=2a01:111:f400:fe07::601; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=BYWLcyMC44yXEOiTGUUaDjTvQElJrJ60jC4HLunEGs1k4ahItwWR205cpaOJu66jrUl9HhgGi/DhIYW5PuQlq5/2At1DVlZUaf3WIeAxENnq/UquI0XZiRfNm05ta0sGH0Bo3U29/g9JbN0vi9slt77RvW9HqgcfvpBea3NDivTpiP3cZ3kdHIZKISDtwQDFDdFUxlTYB3OodKZcrgokqf3e/14YMFOD8TjYD9SuO1NjNInU3KkpTwjj+18f9GzcsAxvhVd2eEV4L9XCpPHFlYQLKKorCUZUXfEKBXsJYIfN4ipqvX8M1tKce6offJOSdCmxacSElPglM8X6RTv6qQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=D01le/6Qi7sUs8L8LdqLbCdLPUHRebNKhj2jlWB1myQ=; b=jF6D/emO8sSSY/1SQqEQfEiYP2QGN6HlB1uoPKzuzuu9m+JBS5Y6auhtQE1JXwknr4wAFd6Rtks9mQacAXrYPfpk9isYnuxmIQpXgXVi34oe1WhLa8Bs2xddL0QgQwb8+neZyqPUd4IIjg4Bv79/PsORJIz1w0YCZrjAbOyEvFMGD5ONjVY45taWjZUVasCU29y2ezRnlh9tQoaHv/zE/7V/vVqBciC6GBGVUhQpTAeCdbPR9wHrsIvx7xVNW0jim3ArdG5nyVvIzYL47ozAs3FlVb4CM1fyzpfoS0E4Vew0C/4qXJqaChCVkqOPAFr2OnCTt2Ap+HSJulLq4/2u+w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.73) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from DB3PR06CA0026.eurprd06.prod.outlook.com (2603:10a6:8:1::39) by PA4PR10MB4365.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:102:104::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13; Thu, 2 Jun 2022 09:12:52 +0000 Received: from DB5EUR01FT043.eop-EUR01.prod.protection.outlook.com (2603:10a6:8:1:cafe::9a) by DB3PR06CA0026.outlook.office365.com (2603:10a6:8:1::39) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:51 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.73) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.73 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.73; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.73) by DB5EUR01FT043.mail.protection.outlook.com (10.152.5.103) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:51 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SNA.ad011.siemens.net (194.138.21.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:12:36 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:12:36 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 4/7] suricatta/lua: Add suricatta Lua module infrastructure Date: Thu, 2 Jun 2022 11:11:44 +0200 Message-ID: <20220602091147.53323-4-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220602091147.53323-1-christian.storm@siemens.com> References: <20220602091147.53323-1-christian.storm@siemens.com> MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC89XA.ad011.siemens.net (139.25.226.103) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 085a4ea7-427a-44ef-072c-08da44781208 X-MS-TrafficTypeDiagnostic: PA4PR10MB4365:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: BCmHTnyZ18wKkESbQMdxHgMRscj9acjGF31+t3SldGad/fWsTp7sIU499tEC6DTudZ3RBq8xqR8J0PYG7LSO+CjBJXCoBpkstVBS/ri4gWHzpfGsCJyZA8K3namhPLzb24EIACkda4qjLvKuy9JeR1ffGozowZkG/9bytG4bpjdmGwG2fpTCPTcVz6D3CvUvz8rvWylHip2EEAxwmIK2UGaiDZfcV7LUIDbpZDCsz/USMeADvC8nSdzRZ/P2KsH2S196JsP+3kQ5MG1EN0OVaiOS5t53X04Pf7ejfBzVdZy8TMZE6bxygjQOv9G60Nng6G/hCfas5DT/qTS+qUEIglrvN7CuTTq5+fZlVXgyFMdjnys0wScmDHnlRxwTAOarpI+3vqY/xp3SSP6b7RkzZeM1urLKIcijZmnY8Mgr4G7nxB5cQs+jDxYkd4X/KCoEHp5SC0WVc/sOIjxvXSb/HXC3EM/QW0PQdPutPmJGB8kURIS0UVSCtrwHHj8asZTKAtLpDIcLeuWJ/xQkfQxgof000KUOuL0GrIi9YPlNliu3epn1jq440loiq/MQOV4iLaaCG7U2m9e/OxYIMywhZ/Uydm8ZJtuEmynf74YaZszGjLcLEZf4di4H1skSfsTDsXnM+qpzSJKzPIPYb3DJiqZsEjWSFUfflSLg6C5IGovbCUNZrjGjPcBk0MPDgKZV6Fo0IsP9OsAkIXdiGu0nTA== X-Forefront-Antispam-Report: CIP:194.138.21.73;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230001)(4636009)(46966006)(40470700004)(36840700001)(47076005)(107886003)(16526019)(186003)(336012)(1076003)(36756003)(6916009)(83380400001)(86362001)(956004)(81166007)(82960400001)(356005)(2616005)(36860700001)(316002)(30864003)(5660300002)(82310400005)(6666004)(8936002)(8676002)(70586007)(4326008)(70206006)(2906002)(26005)(966005)(44832011)(508600001)(40460700003)(36900700001)(559001)(579004);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:12:51.4888 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 085a4ea7-427a-44ef-072c-08da44781208 X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.73];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: DB5EUR01FT043.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: PA4PR10MB4365 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=s0EAo6Kt; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::601 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , The server_lua.c C-to-Lua bridge enables writing suricatta modules in Lua. It provides the infrastructure in terms of the interface to SWUpdate "core" to the Lua realm, enabling the "business logic" such as handling update flows and communicating with backend server APIs to be modeled in Lua. The server_lua.c C-to-Lua bridge provides the same functionality as the other suricatta modules written in C have, realizing a separation of means and control. Effectively, it lifts the suricatta interface to the Lua realm. Signed-off-by: Christian Storm --- Makefile.flags | 12 + suricatta/Config.in | 43 + suricatta/Makefile | 3 + suricatta/server_lua.c | 1907 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1965 insertions(+) create mode 100644 suricatta/server_lua.c diff --git a/Makefile.flags b/Makefile.flags index 9a7bd35..2bec846 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -233,6 +233,18 @@ else KBUILD_CPPFLAGS += -DBOOTLOADER_DEFAULT="none" endif +ifneq ($(CONFIG_SURICATTA),) +ifneq ($(CONFIG_SURICATTA_LUA),) +ifneq ($(CONFIG_EMBEDDED_SURICATTA_LUA),) +ifneq ($(CONFIG_EMBEDDED_SURICATTA_LUA_SOURCE),) +LDFLAGS_swupdate += -Wl,--format=binary -Wl,$(CONFIG_EMBEDDED_SURICATTA_LUA_SOURCE) -Wl,--format=default +KBUILD_CPPFLAGS += -DEMBEDDED_SURICATTA_LUA_SOURCE_START="_binary_$(subst ",,$(subst .,_,$(subst /,_,$(CONFIG_EMBEDDED_SURICATTA_LUA_SOURCE))))_start" +KBUILD_CPPFLAGS += -DEMBEDDED_SURICATTA_LUA_SOURCE_END="_binary_$(subst ",,$(subst .,_,$(subst /,_,$(CONFIG_EMBEDDED_SURICATTA_LUA_SOURCE))))_end" +endif +endif +endif +endif + # SWU forwarder ifneq ($(CONFIG_SWUFORWARDER_HANDLER),) LDLIBS += websockets uriparser diff --git a/suricatta/Config.in b/suricatta/Config.in index 10331a7..29dc738 100644 --- a/suricatta/Config.in +++ b/suricatta/Config.in @@ -53,6 +53,49 @@ config SURICATTA_HAWKBIT comment "hawkBit support needs json-c" depends on !HAVE_JSON_C +config SURICATTA_LUA + bool "Suricatta Lua module" + depends on HAVE_LIBCURL + depends on HAVE_LUA + select CHANNEL_CURL + help + Support for Suricatta modules in Lua. + + The optional JSON support for Suricatta Lua modules + provides JSON as Lua Tables to the Lua realm and + enables channel result parsing to JSON per default. + To enable, select 'libjson' in 'Parser Features'. + +config EMBEDDED_SURICATTA_LUA + bool "Embed Suricatta Lua module in SWUpdate binary" + depends on SURICATTA_LUA + default n + help + Embed the Suricatta Lua module source code file into + the SWUpdate binary. + + If enabled, a swupdate_suricatta.lua will *not* + be loaded from disk at SWUpdate startup. + + Note: Exactly one Lua source code file is embedded + into the binary, i.e., possible dependencies either + have to be deployed on the target system or put into + the one embedded Lua source code file. + +config EMBEDDED_SURICATTA_LUA_SOURCE + string "Suricatta Lua module file" + depends on EMBEDDED_SURICATTA_LUA + default "swupdate_suricatta.lua" + help + Path to the Suricatta Lua module source code file to + be embedded into the SWUpdate binary. + +comment "Suricatta Lua module support needs libcurl and Lua" + depends on !HAVE_LIBCURL || !HAVE_LUA + +comment "Suricatta Lua module JSON support needs json-c" + depends on SURICATTA_LUA && !JSON + config SURICATTA_GENERAL bool "General HTTP support" depends on HAVE_JSON_C diff --git a/suricatta/Makefile b/suricatta/Makefile index b06d171..8fbb1ce 100644 --- a/suricatta/Makefile +++ b/suricatta/Makefile @@ -5,4 +5,7 @@ lib-$(CONFIG_SURICATTA) += suricatta.o common.o ifneq ($(CONFIG_SURICATTA_HAWKBIT),) lib-$(CONFIG_SURICATTA) += server_hawkbit.o endif +ifneq ($(CONFIG_SURICATTA_LUA),) +lib-$(CONFIG_SURICATTA) += server_lua.o +endif lib-$(CONFIG_SURICATTA_GENERAL) += server_general.o diff --git a/suricatta/server_lua.c b/suricatta/server_lua.c new file mode 100644 index 0000000..6dcf938 --- /dev/null +++ b/suricatta/server_lua.c @@ -0,0 +1,1907 @@ +/* + * Author: Christian Storm + * Copyright (C) 2022, Siemens AG + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_JSON +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "suricatta_private.h" + +#define CONFIG_SECTION "suricatta" + +#if defined(CONFIG_EMBEDDED_SURICATTA_LUA) +extern const char EMBEDDED_SURICATTA_LUA_SOURCE_START[]; +extern const char EMBEDDED_SURICATTA_LUA_SOURCE_END[]; +#endif + +extern channel_op_res_t channel_curl_init(void); + + +/* + * struct for passing a channel closure's C data via the Lua stack + * to other C functions not in the channel closure's domain. + */ +typedef struct { + channel_data_t *channel_data; + channel_t *channel; +} udchannel; + + +/* + * Global default channel options. + */ +static channel_data_t channel_data_defaults = { + .retry_sleep = CHANNEL_DEFAULT_RESUME_DELAY, + .retries = CHANNEL_DEFAULT_RESUME_TRIES, + .low_speed_timeout = 300, +#ifdef CONFIG_JSON + .format = CHANNEL_PARSE_JSON, +#else + .format = CHANNEL_PARSE_RAW, +#endif + .debug = false, +#ifdef CONFIG_SURICATTA_SSL + .usessl = true, +#endif + .strictssl = true, + .nocheckanswer = false, + .nofollow = false, + .source = SOURCE_SURICATTA, +}; + +/* + * Prototypes for "public" functions implementing the server + * interface specified in include/suricatta/server.h. + */ +void server_print_help(void); +unsigned int server_get_polling_interval(void); +server_op_res_t server_has_pending_action(int *action_id); +server_op_res_t server_start(char *fname, int argc, char *argv[]); +server_op_res_t server_stop(void); +server_op_res_t server_install_update(void); +server_op_res_t server_ipc(ipc_message *msg); +server_op_res_t server_send_target_data(void); + +/* Global Lua state for this Suricatta Lua module implementation. */ +static lua_State *gL = NULL; + +/* + * 'enum' to help mapping Lua functions to + * (1) server interface functions (include/suricatta/server.h) and + * (2) callback Lua functions. + */ +#define MAP(x) SURICATTA_FUNC_ ## x, +#define SURICATTA_FUNCS \ + MAP(HAS_PENDING_ACTION)\ + MAP(INSTALL_UPDATE)\ + MAP(SEND_TARGET_DATA)\ + MAP(GET_POLLING_INTERVAL)\ + MAP(SERVER_START)\ + MAP(SERVER_STOP)\ + MAP(IPC)\ + MAP(PRINT_HELP)\ + MAP(CALLBACK_PROGRESS)\ + MAP(CALLBACK_CHECK_CANCEL) +typedef enum { + SURICATTA_FUNCS \ + SURICATTA_FUNC_LAST +} function_t; +#undef MAP + +/* + * Lua functions up to (and including) SURICATTA_FUNC_MANDATORY + * are mandatory to be registered. This is checked for in + * suricatta_lua_create(). + * */ +#define SURICATTA_FUNC_MANDATORY SURICATTA_FUNC_PRINT_HELP + +/* + * Function registry into which the Lua function implementations for + * the server interface and the callback functions are stored. + */ +#define SURICATTA_FUNC_NULL -1 +static int func_registry[SURICATTA_FUNC_LAST] = { SURICATTA_FUNC_NULL }; + + +/* + * Log array gathered via IPC from an installation in progress, + * returned as part of the installation result. + * */ +static const char **ipc_journal = NULL; + + +/* + * Data passed to the Lua callback function C wrappers + * while installation is in-flight. + * + * @see check_cancel_callback() + * @see progress_offloader_thread() + * @see progress_collector_thread() + * */ +struct msgq_data_t { + struct progress_msg entry; + STAILQ_ENTRY(msgq_data_t) entries; +}; +STAILQ_HEAD(messageq_head_t, msgq_data_t); +typedef struct { + lua_State *L; + pthread_mutex_t *lua_lock; + pthread_t *thread_collector; + pthread_t *thread_offloader; + struct messageq_head_t progress_msgq; + pthread_mutex_t *progress_msgq_lock; + bool drain_progress_msgq; + int lua_check_cancel_func; + int fdout; +} callback_data_t; + + +/** + * @brief Push name=value to Lua Table on stack top. + * + * Executes Table[name] = value for the Table on top of Lua's stack. + * + * @param L The Lua state. + * @param name Name (key) of Table entry. + * @param value C value to push to Table[name]. + */ +#define push_to_table(L, name, value) ({ \ + (__builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(name), char[] ), lua_pushstring, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(name), char* ), lua_pushstring, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(name), int ), lua_pushnumber, \ + (void)0)))(L, name) \ + ); \ + (__builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), bool ), lua_pushboolean, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), unsigned int ), lua_pushnumber, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), int ), lua_pushnumber, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), long long ), lua_pushnumber, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), long ), lua_pushnumber, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), double ), lua_pushnumber, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), channel_body_t), lua_pushnumber, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), char[] ), lua_pushstring, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), char* ), lua_pushstring, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(value), const char* ), lua_pushstring, \ + (void)0))))))))))(L, value) \ + ); \ + lua_settable(L, -3); \ +}) + + +/** + * @brief Push name=value and reverse-lookup to Lua Table on stack top. + * + * Executes for the Table on top of Lua's stack: + * Table[name] = value + * Table[value] = name + * + * @param L The Lua state. + * @param name Name (key) of Table entry. + * @param value C value to push to Table[name]. + */ +#define push_enum_to_table(L, name, value) ({ \ + push_to_table(L, name, value); \ + push_to_table(L, value, name); \ +}) + + +/** + * @brief Get value by name from Lua Table on stack top. + * + * The mechanics is as follows: + * (1) Table[name] is defined, + * → get the value from the Lua Table on top of Lua's stack into dest. + * (2) Table[name] is not defined + * (2.1) copy_dest is not given, + * → dest is not modified. + * (2.2) copy_dest is given and dest is a string + * → dest is strdup()'d. + * + * @param L The Lua state. + * @param name Name (key) of Table entry. + * @param dest C destination for Table[key]'s value. + * @param copy_dest Whether or not to duplicate dest's value + */ +#define COPY_DEST true +#define get_from_table_3(L, name, dest) ({ \ + lua_getfield(L, -1, name); \ + if (!lua_isnil(L, -1)) { \ + dest = \ + (__builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), bool ), lua_toboolean(L, -1), \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), unsigned int ), (unsigned int)lua_tointeger(L, -1), \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), int ), (int)lua_tointeger(L, -1), \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), long ), (long)lua_tointeger(L, -1), \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), channel_body_t), lua_tointeger(L, -1), \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), char* ), strdup(lua_tostring(L, -1)), \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), const char* ), strdup(lua_tostring(L, -1)), \ + (void)0))))))) \ + ); \ + } \ + lua_pop(L, 1); \ +}) +#define get_from_table_4(L, name, dest, copy_dest) ({ \ + get_from_table_3(L, name, dest); \ + lua_getfield(L, -1, name); \ + if (lua_isnil(L, -1)) { \ + dest = \ + (__builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), char* ), dest ? strdup(dest) : NULL, \ + __builtin_choose_expr(__builtin_types_compatible_p( \ + typeof(dest), const char* ), dest ? strdup(dest) : NULL, \ + (void)0)) \ + ); \ + } \ + lua_pop(L, 1); \ +}) +#define get_from_table_X(x, arg1, arg2, arg3, arg4, FUNC, ...) FUNC +#define get_from_table(...) get_from_table_X(, ##__VA_ARGS__, \ + get_from_table_4(__VA_ARGS__),\ + get_from_table_3(__VA_ARGS__) \ + ) + + +#ifdef CONFIG_JSON +/** + * @brief Push the Lua equivalent of a JSON type to Table on stack top. + * + * @param L The Lua state. + * @param jsobj The JSON object. + * @return JSON_C_VISIT_RETURN_CONTINUE + */ +static int json_push_to_table(lua_State *L, json_object *jsobj) +{ + switch (json_object_get_type(jsobj)) { + case json_type_string: { + /* Unquote JSON string to push it unquoted to Lua. */ + char *trimmed = strdupa(json_object_to_json_string(jsobj)); + trimmed = trimmed[0] == '"' ? trimmed + 1 : trimmed; + size_t len = strlen(trimmed); + trimmed[len - 1] = trimmed[len - 1] == '"' ? '\0' : trimmed[len - 1]; + lua_pushstring(L, trimmed); + break; + } + case json_type_boolean: + lua_pushboolean(L, json_object_get_boolean(jsobj)); + break; + case json_type_double: + case json_type_int: + lua_pushnumber(L, json_object_get_int(jsobj)); + break; + case json_type_null: + /* Lua has no notion of 'null', mimic it by an empty Table. */ + lua_createtable(L, 0, 0); + break; + case json_type_object: + case json_type_array: + /* Visiting array or object: just create a new Lua Table */ + lua_newtable(L); + return JSON_C_VISIT_RETURN_CONTINUE; + } + lua_settable(L, -3); + return JSON_C_VISIT_RETURN_CONTINUE; +} + + +/** + * @brief Callback for generating a Lua Table from JSON object. + * + * Callback for json-c's json_c_visit() populating a Lua Table while + * walking the JSON hierarchy. See json-c's json_c_visit() documentation. + * + * @param jsobj The JSON object. + * @param flags Flags for the JSON object. + * @param parent_jsobj The JSON object's parent, if any. + * @param jsobj_key Set if visiting a JSON object ("dict"). + * @param jsobj_index Set if visiting a JSON array. + * @param userarg The Lua state passed through. + * @return 0, or, in case of error, -1. + */ +static int json_to_table_callback(json_object *jsobj, int flags, + json_object __attribute__((__unused__)) * parent_jsobj, + const char *jsobj_key, size_t *jsobj_index, void *userarg) +{ + lua_State *L = (lua_State *)userarg; + + if (!jsobj_key && !jsobj_index) { + /* Neither array nor object, can be root node. */ + return JSON_C_VISIT_RETURN_CONTINUE; + } + if (flags == JSON_C_VISIT_SECOND) { + /* Second visit to array or object: "commit" to Lua Table. */ + lua_settable(L, -3); + return JSON_C_VISIT_RETURN_CONTINUE; + } + if (jsobj_index && !jsobj_key) { + /* Visiting array element: push to Lua "Array" Table part. */ + lua_pushnumber(L, (int)*jsobj_index + 1); + } else { + /* Visiting object element: push to Lua "Dict" Table part. */ + lua_pushstring(L, jsobj_key); + } + return json_push_to_table(L, jsobj); +} + + +/** + * @brief Create a Lua Table from JSON object. + * + * Creates a new Lua Table on stack top from a JSON object. + * On error, the Lua stack is unchanged. + * + * @param L The Lua state. + * @param json_root The JSON object. + * @return true if successful, false otherwise. + */ +static bool json_to_table(lua_State *L, json_object *json_root) +{ + int stacktop = lua_gettop(L); + lua_newtable(L); + if (json_c_visit(json_root, 0, json_to_table_callback, L) < 0) { + lua_settop(L, stacktop); + return false; + } + return true; +} +#endif + + +/** + * @brief Push true or nil to Lua stack according to result argument. + * + * Pushes true to the Lua stack if result is SERVER_OK. + * Otherwise, nil is pushed onto the Lua stack. + * + * @param L The Lua state. + * @param result enum value of server_op_res_t. + */ +static inline void push_result(lua_State *L, server_op_res_t result) +{ + result == SERVER_OK ? lua_pushboolean(L, true) : lua_pushnil(L); +} + + +/** + * @brief Helper function testing whether a Lua function is registered. + * + * If registered, the function is left on top of Lua's stack. + * Otherwise, the stack is not modified. + * + * @param L The Lua state. + * @param func The Lua function to call, mapped to by the function_t enum. + * @return true, or, if func is not registered, false. + */ +static bool push_registered_lua_func(lua_State *L, function_t func) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, func_registry[func]); + if (lua_isfunction(L, -1)) { + return true; + } + lua_pop(L, 1); + return false; +} + + +/** + * @brief Wrapper to call a registered Lua function. + * + * The reference to the Lua function to call is stored in Lua's registry table. + * The required index to the Lua function is given in the 'func' parameter. + * The arguments as counted in the parameter 'numargs' must be pushed onto the + * Lua stack prior to calling this function. + * + * @param L The Lua state. + * @param func The Lua function to call, mapped to by the function_t enum. + * @param numargs The number of arguments for the Lua function. + * @return func's (int) return code, or, in case of error, -1. + */ +static int call_lua_func(lua_State *L, function_t func, int numargs) +{ + #define MAP(x) #x, + static const char *const function_names[] = { SURICATTA_FUNCS }; + #undef MAP + + if (!push_registered_lua_func(L, func)) { + /* + * Optional (callback) function is not registered, + * return no error in this case. + * Mandatory functions' registration is checked in + * suricatta_lua_create(). + * */ + return 0; + } + + /* + * Move the function below its arguments on stack, + * satisfying the lua_pcall() protocol. + * */ + lua_insert(L, -(numargs + 1)); + + int top = lua_gettop(L) - numargs - 1; + if (lua_pcall(L, numargs, LUA_MULTRET, 0) != LUA_OK) { + ERROR("Error executing Lua function %s: %s", function_names[func], + lua_tostring(L, -1)); + lua_pop(L, 1); + return -1; + } + if (top == lua_gettop(L)) { + WARN("Missing return code from Lua function %s, assuming FAILURE.", + function_names[func]); + return -1; + } + int result = (int)luaL_checkinteger(L, -1); + lua_pop(L, 1); + return result; +} + + +/** + * @brief Helper function properly mapping call_lua_func() to server_op_res_t. + * + * Note: As there is no size defined in server_op_res_t such as + * a SERVER_LAST_CODE, any int result value > 0 is simply casted + * to server_op_res_t and assumed to be in the enum's "range". + * + * @param result int return code of call_lua_func(). + * @return enum value of server_op_res_t. + */ +static inline server_op_res_t map_lua_result(int result) +{ + if (result < 0) { + return SERVER_EERR; + } + return (server_op_res_t)result; +} + + +/** + * @brief Register a server interface's or callback Lua function. + * + * @param [Lua] Lua function "pointer". + * @param [Lua] Lua function type "enum" value of function_t. + * @return [Lua] True, or, in case of error, nil. + */ +static int register_lua_func(lua_State *L) +{ + luaL_checktype(L, -2, LUA_TFUNCTION); + function_t func = luaL_checknumber(L, -1); + if (func >= SURICATTA_FUNC_LAST) { + ERROR("Illegal function selector given."); + lua_pushnil(L); + lua_pop(L, 2); + } else { + lua_pop(L, 1); + func_registry[func] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushboolean(L, true); + } + return 1; +} + + +/** + * @brief Push channel options Table onto Lua stack. + * + * Creates a new table on the Lua stack and pushes channel option to it. + * + * @param L The Lua state. + * @param channel_data channel_data_t to fill Lua Table with. + * See include/channel_curl.h and lua_channel_open(). + * @return void + */ +static void channel_push_options(lua_State *L, channel_data_t *channel_data) +{ + lua_newtable(L); + push_to_table(L, "url", channel_data->url); + push_to_table(L, "cached_file", channel_data->cached_file); + push_to_table(L, "auth", channel_data->auth); + push_to_table(L, "request_body", channel_data->request_body); + push_to_table(L, "iface", channel_data->iface); + push_to_table(L, "dry_run", channel_data->dry_run); + push_to_table(L, "cafile", channel_data->cafile); + push_to_table(L, "sslkey", channel_data->sslkey); + push_to_table(L, "sslcert", channel_data->sslcert); + push_to_table(L, "ciphers", channel_data->ciphers); + push_to_table(L, "proxy", channel_data->proxy); + push_to_table(L, "info", channel_data->info); + push_to_table(L, "auth_token", channel_data->auth_token); + push_to_table(L, "content_type", channel_data->content_type); + push_to_table(L, "retry_sleep", channel_data->retry_sleep); + push_to_table(L, "method", channel_data->method); + push_to_table(L, "retries", channel_data->retries); + push_to_table(L, "low_speed_timeout", channel_data->low_speed_timeout); + push_to_table(L, "connection_timeout", channel_data->connection_timeout); + push_to_table(L, "format", channel_data->format); + push_to_table(L, "debug", channel_data->debug); + push_to_table(L, "usessl", channel_data->usessl); + push_to_table(L, "strictssl", channel_data->strictssl); + push_to_table(L, "nocheckanswer", channel_data->nocheckanswer); + push_to_table(L, "nofollow", channel_data->nofollow); + push_to_table(L, "max_download_speed", channel_data->max_download_speed); +} + + +/** + * @brief Set channel options from Lua Table on stack top. + * + * Duplicate (COPY_DEST) channel_data's value if the channel option + * is not set in the Table but is set in channel_data. + * channel_free_options() will free() these values. + * + * @param L The Lua state. + * @param channel_data channel_data_t to fill from Lua Table. + * See include/channel_curl.h and lua_channel_open(). + */ +static void channel_set_options(lua_State *L, channel_data_t *channel_data) +{ + get_from_table(L, "url", channel_data->url, COPY_DEST); + get_from_table(L, "cached_file", channel_data->cached_file, COPY_DEST); + get_from_table(L, "auth", channel_data->auth, COPY_DEST); + get_from_table(L, "request_body", channel_data->request_body, COPY_DEST); + get_from_table(L, "iface", channel_data->iface, COPY_DEST); + get_from_table(L, "dry_run", channel_data->dry_run); + get_from_table(L, "cafile", channel_data->cafile, COPY_DEST); + get_from_table(L, "sslkey", channel_data->sslkey, COPY_DEST); + get_from_table(L, "sslcert", channel_data->sslcert, COPY_DEST); + get_from_table(L, "ciphers", channel_data->ciphers, COPY_DEST); + get_from_table(L, "proxy", channel_data->proxy, COPY_DEST); + get_from_table(L, "info", channel_data->info, COPY_DEST); + get_from_table(L, "auth_token", channel_data->auth_token, COPY_DEST); + get_from_table(L, "content_type", channel_data->content_type, COPY_DEST); + get_from_table(L, "retry_sleep", channel_data->retry_sleep); + get_from_table(L, "method", channel_data->method); + get_from_table(L, "retries", channel_data->retries); + get_from_table(L, "low_speed_timeout", channel_data->low_speed_timeout); + get_from_table(L, "connection_timeout", channel_data->connection_timeout); + get_from_table(L, "format", channel_data->format); + get_from_table(L, "debug", channel_data->debug); + get_from_table(L, "usessl", channel_data->usessl); + get_from_table(L, "strictssl", channel_data->strictssl); + get_from_table(L, "nocheckanswer", channel_data->nocheckanswer); + get_from_table(L, "nofollow", channel_data->nofollow); + char* max_download_speed = NULL; + get_from_table(L, "max_download_speed", max_download_speed); + if (max_download_speed) { + channel_data->max_download_speed = (unsigned int)ustrtoull(max_download_speed, NULL, 10); + free(max_download_speed); + } +} + + +/** + * @brief Free set channel options. + * + * Free memory allocated by channel_set_options() to (string) options. + * + * @param channel_data channel_data_t as in include/channel_curl.h. + */ +static void channel_free_options(channel_data_t *channel_data) +{ + /* Note: If ptr is NULL, no operation is performed as per MALLOC(3). */ + free(channel_data->url); + free(channel_data->cached_file); + free(channel_data->auth); + free(channel_data->request_body); + free(channel_data->auth_token); + free((void *)channel_data->content_type); + free(channel_data->iface); + free(channel_data->cafile); + free(channel_data->sslkey); + free(channel_data->sslcert); + free(channel_data->ciphers); + free(channel_data->proxy); + free(channel_data->info); +} + + +/** + * @brief Set HTTP headers from Lua Table on stack's top. + * + * @param L The Lua state. + * @param headers Pointer to the headers dict. + * @param tablename Stack-top Lua Table's header Subtable name. + * @return true if headers set, false otherwise. + */ +static bool channel_set_header_options(lua_State *L, struct dict *headers, + const char *tablename) +{ + lua_getfield(L, -1, tablename); + if (lua_isnil(L, -1) || lua_type(L, -1) != LUA_TTABLE) { + lua_pop(L, 1); + return false; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_type(L, -1) == LUA_TSTRING && + lua_type(L, -2) == LUA_TSTRING) { + (void)dict_set_value(headers, + lua_tostring(L, -2), + lua_tostring(L, -1)); + } + lua_pop(L, 1); + } + lua_pop(L, 1); + return true; +} + + +/** + * @brief Helper function actually executing a channel operation. + * + * See lua_channel_get() and lua_channel_put() for Lua + * parameter and return values. + * + * @param L The Lua state. + * @param op What channel operation to perform. + */ +static int channel_do_operation(lua_State *L, channel_method_t op) +{ + luaL_checktype(L, -1, LUA_TTABLE); + udchannel *udc = (udchannel *)lua_touserdata(L, lua_upvalueindex(1)); + + if (!udc->channel) { + ERROR("Called GET/PUT channel operation on a closed channel."); + lua_pushnil(L); + lua_pushnumber(L, SERVER_EINIT); + lua_newtable(L); + return 3; + } + + __attribute__((cleanup(channel_free_options))) channel_data_t channel_data = { 0 }; + (void)memcpy(&channel_data, udc->channel_data, sizeof(*udc->channel_data)); + channel_set_options(L, &channel_data); + + struct dict header_send; + LIST_INIT(&header_send); + /* Set HTTP headers as specified while channel creation. */ + if (udc->channel_data->headers_to_send) { + struct dict_entry *entry; + LIST_FOREACH(entry, udc->channel_data->headers_to_send, next) { + (void)dict_insert_value(&header_send, + dict_entry_get_key(entry), + dict_entry_get_value(entry)); + } + + } + /* Set HTTP headers as specified for this operation. */ + (void)channel_set_header_options(L, &header_send, "headers_to_send"); + channel_data.headers_to_send = &header_send; + + /* Setup received HTTP headers dict. */ + struct dict header_receive; + LIST_INIT(&header_receive); + channel_data.received_headers = &header_receive; + + lua_pop(L, 1); + + /* Perform the operation. */ + server_op_res_t result = map_channel_retcode( + op == CHANNEL_GET + ? udc->channel->get(udc->channel, (void *)&channel_data) + : udc->channel->put(udc->channel, (void *)&channel_data)); + + /* Assemble result for passing back to the Lua realm. */ + push_result(L, result); + lua_pushnumber(L, result); + lua_newtable(L); + if (op == CHANNEL_GET || channel_data.method == CHANNEL_POST || + channel_data.method == CHANNEL_PATCH) { + push_to_table(L, "http_response_code", channel_data.http_response_code); + push_to_table(L, "format", channel_data.format); + #ifdef CONFIG_JSON + if (channel_data.format == CHANNEL_PARSE_JSON) { + lua_pushstring(L, "json_reply"); + if (!channel_data.json_reply || + !json_to_table(L, channel_data.json_reply)) { + lua_pushnil(L); + } + lua_settable(L, -3); + + if (channel_data.json_reply && + json_object_put(channel_data.json_reply) != 1) { + ERROR("JSON object should be freed but was not."); + } + } + #endif + if (channel_data.format == CHANNEL_PARSE_RAW) { + lua_pushstring(L, "raw_reply"); + if (!channel_data.raw_reply) { + lua_pushnil(L); + } else { + lua_pushstring(L, channel_data.raw_reply); + free(channel_data.raw_reply); + } + lua_settable(L, -3); + } + + lua_pushstring(L, "received_headers"); + lua_newtable(L); + if (!LIST_EMPTY(channel_data.received_headers)) { + struct dict_entry *entry; + LIST_FOREACH(entry, channel_data.received_headers, next) { + lua_pushstring(L, dict_entry_get_key(entry)); + lua_pushstring(L, dict_entry_get_value(entry)); + lua_settable(L, -3); + } + } + lua_settable(L, -3); + } + + dict_drop_db(&header_send); + dict_drop_db(&header_receive); + + return 3; +} + + +/** + * @brief GET from remote server. + * + * @param [Lua] Channel options Lua Table. + * @return [Lua] True, or, in case of error, nil. + * [Lua] A suricatta.status value. + * [Lua] Operation result Table. + */ +static int lua_channel_get(lua_State *L) +{ + return channel_do_operation(L, CHANNEL_GET); +} + + +/** + * @brief PUT to remote server. + * + * @param [Lua] Channel options Lua Table. + * @return [Lua] True, or, in case of error, nil. + * [Lua] A suricatta.status value. + * [Lua] Operation result Table. + */ +static int lua_channel_put(lua_State *L) +{ + return channel_do_operation(L, CHANNEL_PUT); +} + + +/** + * @brief Get SWUpdate's temporary working directory. + * + * @return [Lua] TMPDIR String. + */ +static int lua_suricatta_get_tmpdir(lua_State *L) +{ + lua_pushstring(L, get_tmpdir()); + return 1; +} + + +/** + * @brief Callback to check for (download) cancellation on server. + * + * Run the Lua equivalent of the dwlwrdata callback function + * (see channel_data_t in include/channel_curl.h) to decide + * on whether an installation should be cancelled while the + * download phase. + * + * The Lua function must have been registered as with the + * "regular" suricatta interface functions. + * + * Note: The "original" suricatta Lua state is suspended here in the + * call to suricatta.{install,download}(), so that it's safe to reuse + * the Lua state here to check for cancellation, mutex'd with other Lua + * callbacks. + * + * @return size * nmemb to continue downloading, != size * nmemb to cancel + */ +static size_t check_cancel_callback(char *streamdata, size_t size, size_t nmemb, void *data) +{ + channel_data_t *channel_data = (channel_data_t *)data; + callback_data_t *callback_data = (callback_data_t *)channel_data->user; + + if (callback_data->lua_check_cancel_func != SURICATTA_FUNC_NULL) { + (void)pthread_mutex_lock(callback_data->lua_lock); + int result = call_lua_func(callback_data->L, + callback_data->lua_check_cancel_func, 0); + (void)pthread_mutex_unlock(callback_data->lua_lock); + if (result == SERVER_UPDATE_CANCELED) { + return 0; + } + } + + if (callback_data->fdout != -1) { + if (copy_write(&callback_data->fdout, streamdata, size * nmemb) != 0) { + return 0; + } + } + + return size * nmemb; +} + + +/** + * @brief IPC wait callback storing error messages into journal. + * + * Callback function for ipc_wait_for_complete() as in ipc/network_ipc.c + * that appends error messages to the journal returned as installation result. + * + * @param msg IPC message. + * @return 0, however not checked by ipc_wait_for_complete(). + */ +static int ipc_wait_for_complete_cb(ipc_message *msg) +{ + if (strncmp(msg->data.status.desc, "ERROR", 5) == 0) { + int lines = (ipc_journal == NULL ? 0 : count_string_array(ipc_journal)) + 2; + const char **tmp = reallocarray(ipc_journal, lines, sizeof(char*)); + if (!tmp) { + ERROR("Cannot (re-)allocate IPC journal memory."); + return 0; + } + ipc_journal = tmp; + ipc_journal[lines - 2] = strndup(msg->data.status.desc, PRINFOSIZE); + ipc_journal[lines - 1] = NULL; + } + return 0; +} + + +/** + * @brief Thread offloading collected progress messages to the server. + * + * Note: The "original" suricatta Lua state is suspended here in the + * call to suricatta.{install,download}(), so that it's safe to reuse + * the Lua state to report progress to the server, mutex'd with other + * Lua callbacks. + * + * @see progress_collector_thread(). + * + * @param data Pointer to a callback_data_t structure. + */ +static void *progress_offloader_thread(void *data) +{ + callback_data_t *thread_data = (callback_data_t *)data; + + while (true) { + (void)pthread_mutex_lock(thread_data->progress_msgq_lock); + if (STAILQ_EMPTY(&thread_data->progress_msgq)) { + (void)pthread_mutex_unlock(thread_data->progress_msgq_lock); + /* Accept cancellation on empty progress message queue. */ + (void)pthread_testcancel(); + } else { + (void)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + struct msgq_data_t *qitem = STAILQ_FIRST( + &thread_data->progress_msgq); + struct progress_msg *message = &qitem->entry; + + (void)pthread_mutex_lock(thread_data->lua_lock); + lua_newtable(thread_data->L); + push_to_table(thread_data->L, "magic", message->magic); + push_to_table(thread_data->L, "status", message->status); + push_to_table(thread_data->L, "dwl_percent", message->dwl_percent); + push_to_table(thread_data->L, "nsteps", message->nsteps); + push_to_table(thread_data->L, "cur_step", message->cur_step); + push_to_table(thread_data->L, "cur_percent", message->cur_percent); + push_to_table(thread_data->L, "cur_image", (char*)message->cur_image); + push_to_table(thread_data->L, "hnd_name", (char*)message->hnd_name); + push_to_table(thread_data->L, "source", message->source); + push_to_table(thread_data->L, "info", (char*)message->info); + #ifdef CONFIG_JSON + if (message->infolen > 0) { + lua_pushstring(thread_data->L, "jsoninfo"); + struct json_object *json_root = json_tokener_parse( + message->info); + if (!json_root || + !json_to_table(thread_data->L, json_root)) { + lua_pushnil(thread_data->L); + } + if (json_root && json_object_put(json_root) != 1) { + ERROR("Progress JSON object should be freed but was not."); + } + lua_settable(thread_data->L, -3); + } + #endif + STAILQ_REMOVE_HEAD(&thread_data->progress_msgq, entries); + free(qitem); + (void)pthread_mutex_unlock(thread_data->progress_msgq_lock); + (void)call_lua_func(thread_data->L, SURICATTA_FUNC_CALLBACK_PROGRESS, 1); + (void)pthread_mutex_unlock(thread_data->lua_lock); + (void)pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + + if (thread_data->drain_progress_msgq == false) { + /* Accept cancellation if messages mustn't be flushed completely. */ + (void)pthread_testcancel(); + } + } + } + return NULL; +} + + +/** + * @brief Cleanup handler for the progress messages collector thread. + * + * Closes the progress socket file descriptor on thread cancellation. + * + * @param pfd Pointer to the progress file descriptor. + */ +static void progress_collector_thread_cleanup(void *pfd) +{ + /* Just close it, ignoring errors. */ + (void)close(*(int *)pfd); +} + + +/** + * @brief Thread collecting progress messages for offload by another thread. + * + * @see progress_offloader_thread(). + * + * @param data Pointer to a callback_data_t structure. + */ +static void *progress_collector_thread(void *data) +{ + callback_data_t *thread_data = (callback_data_t *)data; + struct progress_msg message; + int progress_fd = -1; + + pthread_cleanup_push(progress_collector_thread_cleanup, (void *)&progress_fd); + + while (true) { + if (progress_fd < 0) { + if ((progress_fd = progress_ipc_connect(true)) < 0) { + usleep(250000); + continue; + } + } + + if (progress_ipc_receive(&progress_fd, &message) <= 0) { + continue; + } + + /* Null-terminate progress message strings. */ + if (message.infolen > 0) { + if (message.infolen > sizeof(message.info) - 1) { + message.infolen = sizeof(message.info) - 1; + } + } + message.info[message.infolen] = '\0'; + message.hnd_name[sizeof(message.hnd_name) - 1] = '\0'; + message.cur_image[sizeof(message.cur_image) - 1] = '\0'; + + struct msgq_data_t *qentry = malloc(sizeof(struct msgq_data_t)); + (void)memcpy(&qentry->entry, &message, sizeof(struct progress_msg)); + (void)pthread_mutex_lock(thread_data->progress_msgq_lock); + STAILQ_INSERT_TAIL(&thread_data->progress_msgq, qentry, entries); + (void)pthread_mutex_unlock(thread_data->progress_msgq_lock); + } + + pthread_cleanup_pop(1); + return NULL; +} + + +/** + * @brief Helper function to cancel and join (progress) threads. + * + * @param thread Pointer to thread's pthread_t. + * @param thread_name Name of the thread. + */ +static void join_progress_threads(pthread_t *thread, const char *thread_name) +{ + if (pthread_cancel(*thread) != 0) { + ERROR("Cannot enqueue %s thread cancellation.", thread_name); + } else { + void *join_result; + if (pthread_join(*thread, &join_result) != 0) { + ERROR("Thread join on %s thread failed!", thread_name); + } + if (join_result != PTHREAD_CANCELED) { + ERROR("Thread %s terminated deliberately.", thread_name); + } + } +} + + +/** + * @brief Installation helper doing the installation heavy lifting. + * + * @param [Lua] Channel options Lua Table plus channel={channel} attribute. + * @return [Lua] True, or, in case of error, nil. + * [Lua] A suricatta.status value. + * [Lua] Table with messages in case of error, else empty Table. + * @see lua_suricatta_{install,download}() for parameter details. + * + * @param L The Lua state. + * @param fdout File descriptor to save artifact to, or -1 for IPC. + */ +static void do_install(lua_State *L, int fdout) +{ + __attribute__((cleanup(channel_free_options))) channel_data_t channel_data = { 0 }; + callback_data_t callback_data = { .L = L, .fdout = fdout }; + + lua_getfield(L, -1, "channel"); + if (!lua_istable(L, -1) || (lua_getmetatable(L, -1) == 0)) { + ERROR("Channel table does not have metatable."); + lua_pop(L, 2); + goto error; + } + lua_getfield(L, -1, "__getchannel"); + if (!lua_isfunction(L, -1)) { + ERROR("Channel metatable does not have __getchannel() function."); + lua_pop(L, 4); + goto error; + } + if (lua_pcall(L, 0, 1, 0) != LUA_OK) { + ERROR("Error processing channel metatable: %s", lua_tostring(L, -1)); + lua_pop(L, 3 + 1); + goto error; + } + udchannel *udc = (udchannel *)lua_touserdata(L, -1); + (void)memcpy(&channel_data, udc->channel_data, sizeof(*udc->channel_data)); + lua_pop(L, 3); + channel_set_options(L, &channel_data); + get_from_table(L, "drain_messages", callback_data.drain_progress_msgq); + lua_pop(L, 1); + + channel_data.noipc = fdout == -1 ? false : true; + channel_data.user = (void *)&callback_data; + + /* Global Lua state lock to synchronize the Lua callbacks functions. */ + pthread_mutex_t _lua_lock; + if (pthread_mutex_init(&_lua_lock, NULL)) { + ERROR("Error creating Lua state mutex."); + lua_pop(L, 1); + goto error; + } + callback_data.lua_lock = &_lua_lock; + + /* Setup Lua callback function to call in check_cancel_callback(). */ + channel_data.dwlwrdata = check_cancel_callback; + callback_data.lua_check_cancel_func = SURICATTA_FUNC_NULL; + if (push_registered_lua_func(L, SURICATTA_FUNC_CALLBACK_CHECK_CANCEL)) { + lua_pop(L, 1); + callback_data.lua_check_cancel_func = SURICATTA_FUNC_CALLBACK_CHECK_CANCEL; + } + + /* Setup progress message handling threads and Lua callback function. */ + pthread_mutex_t _progress_msgq_lock; + pthread_t _thread_progress_collector; + pthread_t _thread_progress_offloader; + if (push_registered_lua_func(L, SURICATTA_FUNC_CALLBACK_PROGRESS)) { + lua_pop(L, 1); + + /* Initialize progress messages (tail) queue. */ + STAILQ_INIT(&callback_data.progress_msgq); + + /* Initialize progress messages (tail) queue synchronization mutex. */ + if (pthread_mutex_init(&_progress_msgq_lock, NULL)) { + ERROR("Error creating progress message queue mutex."); + lua_pop(L, 1); + goto error; + } + callback_data.progress_msgq_lock = &_progress_msgq_lock; + + /* Spawn threads handling progress notification to server. */ + if ((pthread_create(&_thread_progress_collector, NULL, + progress_collector_thread, + &callback_data) != 0) || + (pthread_create(&_thread_progress_offloader, NULL, + progress_offloader_thread, + &callback_data) != 0)) { + ERROR("Error starting progress message handling threads."); + lua_pop(L, 1); + goto error; + } + callback_data.thread_collector = &_thread_progress_collector; + callback_data.thread_offloader = &_thread_progress_offloader; + } + + /* Perform the operation.... */ + server_op_res_t result = map_channel_retcode( + udc->channel->get_file(udc->channel, (void *)&channel_data)); + RECOVERY_STATUS iresult = (RECOVERY_STATUS)ipc_wait_for_complete( + ipc_wait_for_complete_cb); + + /* Clean up the progress message handling threads and queue. */ + if (callback_data.thread_collector && callback_data.thread_offloader) { + join_progress_threads(callback_data.thread_offloader, "progress_offloader"); + join_progress_threads(callback_data.thread_collector, "progress_collector"); + + while (!STAILQ_EMPTY(&callback_data.progress_msgq)) { + struct msgq_data_t *entry = STAILQ_FIRST( + &callback_data.progress_msgq); + STAILQ_REMOVE_HEAD(&callback_data.progress_msgq, entries); + free(entry); + } + } + + if (result == SERVER_OK) { + result = iresult == FAILURE ? SERVER_EERR : SERVER_OK; + } + push_result(L, result); + lua_pushnumber(L, result); + lua_newtable(L); + if (ipc_journal) { + const char **iter = ipc_journal; + while (*iter != NULL) { + lua_pushstring(L, *iter); + lua_rawseti(L, -2, (int)lua_rawlen(L, -2) + 1); + iter++; + } + free_string_array((char **)ipc_journal); + ipc_journal = NULL; + } + + goto done; +error: + lua_pushnil(L); + lua_pushnumber(L, SERVER_EINIT); + lua_newtable(L); +done: + if (callback_data.progress_msgq_lock) { + if (pthread_mutex_destroy(callback_data.progress_msgq_lock) != 0) { + ERROR("Mutex deallocation for progress message queue failed!"); + } + } + if (callback_data.lua_lock) { + if (pthread_mutex_destroy(callback_data.lua_lock) != 0) { + ERROR("Mutex deallocation for Lua state failed!"); + } + } +} + + +/** + * @brief Install an update artifact from remote server or local file. + * + * For installing a local update artifact file, the url Table field should + * read file:///path/to/file.swu. For downloading *and* installing an update + * artifact, the url should read, e.g., https://artifacts.io/update.swu. + * Note that this file is to be deleted, if applicable, from the Lua realm. + * + * @param [Lua] Channel options Lua Table plus mandatory channel={channel} attribute. + * @return [Lua] True, or, in case of error, nil. + * [Lua] A suricatta.status value. + * [Lua] Table with messages in case of error, else empty Table. + */ +static int lua_suricatta_install(lua_State *L) +{ + luaL_checktype(L, -1, LUA_TTABLE); + do_install(L, -1); + return 3; +} + + +/** + * @brief Download an update artifact from remote server (w/o installing it). + * + * @param [Lua] Download channel options Lua Table plus mandatory channel={channel} attribute. + * @param [Lua] Output local file name + * @return [Lua] True, or, in case of error, nil. + * [Lua] A suricatta.status value. + * [Lua] Table with messages in case of error, else empty Table. + */ +static int lua_suricatta_download(lua_State *L) +{ + luaL_checktype(L, -1, LUA_TSTRING); + luaL_checktype(L, -2, LUA_TTABLE); + DEBUG("Saving artifact to %s", luaL_checkstring(L, -1)); + int fdout = open(luaL_checkstring(L, -1), O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fdout == -1) { + ERROR("Cannot open %s for writing.", luaL_checkstring(L, -1)); + lua_pop(L, 2); + lua_pushnil(L); + lua_pushnumber(L, SERVER_EINIT); + lua_newtable(L); + return 3; + } + lua_pop(L, 1); + do_install(L, fdout); + (void)close(fdout); + return 3; +} + + +/** + * @brief Helper to push channel closure to Lua stack. + * + * Helper function to push the channel's closure variables 'channel' and + * 'channel_data' (via udchannel) to Lua's stack to allow C functions not + * in the channel's closure domain to access them via lua_touserdata(). + * + * @return [Lua] udchannel. + */ +static int push_channel_closure(lua_State *L) +{ + udchannel *udc = lua_touserdata(L, lua_upvalueindex(1)); + udchannel *tgt = lua_newuserdata(L, sizeof(udchannel)); + *tgt = *udc; + luaL_checktype(L, -1, LUA_TUSERDATA); + return 1; +} + + +/** + * @brief Helper function deallocating a channel. + * @param channel Channel to close. + * @param channel_data Channel data as per include/channel_curl.h. + */ +static void do_channel_close(channel_t *channel, channel_data_t *channel_data) +{ + if (channel) { + (void)channel->close(channel); + free(channel); + channel = NULL; + } + + if (channel_data) { + channel_free_options(channel_data); + + if (channel_data->headers_to_send) { + dict_drop_db(channel_data->headers_to_send); + free(channel_data->headers_to_send); + } + + free(channel_data); + channel_data = NULL; + } +} + + +/** + * @brief Close channel to remote server. + */ +static int lua_channel_close(lua_State *L) +{ + udchannel *udc = (udchannel *)lua_touserdata(L, lua_upvalueindex(1)); + if (!udc->channel && !udc->channel_data) { + ERROR("Called CLOSE operation on a closed channel."); + } + do_channel_close(udc->channel, udc->channel_data); + return 0; +} + + +/** + * @brief Open channel to remote server. + * + * @param [Lua] Lua Table with Channel defaults. + * See channel_set_options() and include/channel_curl.h. + * @return [Lua] True, or, in case of error, nil. + * [Lua] Channel operations Table, empty Table in case of error. + */ +static int lua_channel_open(lua_State *L) +{ + luaL_checktype(L, -1, LUA_TTABLE); + + channel_t *channel = NULL; + channel_data_t *channel_data = malloc(sizeof(*channel_data)); + if (!channel_data) { + ERROR("Cannot allocate channel memory."); + goto failure; + } + (void)memcpy(channel_data, &channel_data_defaults, sizeof(*channel_data)); + + /* Set common options selectively overridable in channel operations. */ + channel_set_options(L, channel_data); + + channel_data->headers_to_send = malloc(sizeof(struct dict)); + if (!channel_data->headers_to_send) { + ERROR("Cannot allocate channel header memory."); + goto failure; + } + + /* Set global default HTTP header options for channel. */ + LIST_INIT(channel_data->headers_to_send); + (void)channel_set_header_options(L, channel_data->headers_to_send, + "headers_to_send"); + + lua_pop(L, 1); + + luaL_Reg lua_funcs_channel[] = { + { "get", lua_channel_get }, + { "put", lua_channel_put }, + { "close", lua_channel_close }, + { NULL, NULL } + }; + + channel = channel_new(); + if (!channel) { + ERROR("Cannot allocate new channel memory."); + goto failure; + } + + channel_op_res_t result = channel->open(channel, channel_data); + if (result != CHANNEL_OK) { + ERROR("Cannot open channel."); + goto failure; + } + + lua_pushboolean(L, true); + lua_newtable(L); + + lua_pushstring(L, "options"); + channel_push_options(L, channel_data); + lua_settable(L, -3); + + udchannel *udc = lua_newuserdata(L, sizeof(udchannel)); + udc->channel = channel; + udc->channel_data = channel_data; + lua_pushvalue(L, -1); + lua_insert(L, -3); + luaL_setfuncs(L, lua_funcs_channel, 1); + lua_insert(L, -2); + lua_pushcclosure(L, push_channel_closure, 1); + lua_createtable(L, 0, 1); + lua_insert(L, -2); + lua_setfield(L, -2, "__getchannel"); + lua_setmetatable(L, -2); + return 2; + +failure: + do_channel_close(channel, channel_data); + lua_pushnil(L); + lua_newtable(L); + return 2; +} + + +/** + * @brief Get update state from persistent storage (bootloader). + * + * @return [Lua] True, or, in case of error, nil. + * [Lua] One of pstate's enum values. + */ +static int lua_pstate_get(lua_State *L) +{ + update_state_t state = get_state(); + if (is_valid_state(state)) { + lua_pushboolean(L, true); + lua_pushnumber(L, (int)state); + } else { + lua_pushnil(L); + lua_pushnil(L); + } + return 2; +} + + +/** + * @brief Save update state to persistent storage (bootloader). + * + * @param [Lua] One of pstate's enum values. + * @return [Lua] True, or, in case of error, nil. + */ +static int lua_pstate_save(lua_State *L) +{ + update_state_t state = luaL_checknumber(L, -1); + lua_pop(L, 1); + if (!is_valid_state(state)) { + lua_pushnil(L); + } else { + push_result(L, save_state(state) == 0 ? SERVER_OK : SERVER_EERR); + } + return 1; +} + + +/** + * @brief Sleep for a specified number of seconds. + * + * Seconds timer resolution is sufficient for the purpose of a + * short pause in loops. That's why handling the return value + * (short-sleep due to signal) is deliberately ignored. + * + * @param [Lua] Number of seconds to sleep. + */ +static int lua_suricatta_sleep(lua_State *L) +{ + (void)sleep((unsigned int)luaL_checkinteger(L, -1)); + return 0; +} + + +/** + * @brief Register the 'suricatta' module to Lua. + * + * @param L The Lua state. + * @return 1, i.e., the 'suricatta' module Table on stack. + */ +static int suricatta_lua_module(lua_State *L) +{ + /* Remove 'modname' string from stack. */ + lua_pop(L, 1); + + luaL_checkversion(L); + lua_newtable(L); + luaL_Reg lua_funcs_suricatta[] = { + { "sleep", lua_suricatta_sleep }, + { "install", lua_suricatta_install }, + { "download", lua_suricatta_download }, + { "get_tmpdir", lua_suricatta_get_tmpdir }, + { "getversion", lua_get_swupdate_version }, + { NULL, NULL } + }; + luaL_setfuncs(L, lua_funcs_suricatta, 0); + + lua_pushstring(L, "server"); + lua_newtable(L); + luaL_Reg lua_funcs_server[] = { + { "register", register_lua_func }, + { NULL, NULL } + }; + luaL_setfuncs(L, lua_funcs_server, 0); + #define MAP(x) push_to_table(L, #x, SURICATTA_FUNC_ ## x); + SURICATTA_FUNCS + #undef MAP + lua_settable(L, -3); + + lua_pushstring(L, "status"); + lua_newtable(L); + push_to_table(L, "OK", SERVER_OK); + push_to_table(L, "EERR", SERVER_EERR); + push_to_table(L, "EBADMSG", SERVER_EBADMSG); + push_to_table(L, "EINIT", SERVER_EINIT); + push_to_table(L, "EACCES", SERVER_EACCES); + push_to_table(L, "EAGAIN", SERVER_EAGAIN); + push_to_table(L, "UPDATE_AVAILABLE", SERVER_UPDATE_AVAILABLE); + push_to_table(L, "NO_UPDATE_AVAILABLE", SERVER_NO_UPDATE_AVAILABLE); + push_to_table(L, "UPDATE_CANCELED", SERVER_UPDATE_CANCELED); + push_to_table(L, "ID_REQUESTED", SERVER_ID_REQUESTED); + lua_settable(L, -3); + + luaL_Reg lua_funcs_pstate[] = { + { "get", lua_pstate_get }, + { "save", lua_pstate_save }, + { NULL, NULL } + }; + lua_pushstring(L, "pstate"); + lua_newtable(L); + luaL_setfuncs(L, lua_funcs_pstate, 0); + push_enum_to_table(L, "OK", STATE_OK); + push_enum_to_table(L, "INSTALLED", STATE_INSTALLED); + push_enum_to_table(L, "TESTING", STATE_TESTING); + push_enum_to_table(L, "FAILED", STATE_FAILED); + push_enum_to_table(L, "NOT_AVAILABLE", STATE_NOT_AVAILABLE); + push_enum_to_table(L, "ERROR", STATE_ERROR); + push_enum_to_table(L, "WAIT", STATE_WAIT); + push_enum_to_table(L, "IN_PROGRESS", STATE_IN_PROGRESS); + lua_settable(L, -3); + + luaL_Reg lua_funcs_channel[] = { + { "open", lua_channel_open }, + { NULL, NULL } + }; + lua_pushstring(L, "channel"); + lua_newtable(L); + luaL_setfuncs(L, lua_funcs_channel, 0); + + lua_pushstring(L, "options"); + channel_push_options(L, &channel_data_defaults); + lua_settable(L, -3); + + lua_pushstring(L, "content"); + lua_newtable(L); + push_to_table(L, "RAW", CHANNEL_PARSE_RAW); + push_to_table(L, "JSON", CHANNEL_PARSE_JSON); + push_to_table(L, "NONE", CHANNEL_PARSE_NONE); + lua_settable(L, -3); + + lua_pushstring(L, "method"); + lua_newtable(L); + push_to_table(L, "GET", CHANNEL_GET); + push_to_table(L, "POST", CHANNEL_POST); + push_to_table(L, "PUT", CHANNEL_PUT); + push_to_table(L, "PATCH", CHANNEL_PATCH); + lua_settable(L, -3); + + lua_settable(L, -3); + + luaL_Reg lua_funcs_notify[] = { + { "error", lua_notify_error }, + { "trace", lua_notify_trace }, + { "info", lua_notify_info }, + { "warn", lua_notify_warn }, + { "debug", lua_notify_debug }, + { "progress", lua_notify_progress }, + { NULL, NULL } + }; + lua_pushstring(L, "notify"); + lua_newtable(L); + luaL_setfuncs(L, lua_funcs_notify, 0); + lua_settable(L, -3); + + return 1; +} + + +/** + * @brief Unload and de-initialize Lua state and the Suricatta Lua module. + */ +static void suricatta_lua_destroy(void) +{ + if (!gL) { + return; + } + lua_close(gL); + gL = NULL; +} + + +/** + * @brief Helper function for Lua Table nil-indexing. + * + * Read-accessing an absent field in a Lua Table will invoke and return + * the result of the __index metamethod, if defined, or, if not defined + * as per default, return nil. + * As a consequence, in nested Lua Tables, all intermediate Tables on a + * "path" must exist as it is the case in the following example + * local tbl = { a = { b = { c = { d = 23 } } } } + * print(tbl.a.b.c.d) + * or an "attempt to index a nil value" error is triggered, requiring + * existence checks for every node in the "path" to be implemented. + * + * For convenience, on_nil_table_index() may be registered as __index + * metamethod to nil returning zero results, i.e., nil, if a node in + * the Table "path" is nil without triggering the "attempt to index + * a nil value" error. + * + * @param L The Lua state. + * @param [Lua] The table in which the look-up failed. + * @param [Lua] The key name whose look-up failed. + * @return 0, i.e., nil + */ +static int on_nil_table_index(lua_State *L) +{ + /* Pop table and key parameters. */ + lua_pop(L, 2); + return 0; +} + + +/** + * @brief Load and initialize Lua and the Suricatta Lua module. + * + * @return SERVER_OK, or, in case of errors, SERVER_EINIT. + */ +static server_op_res_t suricatta_lua_create(void) +{ + if (gL) { + TRACE("[Lua suricatta] Lua state already initialized."); + return SERVER_OK; + } + if (!(gL = luaL_newstate())) { + ERROR("Unable to register Suricatta Lua module."); + return SERVER_EINIT; + } + luaL_openlibs(gL); + luaL_requiref(gL, "suricatta", suricatta_lua_module, true); + lua_pop(gL, 1); + + #if defined(CONFIG_EMBEDDED_SURICATTA_LUA) + if ((luaL_loadbuffer(gL, EMBEDDED_SURICATTA_LUA_SOURCE_START, + EMBEDDED_SURICATTA_LUA_SOURCE_END - + EMBEDDED_SURICATTA_LUA_SOURCE_START, + "LuaSuricatta") || + lua_pcall(gL, 0, LUA_MULTRET, 0)) != LUA_OK) { + INFO("No compiled-in Suricatta Lua module(s) found."); + TRACE("Lua exception:\n%s", lua_tostring(gL, -1)); + lua_pop(gL, 1); + return SERVER_EINIT; + } + #else + if (luaL_dostring(gL, "require (\"swupdate_suricatta\")") != LUA_OK) { + ERROR("Error while executing require: %s", lua_tostring(gL, -1)); + WARN("No Suricatta Lua module(s) found."); + if (luaL_dostring(gL, "return " + "package.path:gsub(';','\\n'):gsub('?','" + "swupdate_suricatta')") == 0) { + lua_pop(gL, 1); + TRACE("Suricatta Lua module search path:\n%s", + lua_tostring(gL, -1)); + lua_pop(gL, 1); + } + return SERVER_EINIT; + } + #endif + + #define MAP(x) #x, + static const char *const function_names[] = { SURICATTA_FUNCS }; + #undef MAP + for (int i = 0; i <= SURICATTA_FUNC_MANDATORY; i++) { + if (func_registry[i] == SURICATTA_FUNC_NULL) { + ERROR("Lua function for %s required but not registered.", + function_names[i]); + return SERVER_EINIT; + } + } + + /* Assign __index metamethod to nil for convenient nil-indexing tables. */ + lua_pushnil(gL); + lua_createtable(gL, 0, 1); + lua_pushcfunction(gL, on_nil_table_index); + lua_setfield(gL, -2, "__index"); + lua_setmetatable(gL, -2); + lua_pop(gL, 1); + + return SERVER_OK; +} + + +/** + * @brief Iterator over SWUpdate configuration section key-value pairs. + * + * @param settings Pointer to configuration section. + * @param data The Lua state to pass through to callback. + * @return 0 + */ +static int config_section_to_table(void *setting, void *data) +{ + for (int i = 0; i < config_setting_length(setting); i++) { + config_setting_t *entry = config_setting_get_elem(setting, i); + switch (config_setting_type(entry)) { + case CONFIG_TYPE_INT: + push_to_table((lua_State *)data, entry->name, + config_setting_get_int(entry)); + break; + case CONFIG_TYPE_INT64: + push_to_table((lua_State *)data, entry->name, + config_setting_get_int64(entry)); + break; + case CONFIG_TYPE_STRING: + push_to_table((lua_State *)data, entry->name, + config_setting_get_string(entry)); + break; + case CONFIG_TYPE_BOOL: + push_to_table((lua_State *)data, entry->name, + config_setting_get_bool(entry)); + break; + case CONFIG_TYPE_FLOAT: + push_to_table((lua_State *)data, entry->name, + config_setting_get_float(entry)); + break; + } + } + + return 0; +} + + +/** + * @brief Start the Suricatta Lua module. + * + * @param fname The configuration file to read additional configuration from. + * @param argc The number of arguments. + * @param argv The array of arguments. + * @return SERVER_OK, or, in case of errors, SERVER_EINIT or SERVER_EERR. + */ +server_op_res_t server_start(char *fname, int argc, char *argv[]) +{ + if (suricatta_lua_create() != SERVER_OK) { + suricatta_lua_destroy(); + return SERVER_EINIT; + } + + if (channel_curl_init() != CHANNEL_OK) { + suricatta_lua_destroy(); + return SERVER_EINIT; + } + + /* Prepare argument Table #1: Channel default options. */ + channel_push_options(gL, &channel_data_defaults); + + /* Prepare argument Table #2: Command line options. */ + lua_newtable(gL); + for (char **pargv = argv + 1; *pargv != argv[argc]; pargv++) { + lua_pushstring(gL, *pargv); + lua_rawseti(gL, -2, (int)lua_rawlen(gL, -2) + 1); + } + + /* Prepare argument Table #3: Configuration file options. */ + lua_newtable(gL); + push_to_table(gL, "polldelay", CHANNEL_DEFAULT_POLLING_INTERVAL); + if (fname) { + swupdate_cfg_handle handle; + swupdate_cfg_init(&handle); + if (swupdate_cfg_read_file(&handle, fname) == 0) { + if (read_module_settings(&handle, CONFIG_SECTION, + config_section_to_table, gL) != 0) { + ERROR("Error reading module settings \"%s\" from %s", + CONFIG_SECTION, fname); + } + } + swupdate_cfg_destroy(&handle); + } + return map_lua_result(call_lua_func(gL, SURICATTA_FUNC_SERVER_START, 3)); +} + + +/** + * @brief Stop the Suricatta Lua module. + * + * @return SERVER_OK or, in case of errors, any other from server_op_res_t. + */ +server_op_res_t server_stop(void) +{ + server_op_res_t result = map_lua_result( + call_lua_func(gL, SURICATTA_FUNC_SERVER_STOP, 0)); + suricatta_lua_destroy(); + return result; +} + + +/** + * @brief Print the Suricatta Lua module's help text. + */ +void server_print_help(void) +{ + if (suricatta_lua_create() != SERVER_OK) { + fprintf(stderr, "Error loading Suricatta Lua module.\n"); + suricatta_lua_destroy(); + return; + } + channel_push_options(gL, &channel_data_defaults); + push_to_table(gL, "polldelay", CHANNEL_DEFAULT_POLLING_INTERVAL); + (void)call_lua_func(gL, SURICATTA_FUNC_PRINT_HELP, 1); + suricatta_lua_destroy(); +} + + +/** + * @brief Get the polling interval. + * + * Note: The polling interval may be fetched from the remote server within + * the Lua realm, set locally via IPC, or be calculated otherwise by the + * Lua function implementing server_get_polling_interval(). + * + * @return Polling interval in seconds. + */ +unsigned int server_get_polling_interval(void) +{ + int result = call_lua_func(gL, SURICATTA_FUNC_GET_POLLING_INTERVAL, 0); + return result >= 0 ? (unsigned int)result : CHANNEL_DEFAULT_POLLING_INTERVAL; +} + + +/** + * @brief Query the remote server for pending actions. + * + * @param action_id Action ID of the pending action. + * @return SERVER_UPDATE_AVAILABLE and SERVER_ID_REQUESTED are handled + * in suricatta/suricatta.c, the others result in suricatta + * sleeping again. + */ +server_op_res_t server_has_pending_action(int *action_id) +{ + lua_pushnumber(gL, *action_id); + server_op_res_t result = map_lua_result( + call_lua_func(gL, SURICATTA_FUNC_HAS_PENDING_ACTION, 1)); + if ((lua_gettop(gL) > 0) && (lua_isnumber(gL, -1))) { + /* + * The Lua function may return action_id only in the non-error case, + * so don't strictly require it to be returned. + */ + *action_id = (int)lua_tointeger(gL, -1); + lua_pop(gL, 1); + } + return result; +} + + +/** + * @brief Install an update. + * + * @return SERVER_OK or, in case of errors, any other from server_op_res_t. + */ +server_op_res_t server_install_update(void) +{ + return map_lua_result(call_lua_func(gL, SURICATTA_FUNC_INSTALL_UPDATE, 0)); +} + + +/** + * @brief Send device configuration/data to remote server. + * + * @return SERVER_OK or, in case of errors, any other from server_op_res_t. + */ +server_op_res_t server_send_target_data(void) +{ + return map_lua_result(call_lua_func(gL, SURICATTA_FUNC_SEND_TARGET_DATA, 0)); +} + + +/** + * @brief Handle IPC message sent to Suricatta Lua module. + * + * @param msg IPC message. + * @return SERVER_OK or, in case of errors, any other from server_op_res_t. + */ +server_op_res_t server_ipc(ipc_message *msg) +{ + lua_newtable(gL); + push_to_table(gL, "magic", msg->magic); + push_to_table(gL, "cmd", msg->data.procmsg.cmd); + lua_pushstring(gL, "commands"); + lua_newtable(gL); + push_to_table(gL, "CONFIG", CMD_CONFIG); + push_to_table(gL, "ACTIVATION", CMD_ACTIVATION); + push_to_table(gL, "GET_STATUS", CMD_GET_STATUS); + /* CMD_ENABLE is handled directly in suricatta/suricatta.c */ + lua_settable(gL, -3); + lua_pushstring(gL, "msg"); + lua_pushlstring(gL, (char *)msg->data.procmsg.buf, msg->data.procmsg.len); + lua_settable(gL, -3); + #ifdef CONFIG_JSON + if (msg->data.procmsg.len > 0) { + lua_pushstring(gL, "json"); + struct json_object *json_root = json_tokener_parse(msg->data.procmsg.buf); + if (!json_root || !json_to_table(gL, json_root)) { + ERROR("Error parsing JSON IPC string: %s\n", msg->data.procmsg.buf); + lua_pushnil(gL); + } + lua_settable(gL, -3); + if (json_root && json_object_put(json_root) != 1) { + ERROR("JSON object should be freed but was not."); + } + } + #endif + server_op_res_t result = map_lua_result(call_lua_func(gL, SURICATTA_FUNC_IPC, 1)); + msg->type = result == SERVER_OK ? ACK : NACK; + msg->data.procmsg.len = 0; + if (lua_isstring(gL, -1)) { + /* + * Dancing here to not fail on alignment-sensitive architectures as + * *size_t has a different alignment requirement than *unsigned int. + */ + size_t len; + (void)strncpy(msg->data.procmsg.buf, lua_tolstring(gL, -1, &len), + sizeof(msg->data.procmsg.buf) - 1); + msg->data.procmsg.buf[sizeof(msg->data.procmsg.buf)-1] = '\0'; + msg->data.procmsg.len = (unsigned int)len; + lua_pop(gL, 1); + } + return result; +} From patchwork Thu Jun 2 09:11:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638263 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=dpuhl3cy; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::33a; helo=mail-wm1-x33a.google.com; envelope-from=swupdate+bncbdd6bwv65qpbbdh64gkamgqeb2nnv4y@googlegroups.com; receiver=) Received: from mail-wm1-x33a.google.com (mail-wm1-x33a.google.com [IPv6:2a00:1450:4864:20::33a]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDKzg5z5gz9s0w for ; Thu, 2 Jun 2022 19:12:47 +1000 (AEST) Received: by mail-wm1-x33a.google.com with SMTP id bg13-20020a05600c3c8d00b003974d0ff917sf2539080wmb.3 for ; Thu, 02 Jun 2022 02:12:47 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161164; cv=pass; d=google.com; s=arc-20160816; b=t3X508Gcsh52lScnxVZMc67Q/yGu1hotGyCwxI3m48RsVPUVfHpAu5gF0+qh7fudu6 QoEJgLG4utq/B+HdtmwDhgm1Cl2O5lGuNnPtCy6DsdChsYSXtIX2RLuc76VPiKRWVfA/ TBDsy65k0le5XDwDicSNmpkc/icj0u1KfuXoVHdMrbV6TzjhL18BYrtyKTToMzRTAPvB 6g5JCBdxqb44utjhSIrW1H/I3txhmXLFb5mc3m2X+4MlIgCpM9NwmuBs5SRhfZOqLWE2 4Qse0v8z6BrBbZeZ2QqAuI+egNC3RA3D34o8517Kmidb38td5+E+I2mlD0KnJV49JVbF ttHw== ARC-Message-Signature: i=3; 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:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:sender:dkim-signature; bh=OoxycozuKmgl6V5GXhcMUFmXZBx/Ju/gosn1l5Vzna4=; b=qLngtgJHiq8XAikIwnq0xkVwUhdBQfIvikAFYcLj4QdDJdTvIVyrC4jLix8r5k4Ze6 iswu0YmLKlmJaI7YicNqf0S/LIbAo40ZbBJ4GuO7EIp5AhXoCWmXJ3n+WuSd/CZkLgIi +LMWrTxURCblVOywmSqhjclnFGuN3i82hAxVlXe25Etwc3jkpem0imH5L7UvniutmvCw N8DU827KISaoQY89RfhrXApdzEl7p3TGtkpQjGq4UC7cBpu/5H1CaDg1vpJ4gY6Xs2bX xVd1ATl3cXd7ATUMnIoaNeW8RBa+NNUdK5fsunqDsUypYtgVL9b96aUSLQ/Qgl9EB0DB j6JQ== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=u1aQR98e; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::62f as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=OoxycozuKmgl6V5GXhcMUFmXZBx/Ju/gosn1l5Vzna4=; b=dpuhl3cyRDn/A7tnJ5K3rSdu2LguBX3ntyw0HnqAQAI5ZOxS2CUd1cZccwP4YaP111 1WS8sX/OPtAc3srCGOvIFwOgLKOmkDhWcfG027sPMwHX1OR/Shu/CLDe1H6YJZ69DLHI O4XeyTPTEjhccrThwz8YxLwtI8x5rlZA4P6UDy/MwcJoz3zQmGSpyvrpaclDVWUVgEHI 6hxYn4b0jZEXBKLRJAYpRrsJG5VbBvibTKD0e41Uq2/0wO+XoCUsffhJwoUOCAAzxqAZ 7dIBOBaxsgFMZHYBb710UhcKJldRnsiOZZHIGlcKwoIk9tuKMmuc67v6RyiPYBweR+W1 wuOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding :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=OoxycozuKmgl6V5GXhcMUFmXZBx/Ju/gosn1l5Vzna4=; b=23vQen+bjcTDzYCebKktN0MIyWiGI7AsOt7+tdmTAiek35G6CCmu9o001nDsjLOq6u 2KeqDQNhw9hn16voiePy4ot4iehu69QcbqaC/W5sQs6IeBDjJB6zUKsxWZX5dpKSbFOj ycjnWwfT/w9jKHBrFLnOfLPe/8+xVsKLeY2eApw1h1Aarn/gvdrNaEw89iCCsUgWBhuT PFu6ibzThrkVK9cepmTN39SoI0FOlMxOAF9ZeI5xNM1C1Iu9hTNw/LxGk0EV6FFjI6PZ hRiLQbq9fGE6WRz22u0YyUYv5FC8tfcB/un2mF3Q+olL+IerRjtqpyrPgesMU9EfETb1 ZBVQ== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM531rfQ/0TqU0aYLeg1jaBaLHb6p4+PyTNmxVEEVUTcrKwGxSDuvp aPkHaWMXyqW+OoZyg6zVyDE= X-Google-Smtp-Source: ABdhPJylrUweIwbvMEcB1znBfrkXtOnNuLX92Uit9+iEHGguC1MGfP9akYj1jwk7F3JFavmt16TGfQ== X-Received: by 2002:a05:6000:1a8c:b0:20c:bd6b:ecaf with SMTP id f12-20020a0560001a8c00b0020cbd6becafmr2754142wry.341.1654161164487; Thu, 02 Jun 2022 02:12:44 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a5d:6dab:0:b0:212:d9db:a98 with SMTP id u11-20020a5d6dab000000b00212d9db0a98ls2666077wrs.3.gmail; Thu, 02 Jun 2022 02:12:43 -0700 (PDT) X-Received: by 2002:adf:ebc7:0:b0:20c:d65d:3f19 with SMTP id v7-20020adfebc7000000b0020cd65d3f19mr2794122wrn.613.1654161163442; Thu, 02 Jun 2022 02:12:43 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161163; cv=pass; d=google.com; s=arc-20160816; b=orcaJcHri7iy4plNsuvHZtrUSF9ZXZ8cADnrj0RDskAry+gviPvszVWUuGwD+lbgXr BYntuMXJg5ysxOJnBQQjy6sC98XY4euGos1EySgEsSPE83hOluvELLB/Xr0opc+7gavE LzVaPMGDD6ia6i4xKjuirm9SOkNba46owLppcaW7+qq3bxZKKyUvt9X+owg500AV0AkK iMF8eJqTTkhG4MIPwOzaj7Uc01LzIcG8XNqLsHbVOreApAUht9FPXS82vyKRCmQUh9Z2 H9Ki4N3Iqu8Gz9yvy5/MX1eS7GTNKr8crFc3mkqTYAz4A3LS0n4CLBRvBee1eN6R8wh4 x4GA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=mHrVN9+FIZrOj+kKdr19OK8g/xcA1NYKFIUB/1SNKLE=; b=uh+Vmc1F7PyX1BZJKjW5XEsT9lbA9YWY7GzZruPQv8NYgV4XXBA0s8D3Uorv/6qLde gSVdQIffEEoBi0JyZw/yh4AFi34zLiGpsCdsITwTbWQjkdpM5UNu7j0cDXczkggw7qHD FHL6HWcugbyigFzt3jCtFgF0eMVYnpaArRHXlN7fQR1Zcg+jmNVBLnDYEZ1STpMbJM+T AYXdtW+geNBf8kZTJUtQZNp/0RlDMGCTNny3LlA86fHgmRTpq3gtjJu1pS5xDW4DXS08 zBkprHy4BOlA36okbmSPzFnB8OpEIgIHIp3m6O2lr72Q2p9QlPzJz46fVNgJTITJt3oX IkfA== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=u1aQR98e; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::62f as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR02-AM5-obe.outbound.protection.outlook.com (mail-am5eur02on062f.outbound.protection.outlook.com. [2a01:111:f400:fe07::62f]) by gmr-mx.google.com with ESMTPS id o24-20020a05600c511800b00397320af7e9si367559wms.4.2022.06.02.02.12.43 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:12:43 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::62f as permitted sender) client-ip=2a01:111:f400:fe07::62f; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Wi+Kwp2u9RlgpYQmYyaLe07qwwHmZ/R2a3Z4Oe64NBQJBBOBaGBKjsWCA+dEz3D61aOktX8luby2W74eyVgxLOzcmH8xB0BfH8XibgX+BGvvszAltQ69m+t+f8Nc+ps+EkevItksmpIcBoFBJ6gb8Y/WonNHQXQ2EHhebrMJJApZ7COo1V5PWUgN519MKJNuEmjlmNEav5ML55BckAVHEAlvy/PI97DBb0qBJFy+4mtiClKgNgiWNsd6s2SlI/i1vis7TdA6zlkOeCFLnAuJ1AQVZWobamk5wyXtGavNsjgGnPADI1WlIlW/7vcormW1psqRycmWh50sGglf8f+v8A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=mHrVN9+FIZrOj+kKdr19OK8g/xcA1NYKFIUB/1SNKLE=; b=XHkVOMDcat7p46DCn2q7wtToGarwtlrtILRc9pOyylmaZgbE5U3CurEPydkaQIt7mHHGfwWDzSt8DGp5tFXFC7ombMduHXE8/iTF19MPSPIHmoiiJSPdH/wOIpDPVcnf6q4dJ5gyD1/0Be3FyxXUgI9x/5k6yyi8y4KdR6GK/TMENxidcpBfkHiLZ+YlKdxyTL9F9AjB9gK7XpO2YN+GbABmSXfiTPQJZQn8pqWsC9xo+PHK2C9Iw5G/9QuB8+qodMQsXg79X6q+xd29m7Qu4Rm/uTu96m+7+Jgxd3CkiVxdyLW39V6/NAUEvJSrGa5GklAp6ltsZ2X7Bl0mQZxfPg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.72) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from AM5PR0601CA0064.eurprd06.prod.outlook.com (2603:10a6:206::29) by DB6PR10MB1717.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:6:3a::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5293.13; Thu, 2 Jun 2022 09:12:41 +0000 Received: from VE1EUR01FT104.eop-EUR01.prod.protection.outlook.com (2603:10a6:206:0:cafe::e8) by AM5PR0601CA0064.outlook.office365.com (2603:10a6:206::29) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.12 via Frontend Transport; Thu, 2 Jun 2022 09:12:41 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.72) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.72 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.72; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.72) by VE1EUR01FT104.mail.protection.outlook.com (10.152.3.120) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:12:41 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SMA.ad011.siemens.net (194.138.21.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:12:41 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:12:40 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 5/7] suricatta/lua: Add Lua interface specification Date: Thu, 2 Jun 2022 11:11:45 +0200 Message-ID: <20220602091147.53323-5-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220602091147.53323-1-christian.storm@siemens.com> References: <20220602091147.53323-1-christian.storm@siemens.com> MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC89XA.ad011.siemens.net (139.25.226.103) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: fb0ca3e4-cfad-4d04-ddb8-08da44780c1d X-MS-TrafficTypeDiagnostic: DB6PR10MB1717:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: rhiLu339EsQOgMtkU6CbqBFdhd/5fslucvdLRTvTIrcz+s4p5YXh0EbvIJPuQw5enlxqchek1w5lfzjTnn6V64YGlXoAnnOtfkMhKMCTa2UiNq/XKNlltqG+CSwtMhPPK+6Ln9OrBEYj7eHmNt/yNbzySKJuClLRDtQDrXIyMaHLPa2a7XA+6Ai8KOm7r0XP5ipnd881vjTM111eqb9ySk1lbwsKXS+37PcKY8vKEof+CvCcvgBm96epv9O17gYg/9OSmMdPOzVczpvyGq1CqDW05fgptdpdmSvQ2QsP0XRTghBMsxz3U/aszP3xM7ZMTryUdk/UfgU4j/h7MeDvRpVuPJqA/+QyAaeJE7kkyunTvlzuIN0aQ//m8BuVfqeL+hEtXoABrFTfxqqW2AbgssRJRW3+rYD+0t5CTt8IX193tP5IfhOFHCc8tek2mCaK7lG4q/UCLdLB3OQJpI6GynaQccTi+T2GPNkBGtCSkUAyVtC2fxGLf5Oo0+GVNtVxYtNnEWH9lihqu428PqX8om11WXzlzT7mCNuCer2nYSDdB0Ok+Plm7zND90+M9b7Sal1xR07AjzBnQUs8xTI0Ve2SCuM47UCfVFjbAJ1QuuKqh12t8N6EWQrMXS26JRBTjDLbnD14ZS6FOv2OJDLvfUXKX2VPRMCCviFL4mDuuMzY78k/4fvTR0UKH8hC9tiPegXI5JDXyk33v8BqYvbITQ== X-Forefront-Antispam-Report: CIP:194.138.21.72;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230001)(4636009)(36840700001)(46966006)(40470700004)(82310400005)(1076003)(186003)(107886003)(8936002)(2906002)(8676002)(6916009)(316002)(36756003)(4326008)(70206006)(16526019)(336012)(70586007)(83380400001)(47076005)(86362001)(956004)(508600001)(5660300002)(44832011)(30864003)(2616005)(81166007)(36860700001)(82960400001)(356005)(26005)(40460700003)(36900700001);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:12:41.5925 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: fb0ca3e4-cfad-4d04-ddb8-08da44780c1d X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.72];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR01FT104.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR10MB1717 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=u1aQR98e; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:fe07::62f as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , The interface specification details what functionality is available to suricatta Lua modules via SWUpdate's exported suricatta Lua module. It serves as reference, for mocking purposes, and type checking thanks to the EmmyLua annotations. Signed-off-by: Christian Storm --- suricatta/suricatta.lua | 328 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 suricatta/suricatta.lua diff --git a/suricatta/suricatta.lua b/suricatta/suricatta.lua new file mode 100644 index 0000000..d15ab47 --- /dev/null +++ b/suricatta/suricatta.lua @@ -0,0 +1,328 @@ +--[[ + + SWUpdate Suricatta Lua Module. + + Interface specification for the Lua module provided by the + "Suricatta Lua module" suricatta "server" (suricatta/server_lua.c). + + Author: Christian Storm + Copyright (C) 2022, Siemens AG + + SPDX-License-Identifier: GPL-2.0-or-later + +--]] + +---@diagnostic disable: unused-local +-- luacheck: no max line length +-- luacheck: no unused args + +local suricatta = {} + + +--- Lua equivalent of `server_op_res_t` enum as in `include/util.h`. +-- +--- @class suricatta.status +--- @type table +suricatta.status = { + OK = 0, + EERR = 1, + EBADMSG = 2, + EINIT = 3, + EACCES = 4, + EAGAIN = 5, + UPDATE_AVAILABLE = 6, + NO_UPDATE_AVAILABLE = 7, + UPDATE_CANCELED = 8, + ID_REQUESTED = 9, +} + + +--- SWUpdate notify function bindings. +-- +-- Translates to `notify(string.format(message, ...))`, see +-- `corelib/lua_interface.c`. +-- +--- @type table +suricatta.notify = { + --- @type fun(message: string, ...: any) + error = function(message, ...) end, + --- @type fun(message: string, ...: any) + trace = function(message, ...) end, + --- @type fun(message: string, ...: any) + debug = function(message, ...) end, + --- @type fun(message: string, ...: any) + info = function(message, ...) end, + --- @type fun(message: string, ...: any) + warn = function(message, ...) end, + --- @type fun(message: string, ...: any) + progress = function(message, ...) end, +} + + +--- SWUpdate's persistent state IDs as in `include/state.h` and reverse-lookup. +-- +--- @class suricatta.pstate +suricatta.pstate = { + OK = string.byte('0'), [string.byte('0')] = "OK", + INSTALLED = string.byte('1'), [string.byte('1')] = "INSTALLED", + TESTING = string.byte('2'), [string.byte('2')] = "TESTING", + FAILED = string.byte('3'), [string.byte('3')] = "FAILED", + NOT_AVAILABLE = string.byte('4'), [string.byte('4')] = "NOT_AVAILABLE", + ERROR = string.byte('5'), [string.byte('5')] = "ERROR", + WAIT = string.byte('6'), [string.byte('6')] = "WAIT", + IN_PROGRESS = string.byte('7'), [string.byte('7')] = "IN_PROGRESS", + + --- Get the current stored persistent state. + -- + --- @return boolean # Whether operation was successful or not + --- @return suricatta.pstate # Persistent state ID number + get = function() end, + + --- Save persistent state information. + -- + --- @param state suricatta.pstate Persistent state ID number + --- @return boolean # Whether operation was successful or not + save = function(state) end, +} + + +--- Function registry IDs for Lua suricatta functions. +-- +--- @class suricatta.server +suricatta.server = { + HAS_PENDING_ACTION = 0, + INSTALL_UPDATE = 1, + SEND_TARGET_DATA = 2, + GET_POLLING_INTERVAL = 3, + SERVER_START = 4, + SERVER_STOP = 5, + IPC = 6, + PRINT_HELP = 7, + CALLBACK_PROGRESS = 8, + CALLBACK_CHECK_CANCEL = 9, + + --- Register a Lua function as Suricatta interface implementation. + -- + --- @param function_p function Function to register for `purpose` + --- @param purpose suricatta.server Suricatta interface function implemented + --- @return boolean # Whether operation was successful or not + register = function(function_p, purpose) end, +} + + +suricatta.channel = { + --- Content type passed over the channel as in `include/channel_curl.h`. + -- + --- @class suricatta.channel.content + --- @type table + content = { + NONE = 0, + JSON = 1, + RAW = 2, + }, + + --- Transfer method to use over channel as in `include/channel_curl.h`. + -- + --- @class suricatta.channel.method + --- @type table + method = { + GET = 0, + POST = 1, + PUT = 2, + PATCH = 3, + }, + + --- Channel options as in `include/channel_curl.h`. + -- + --- @class suricatta.channel.options + --- @field url string `CURLOPT_URL` - URL for this transfer + --- @field cached_file string Resume download from cached file at path + --- @field auth string `CURLOPT_USERPWD` - user name and password to use in authentication + --- @field request_body string Data to send to server for `PUT` and `POST` + --- @field iface string `CURLOPT_INTERFACE` - source interface for outgoing traffic + --- @field dry_run boolean `swupdate_request`'s dry_run field as in `include/network_ipc.h` + --- @field cafile string `CURLOPT_CAINFO` - path to Certificate Authority (CA) bundle + --- @field sslkey string `CURLOPT_SSLKEY` - private key file for TLS and SSL client cert + --- @field sslcert string `CURLOPT_SSLCERT` - SSL client certificate + --- @field ciphers string `CURLOPT_SSL_CIPHER_LIST` - ciphers to use for TLS + --- @field proxy string `CURLOPT_PROXY` - proxy to use + --- @field info string `swupdate_request`'s info field as in `include/network_ipc.h` + --- @field auth_token string String appended to Header + --- @field content_type string `Content-Type:` and `Accept:` appended to Header + --- @field retry_sleep number Time to wait prior to retry and resume a download + --- @field method suricatta.channel.method Channel transfer method to use + --- @field retries number Maximal download attempt count + --- @field low_speed_timeout number `CURLOPT_LOW_SPEED_TIME` - low speed limit time period + --- @field connection_timeout number `CURLOPT_CONNECTTIMEOUT` - timeout for the connect phase + --- @field format suricatta.channel.content Content type passed over the channel + --- @field debug boolean Set channel debug logging + --- @field usessl boolean Enable SSL hash sum calculation + --- @field strictssl boolean `CURLOPT_SSL_VERIFYHOST` + `CURLOPT_SSL_VERIFYPEER` + --- @field nocheckanswer boolean Whether the reply is interpreted/logged and tried to be parsed + --- @field nofollow boolean `CURLOPT_FOLLOWLOCATION` - follow HTTP 3xx redirects + --- @field max_download_speed string `CURLOPT_MAX_RECV_SPEED_LARGE` - rate limit data download speed + --- @field headers_to_send table Header to send + options = { + url = nil, + cached_file = nil, + auth = nil, + request_body = nil, + iface = nil, + dry_run = nil, + cafile = nil, + sslkey = nil, + sslcert = nil, + ciphers = nil, + proxy = nil, + info = nil, + auth_token = nil, + content_type = nil, + retry_sleep = nil, + method = nil, + retries = nil, + low_speed_timeout = nil, + connection_timeout = nil, + format = nil, + debug = nil, + usessl = nil, + strictssl = nil, + nocheckanswer = nil, + nofollow = nil, + max_download_speed = nil, + headers_to_send = nil, + }, + + --- Open a new channel. + -- + --- @param options suricatta.channel.options Channel default options overridable per operation + --- @return boolean # Whether operation was successful or not + --- @return table # Options of and operations on the opened channel + open = function(options) + --- Returned channel instance, on successful open. + -- + --- @type table + --- @class channel + --- @field options suricatta.channel.options Channel creation-time set options + --- @field get function Channel get operation + --- @field put function Channel put operation + --- @field close function Channel close operation + return { + + --- Channel creation-time set options as in `include/channel_curl.h`. + -- + --- @type suricatta.channel.options + options = {}, + + --- Execute get operation over channel. + -- + --- @param options_get suricatta.channel.options Channel options for get operation + --- @return boolean # Whether operation was successful or not + --- @return suricatta.status # Suricatta return code + --- @return table # Operation results + get = function(options_get) + return { + --- @type number + http_response_code = nil, + --- @type suricatta.channel.content + format = nil, + --- @type table | nil + json_reply = nil, -- if request method was `suricatta.channel.content.JSON` + --- @type string | nil + raw_reply = nil, -- if request method was `suricatta.channel.content.RAW` + --- @type table | nil + received_headers = nil, + } + end, + + --- Execute put operation over channel. + -- + --- @param options_put suricatta.channel.options Channel options for put operation + --- @return boolean # Whether operation was successful or not + --- @return suricatta.status # Suricatta return code + --- @return table # Operation results + put = function(options_put) + return { + --- @type number + http_response_code = nil, + --- @type suricatta.channel.content + format = nil, + --- @type table | nil + json_reply = nil, -- if request method was `suricatta.channel.content.JSON` + --- @type string | nil + raw_reply = nil, -- if request method was `suricatta.channel.content.RAW` + --- @type table | nil + received_headers = nil, + } + end, + + --- Close channel. + close = function() end, + } + end, +} + + +--- @type table +--- @class op_channel +-- +-- Channel to use for the download / installation operation as returned by `suricatta.channel.open()` +-- plus channel options overriding the defaults per operation (@see suricatta.channel.options) +-- and specific options to the download / installation operation, e.g., `drain_messages`. +-- +--- @field channel channel Channel table as returned by `suricatta.channel.open()` +--- @field drain_messages? boolean Whether to flush all progress messages or only those while in-flight operation (default) +--- @field ∈? suricatta.channel.options Channel options to override for this operation + + +--- Install an update artifact from remote server or local file. +-- +-- If the protocol specified in Table `install_channel`'s `url` field is `file://`, +-- a local update artifact file is installed. If it is, e.g., `https://`, the +-- update artifact is downloaded *and* installed. +-- Note that this file is to be deleted, if applicable, from the Lua realm. +-- +--- @see suricatta.download +--- @param install_channel op_channel Channel to use for the download+installation operation +--- @return boolean # Whether operation was successful or not +--- @return suricatta.status # Suricatta return code +--- @return table # Error messages, if any +suricatta.install = function(install_channel) end + +--- Download an update artifact from remote server. +-- +-- `suricatta.download()` just downloads an update artifact from the remote server +-- without installing it. For later installation, call `suricatta.install()` with +-- an appropriate `install_channel` Table's `url` field. +-- +--- @see suricatta.install +--- @param download_channel op_channel Channel to use for the download operation +--- @param localpath string Path where to store the downloaded artifact to +--- @return boolean # Whether operation was successful or not +--- @return suricatta.status # Suricatta return code +--- @return table # Error messages, if any +suricatta.download = function(download_channel, localpath) end + + +--- Sleep for a number of seconds. +-- +-- Call SLEEP(3) via C realm. +-- +--- @param seconds number # Number of seconds to sleep +suricatta.sleep = function(seconds) end + + +--- Get TMPDIR from SWUpdate. +-- +-- @see `core/util.c` :: get_tmpdir() +-- +--- @return string # TMPDIR path +suricatta.get_tmpdir = function() end + + +--- Get SWUpdate version. +-- +--- @return table # Table with 'version' and 'patchlevel' fields +suricatta.getversion = function() end + + +return suricatta From patchwork Thu Jun 2 09:17:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638271 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=sTW3U9AR; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::139; helo=mail-lf1-x139.google.com; envelope-from=swupdate+bncbdd6bwv65qpbbeua4kkamgqev3fow3i@googlegroups.com; receiver=) Received: from mail-lf1-x139.google.com (mail-lf1-x139.google.com [IPv6:2a00:1450:4864:20::139]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDL4k3PlBz9s0w for ; Thu, 2 Jun 2022 19:17:10 +1000 (AEST) Received: by mail-lf1-x139.google.com with SMTP id c6-20020a196546000000b0047913eb1e9fsf191530lfj.8 for ; Thu, 02 Jun 2022 02:17:10 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161426; cv=pass; d=google.com; s=arc-20160816; b=cUJ+EzFG6cE2DoFGjTxxVMf1ROHMfyQ/4YweCwrDJExCySWz0ld5MOjbcp9savO7Rf sVYZz5tIKYX8KIMBOGdxqjB8qwJCxWBFYg3xlDJe5A55hgjwafawWQzqlD/XDWmnTBpp BYCDCRoEc/S90apZ3KTnh9S40G7aUo9BXfSKW3h3CihPOHnfi9VXDMeYud+eY/DR4FeB 1DC7rglN3w7t4W7rS1BnFCEs77KTyy9EilRrc+oedKeRa2z7ALUTlDzbgDcGOnTS+YmZ P7K3figNDjFmORKyOLFxNLPHJKeGk8EBAYcL6xmuEdz5QKy4pi+abLPlrtCthrQ6JxoJ bMbw== ARC-Message-Signature: i=3; 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:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:sender:dkim-signature; bh=ihS7S6WVwQl9+QEzWzp77Eos5T6oa/GvVmEF344MyPU=; b=Br8AtC7wB+8lgIMmHkyRN85+3kl4cwDcR0RU7y2fGGkYwGcLU+Uv9MWnjo2O1ym3fo wkU205DZO7NFiJOkKVJ7D3llQS0TAjdjaV4XyR5rwzojUJKoj8lxnhea8rlbN6O62HNg f7+ibuOR1EuAH2LfhJmy2lGPzt/27/p3qYoLLbuFtZS9Po8XpZPXW4euppvb0wga/kvT eujN48YyjfvAbxmdun2u8qN0qHp8f6iYl7bGDoAKS/7cUGDXmPJHFbQw1f2QVfvVfS0p yl465relEZwk1miPGIsMLBvbMZY9mIrMY3ACxZdrANzZnFT2OZ0XwE2wEZB9a9hs1SRD gN5Q== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b="HbB/6zho"; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::629 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=ihS7S6WVwQl9+QEzWzp77Eos5T6oa/GvVmEF344MyPU=; b=sTW3U9ARLulk8SQP1JaRq2k2G//TmWvtm+HnO0Xbr9+5O1RMeMOvSnKEaUdfGVSHfn w/Fiuoxx8gF1aZBaUo0uuv2uFTKxTx1UBI583Fhz0wK7QQWf9GpAIbFz3S0GlLS8MHn2 /YkT7b23BACyUO5qxY54zJHlL+4dkkmuIetJXCzGSclQmjzOtVq9Q6I/lWOSHFDq73wp eYcGGxcInNsxTBnFMOv7s+wBdGX+pn1iZ92q1vDw6PIpcPWy6s8AAf6ToFWPzdPJ3nf6 xfXRVnQqB1qI3e3GP//+YkOxz9X8zsPS/D/P2PAP5SSsGkM7MNNaU9pLFRVfSN1/CR5G pkyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding :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=ihS7S6WVwQl9+QEzWzp77Eos5T6oa/GvVmEF344MyPU=; b=tlECyrA6e6GLMvsXnv3AXXwqeqlgOMLltXyoc6ABmvdj5VNPxdfKCOk5zyr9H2ts0u IiMSMZNL5GmKGw8sO2NhJksd3wAa8dKXI4RpyJV7hLqsml+STprVo0AStM8s0ilRXTQO QZwwLwJS6j1JITsm+6b64r5XiVL6aS5XwsVpPnYAIgRV/+XaOEHyMjRUkzD0NPV3Z9yW dW/ObScBlhqhTCE2sCEtsV7YVEkF6TZkfKGq3q5zwQQgP6Gyvfhx45vZE7KgmP1orDlJ Vn1cncRBNuejRbk45jr1RlN0zZ4XCS7MQg2ZKnVBbDMTmSm/wzuA1lqOQ7eICmTfhhGd 1uiw== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM531ulxO5nQbpvazByLdUraHdGRlKupGljX0zIkA2N5JgPoxR7dZe cUKtC3dlrTb12argv16zR0M= X-Google-Smtp-Source: ABdhPJzdj+E4lOALv6U0CoZP7XumLofIzvo1PNmfkiAa785E+6i7p4E6UFL34DT4upKBcX9jYFkxrQ== X-Received: by 2002:a2e:8805:0:b0:255:6e73:9a67 with SMTP id x5-20020a2e8805000000b002556e739a67mr3673972ljh.426.1654161426226; Thu, 02 Jun 2022 02:17:06 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:6512:1693:b0:448:3742:2320 with SMTP id bu19-20020a056512169300b0044837422320ls402295lfb.1.gmail; Thu, 02 Jun 2022 02:17:05 -0700 (PDT) X-Received: by 2002:a05:6512:2353:b0:478:734e:753f with SMTP id p19-20020a056512235300b00478734e753fmr2828686lfu.214.1654161425071; Thu, 02 Jun 2022 02:17:05 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161425; cv=pass; d=google.com; s=arc-20160816; b=e0tjgk5oAG/dtSz+a9XTbZJ7x5e3mMhGRGmmwJfzy4ed15ChH83ZZocYOlXfmSVlGN puajrsGLa7aHHu4qhFxOMF51HVjpxdDUq4KLkuv0SezNays+MaVpbcKj+o23DkEWouR3 govv1Yrhj4XJUBMi7S5Qzv5dPLXfnMbhd2SR0Oy4zKMmxn6+LhoNzmF0bFtEXPKf0W1Z ezjD91gLAYtYxnD9gF7i+IqA+nKFyaPXIpu/gbfh/9vImM6pg3e8JWkEf0W6cE0YbZG7 As/JxzxMmJOYYBX+wUcq1Ef05X6Y2OW+vywWuzbM6qZFHbCK/8+o+0wbGEeaDyhsGG1U bGRg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=w9wJ27ALzoaVAVb1NKdEtnv63ALXYSSSleA/38eCC4o=; b=SodzslhCIHawtavxTqeRVjJPOhF3ZB0oZpy68cWimHGP5hsgkFYKpZo2x8kQGqU38a 6Pbqu8zBZXzhpdx1GzK72hYaj4QVc3gOaWWb33kVtgY2d8NEPuVXPMgswSfSolfiUtzc jfZhbh7efYxzgN/Ieo83H5vUx+fqPfygIR0uZXgvRrSMwkAuSLG+3qACfASunRwRc4JW Kfx/Lk9XL7tQsrrefoGtNxLR9S3fwRGLp2nWE70jpakVkWYE96AZeF07U9Xe8NUjCIFL +sWfrIiQyYB1Yu/ZNWevgkqnQERagSxqHiTJDOxA9zvGaGHIOdPCspFi4BwJw+py2CZm AP4A== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b="HbB/6zho"; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::629 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on20629.outbound.protection.outlook.com. [2a01:111:f400:7d00::629]) by gmr-mx.google.com with ESMTPS id k11-20020a05651210cb00b004786d36663asi228848lfg.9.2022.06.02.02.17.04 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:17:04 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::629 as permitted sender) client-ip=2a01:111:f400:7d00::629; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Fdfw3uakYy+9c6bnCIdSP+2VDNsQ097C1dCc3CLr5IKngkkHSYIYG6UVxEKUnBRsgqSINvHotS2rCuRnamwTjyIezPSTytc3ebJNb9fGoEKGmMLS5a7iFLW+coLNEOGE17/g9Sts7qEP1PTMtI8lETYQPPpgrRktGEf+2c2BEBXAql4ykUysU0zVWgJKhxB/QGriXVNvP4KXZtb4QymVxKQxVk1kUxw3o6kofGkr+cCfz5RctcS5+4gSiIk8vuE5OaQMm8EqVBf3uoPJUDzPlvfaMeTXJyaGYQuW7Wlp2H+SqDvynVJ43c6vvXlGm8IAG96wjCPwq9OTM0eSsnaztA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=w9wJ27ALzoaVAVb1NKdEtnv63ALXYSSSleA/38eCC4o=; b=eciTU4OI8HlOSsm0pamkYXF9ngrpTKgqRHxkug620xpwPO/KLBpzzV0cR9SVKrzkmRiPJzSbKo1P4qiFzb4+Wnh1wSmhYxtyDk4B9swLceGGIzyQM40UQcFvM0AhO93yjWDI8QzmGzaDXf2OhpxVgPnQUUreaD8tibmAHYfKhO6ig2+2ybWDhdn8aphe3IuiabCtkXS1mQ9+i6cnATCDrhj1IvAxna3ZKUHIkTDJMUKDLMJL208o21xfuge3AMtyNikNkL0LXNajGDdRbLyMDuhcbxMzMPIIhIt90XfMryrJ0GlrOpMoZfLs4mr9GzSCtAK+d3ieZjWCLE6sLleZZQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.73) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from AS9PR04CA0169.eurprd04.prod.outlook.com (2603:10a6:20b:530::24) by AM0PR10MB2289.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:208:dd::32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.12; Thu, 2 Jun 2022 09:17:03 +0000 Received: from VE1EUR01FT045.eop-EUR01.prod.protection.outlook.com (2603:10a6:20b:530:cafe::cd) by AS9PR04CA0169.outlook.office365.com (2603:10a6:20b:530::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:17:03 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.73) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.73 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.73; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.73) by VE1EUR01FT045.mail.protection.outlook.com (10.152.3.125) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:17:02 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SNA.ad011.siemens.net (194.138.21.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:17:02 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:17:01 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 6/7] suricatta/lua: General HTTP Server example suricatta Lua module Date: Thu, 2 Jun 2022 11:17:07 +0200 Message-ID: <20220602091707.53883-1-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220602091147.53323-1-christian.storm@siemens.com> References: <20220602091147.53323-1-christian.storm@siemens.com> MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 8a1164e4-fc73-4d4b-5c79-08da4478a7df X-MS-TrafficTypeDiagnostic: AM0PR10MB2289:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5alXYW8o0sP2d2/CJ55BRjJo+L4NJxqjfSeNt0mYuCesTllxfxumXosJ6dNhTYGFSvtbaprKPVOzyQQv8uVTpd3CKAhIP60rCzmVJlpwS6lvCuczMceOKRBi7AudMlUu8rI58LdKfbNtuKYrQwGlgcBvAwalYLw9FcakktNiL9aJKy18sYE2wqwbc3Duah6BPc0+69SdYJqbNcWo7SwIjg+RUOvAaz3VAkA7bx+kqu1UxqNyKVqVEyZhjEPaRmh9wY+UL+3IcH4b/FvLionqlTcNz66oGGvE96xOWPmgdJW4do5lSab0Itvu/B0bjItOcmHv4aTaeNN+Ah2FHszIw9X4nRqmkku/dMXINwDkNwtPHtqY3VV2VAyYWyzjvKxycopx2LavxaMJnIBSyJEJd87zUpQJjR8ljNqDDQoGwjtUosD4Hh+DIheh/C1GqQbZLLq+h6b0udEU1oZIHAZcyl98LqZbSskYO8aFjGEAkoVTBx4ZQXgDoevukbZOaZ1RQwYKA0xfFHc70q6y8XDUyv0XxyKHHCh9VW7jd96cdiJ8QwI6Kp3Z7ByCFvHx2HDPVGI8RD4HQmpITuqJtv3UQSZDps0Kj7RbNHqFO47Kno2hdlloDA8BTbQnON0ut3oXdns44oCxSTthbnAv2VL7XZr5rWJ4AtgbYQEsFHykPGNxM0nbyUnda/CsBKtZgohCBgxTC44B6mle/hvbxmuZagzD/8ym8alXbbDEfPr0D66KFWxA+31Cjp59MqcBvVG/OL18dPFN/J8rgFv5O3bHZTj1EDC7OM12P3P/2o7PmBg= X-Forefront-Antispam-Report: CIP:194.138.21.73;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230001)(4636009)(36840700001)(40470700004)(46966006)(8676002)(86362001)(26005)(5660300002)(6916009)(356005)(4326008)(81166007)(966005)(82960400001)(508600001)(2906002)(83380400001)(316002)(16526019)(186003)(40460700003)(336012)(107886003)(956004)(2616005)(1076003)(36860700001)(82310400005)(47076005)(30864003)(44832011)(8936002)(70206006)(70586007)(36756003)(36900700001);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:17:02.8919 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 8a1164e4-fc73-4d4b-5c79-08da4478a7df X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.73];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR01FT045.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR10MB2289 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b="HbB/6zho"; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7d00::629 as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , The example suricatta Lua module implementing support for a server mock implementation modeled after the "General Purpose HTTP Server" showcases how a suricatta Lua module could look like. It is documented extensively supporting and extending the suricatta Lua module documentation. Signed-off-by: Christian Storm --- examples/suricatta/server_general.py | 190 +++++++ examples/suricatta/swupdate_suricatta.lua | 613 ++++++++++++++++++++++ 2 files changed, 803 insertions(+) create mode 100755 examples/suricatta/server_general.py create mode 100644 examples/suricatta/swupdate_suricatta.lua diff --git a/examples/suricatta/server_general.py b/examples/suricatta/server_general.py new file mode 100755 index 0000000..17c0455 --- /dev/null +++ b/examples/suricatta/server_general.py @@ -0,0 +1,190 @@ +#!/usr/bin/python -B +# +# Author: Christian Storm +# Copyright (C) 2022, Siemens AG +# +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +Suricatta General HTTP Server Mock + +Server mock implementation modeled after the 'General Purpose HTTP Server'. +""" + +import os +import sys +import argparse +import hashlib +import pathlib +import logging + +try: + import bottle +except ModuleNotFoundError: + server_dir = os.path.dirname(os.path.abspath(__file__)) + sys.exit(f"Install bottle, e.g., 'cd {server_dir}; wget http://bottlepy.org/bottle.py'") + +BOTTLE_SCHEME = "http" +RETRY_BUSY = 10 + + +class server(object): + firmware_dir = None + url = None + debug = False + + +def log(message, loglevel=logging.INFO): + logcolor = { + logging.ERROR: 31, + logging.WARN: 33, + logging.INFO: 32, + logging.DEBUG: 30, + } + loglevel = logging.ERROR if str(message).startswith(("400", "500")) else loglevel + color = logcolor.get(loglevel, 1) + print(f"\033[{color}m{message}\033[0m") + return message + + +def logresult(func): + def decorator(*args, **kwargs): + return log(func(*args, **kwargs)) + + return decorator + + +def extract_device_name(query): + device_name = bottle.request.headers.get("name") + if device_name is None and len(query) > 0: + device_name = "".join(ch for ch in "".join(map("".join, sorted(query.items()))) if ch.isalnum()) + return device_name + + +@bottle.error(500) +@logresult +def error500(error): + bottle.response.set_header("Content-Type", "text/plain; charset=utf-8") + bottle.response.status = "500 Oops." + return bottle.response.status + + +@bottle.error(404) +@logresult +def error404(error): + bottle.response.set_header("Content-Type", "text/plain; charset=utf-8") + bottle.response.status = "404 Not found." + return bottle.response.status + + +@bottle.route("/log", method="PUT") +@logresult +def route_log(): + bottle.response.set_header("Content-Type", "text/plain; charset=utf-8") + bottle.response.status = "200 Log received." + log(">>> Log received: {}".format((bottle.request.body.read()).decode("UTF-8"))) + return bottle.response.status + + +@bottle.route("/", method="GET") +@logresult +def route_main(): + if server.debug: + headers = ["{}: {}".format(h, bottle.request.headers.get(h)) for h in bottle.request.headers.keys()] + log( + ">>> {} {}\n{}".format(bottle.request.method, bottle.request.url, "\n".join(headers)), + logging.DEBUG, + ) + + bottle.response.set_header("Content-Type", "text/plain; charset=utf-8") + + device_name = extract_device_name(bottle.request.query) + if device_name is None: + bottle.response.status = "400 HTTP Header 'name' or 'identify' cfg section error." + return bottle.response.status + + firmware = os.path.join(server.firmware_dir, device_name) + firmware_relative_path = pathlib.Path(firmware).relative_to(os.getcwd()) + if not os.path.isfile(firmware) or not os.access(firmware, os.R_OK): + log(f">>> Place firmware file at '{firmware_relative_path}' to update device {device_name}.") + # Send back the client name ID served just for the client's information. + bottle.response.set_header("Served-Client", device_name) + bottle.response.status = f"404 No update is available for {device_name}" + return bottle.response.status + log(f">>> Firmware file found at {firmware_relative_path}.") + + try: + with open(firmware, "rb") as file: + bottle.response.set_header("Content-Md5", hashlib.md5(file.read()).hexdigest()) + except FileNotFoundError: + bottle.response.status = f"500 Firmware file not found: {firmware_relative_path}" + return bottle.response.status + + bottle.response.status = "302 Update available." + bottle.response.set_header("Location", f"{server.url}/firmware/{device_name}") + return bottle.response.status + + +@bottle.route("/firmware/") +def route_download(filepath): + if bottle.request.method == "GET": + log(f">>> Serving firmware file: {filepath}") + return bottle.static_file(filepath, root=server.firmware_dir) + + +def runserver(): + parser = argparse.ArgumentParser( + add_help=True, + description="""SWUpdate Suricatta 'General HTTP Server' Mock.""", + epilog="""""", + ) + parser.add_argument( + "-x", + metavar="BOTTLE_HOST", + dest="BOTTLE_HOST", + help="host to bind to (default: %(default)s)", + default="localhost", + ) + parser.add_argument( + "-p", + metavar="BOTTLE_PORT", + dest="BOTTLE_PORT", + help="port to bind to (default: %(default)s)", + default="8080", + ) + parser.add_argument( + "-d", + dest="debug", + help="Enable debug logging", + default=False, + action="store_true", + ) + parser.add_argument( + "-f", + metavar="FIRMWARE_DIR", + dest="FIRMWARE_DIR", + help="path to firmware files" " directory (default: %(default)s)", + default=os.path.join(os.getcwd(), "firmwares"), + ) + args = parser.parse_args() + + global server + server.debug = args.debug + server.url = f"{BOTTLE_SCHEME}://{args.BOTTLE_HOST}:{args.BOTTLE_PORT}" + server.firmware_dir = args.FIRMWARE_DIR + + if not pathlib.Path(server.firmware_dir).is_dir(): + sys.exit(f"Path {server.firmware_dir} is not an existing directory.") + + log(f"Debug logging: {server.debug}.", logging.INFO) + log(f"Serving firmware files from {server.firmware_dir}") + bottle.run(host=args.BOTTLE_HOST, port=args.BOTTLE_PORT, debug=False) + + +if __name__ == "__main__": + try: + runserver() + except OSError as e: + if e.errno != 98: + raise + print("ERROR: Address already in use, server already running?") diff --git a/examples/suricatta/swupdate_suricatta.lua b/examples/suricatta/swupdate_suricatta.lua new file mode 100644 index 0000000..75a35a8 --- /dev/null +++ b/examples/suricatta/swupdate_suricatta.lua @@ -0,0 +1,613 @@ +--[[ + + SWUpdate Suricatta Example Lua Module for the 'General Purpose HTTP Server'. + + Author: Christian Storm + Copyright (C) 2022, Siemens AG + + SPDX-License-Identifier: GPL-2.0-or-later +--]] + +--luacheck: no max line length + +local suricatta = require("suricatta") + +--[[ >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ]] +--[[ Library Functions ]] +--[[ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ]] + +--- Simplistic getopt() +-- +-- Only short options (one dash, one character) are supported. +-- Unknown options are (silently) ignored. +-- If an option's required argument is missing, (':', option) is returned. +-- +--- @param argv string Integer-keyed arguments table +--- @param optstring string GETOPT(3)-like option string +--- @return function # Iterator, returning the next (option, optarg) pair +function getopt(argv, optstring) + if type(argv) ~= "table" or type(optstring) ~= "string" then + return function() end + end + for index, value in ipairs(argv) do + argv[value] = index + end + local f = string.gmatch(optstring, "%l:?") + return function() + while true do + local option = f() + if not option then + return + end + local optchar = option:sub(1, 1) + local param = "-" .. optchar + if argv[param] then + if option:sub(2, 2) ~= ":" then + return optchar, nil + end + local value = argv[argv[param] + 1] + if not value then + return ":", optchar + end + if value:sub(1, 1) == "-" then + return ":", optchar + end + return optchar, value + end + end + end +end + + +--- Merge two tables' data into one. +-- +--- @param dest table Destination table, modified +--- @param source table Table to merge into dest, overruling `dest`'s data if existing +--- @return table # Merged table +function table.merge(dest, source) + local function istable(t) + return type(t) == "table" + end + for k, v in pairs(source) do + if istable(v) and istable(dest[k]) then + table.merge(dest[k], v) + else + dest[k] = v + end + end + return dest +end + + +--- Escape and trim a String. +-- +-- The default substitutions table is suitable for escaping +-- to proper JSON. +-- +--- @param str string The JSON string to be escaped +--- @param substs? table Substitutions to apply +--- @return string # The escaped JSON string +function escape(str, substs) + local escapes = '[%z\1-\31"\\]' + if not substs then + substs = { + ['"'] = '"', + ["\\"] = "\\\\", + ["\b"] = "", + ["\f"] = "", + ["\n"] = "", + ["\r"] = "", + ["\t"] = "", + } + end + substs.__index = function(_, c) + return string.format("\\u00%02X", string.byte(c)) + end + setmetatable(substs, substs) + return ((string.gsub(str, escapes, substs)):match("^%s*(.-)%s*$"):gsub("%s+", " ")) +end + + +--[[ >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ]] +--[[ Suricatta General Purpose HTTP Server Module ]] +--[[ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ]] + +--- Device state and information. +-- +--- @class device +--- @field pstate suricatta.pstate Persistent state ID number +--- @field id string Device ID +device = { + pstate = nil, + id = nil +} + + +--- Job type "enum". +-- +--- @class job.type +--- @type table +jobtype = { + INSTALL = 1, + DOWNLOAD = 2, + BOTH = 3, +} + + +--- A Job. +-- +--- @class job +--- @field md5 string MD5 sum of the artifact +--- @field url string URL of the artifact +--- @field typ job.type Type of job + + +--- Configuration for the General Purpose HTTP Server. +-- +--- @class gs +--- @field channel table Channels used in this module +--- @field channel_config suricatta.channel.options Channel options (defaults, shadowed by config file, shadowed by command line arguments) +--- @field job job Current job information +--- @field polldelay table Default and temporary delay between two server poll operations in seconds +gs = { + channel = {}, + channel_config = {}, + job = {}, + polldelay = { default = 0, current = 0 }, +} + + +--- Query and handle pending actions on server. +-- +-- Lua counterpart of `server_op_res_t server_has_pending_action(int *action_id)`. +-- +-- The suricatta.status return values UPDATE_AVAILABLE and ID_REQUESTED +-- are handled in `suricatta/suricatta.c`, the others result in SWUpdate +-- sleeping again. +-- +--- @param action_id number Current Action ID [unused] +--- @return number # Action ID [optional] +--- @return suricatta.status # Suricatta return code +function has_pending_action(action_id) + action_id = action_id + gs.polldelay.current = gs.polldelay.default + local _, pstate = suricatta.pstate.get() + if pstate == suricatta.pstate.INSTALLED then + suricatta.notify.warn("An installed update is pending testing, not querying server.") + return suricatta.status.NO_UPDATE_AVAILABLE + end + + suricatta.notify.trace("Querying %q", gs.channel_config.url) + local _, _, data = gs.channel.default.get({ + url = gs.channel_config.url, + format = suricatta.channel.content.NONE, + nocheckanswer = true, -- Don't print 404 ERROR() message, see comment below. + headers_to_send = { ["name"] = device.id }, + }) + + if data.http_response_code == 404 then + -- Note: Returning 404 on no update available is a bit unfortunate + -- printing an error message if `nocheckanswer` isn't true. + -- Maybe '204 No Content' as not repurposing a general error condition + -- would've been a better choice. Then, 204 has to be introduced in + -- channel_map_http_code() as known though. + if math.random(3) == 1 then + -- Deliberately update the server from time to time with my health status. + suricatta.notify.trace( + "Server queries client data for: %s", + data.received_headers["Served-Client"] or "" + ) + return suricatta.status.ID_REQUESTED + end + suricatta.notify.trace("Server served request for: %s", data.received_headers["Served-Client"] or "") + return suricatta.status.NO_UPDATE_AVAILABLE + end + + if data.http_response_code == 503 then + -- Server is busy serving updates to another client. + -- Try again after seconds announced in HTTP header or default value. + gs.polldelay.current = tonumber(data.received_headers["Retry-After"]) or gs.polldelay.default + suricatta.notify.debug("Server busy, waiting for %ds.", gs.polldelay.current) + return suricatta.status.NO_UPDATE_AVAILABLE + end + + if data.http_response_code == 302 then + -- Returning 302 is, like 404 above, also a bit unfortunate as it + -- requires telling curl not to follow the redirection but instead + -- treat this as the artifact's URL (see channel initialization + -- in server_start()). + suricatta.notify.info("Update available, update job enqueued.") + gs.job.md5 = data.received_headers["Content-Md5"] + gs.job.url = data.received_headers["Location"] + return suricatta.status.UPDATE_AVAILABLE + end + + suricatta.notify.trace("Unhandled HTTP status code %d.", data.http_response_code) + return suricatta.status.NO_UPDATE_AVAILABLE +end +suricatta.server.register(has_pending_action, suricatta.server.HAS_PENDING_ACTION) + + +--- Callback to check for update cancellation on server while downloading. +-- +-- Some servers, e.g., hawkBit, support (remote) update cancellation while +-- the download phase. This (optional) callback function is registered +-- as `channel_data_t`'s `dwlwrdata` function for this purpose. +-- +-- Note: The CALLBACK_PROGRESS and CALLBACK_CHECK_CANCEL callback functions +-- are both executed under mutual exclusion in suricatta's Lua state which +-- is suspended in the call to suricatta.{install,download}(). While this is +-- safe, both callback functions should take care not to starve each other. +-- +-- WARNING: This function is called as part of the CURLOPT_WRITEFUNCTION +-- callback as soon as there is data received, i.e., usually *MANY* times. +-- Since this function and CALLBACK_PROGRESS are contending for the Lua +-- state lock (and by extension for some others), register and use this +-- function only if really necessary. +-- +--- @return suricatta.status # OK to continue downloading, UPDATE_CANCELED to cancel +function check_cancel_callback() + return suricatta.status.OK +end +-- suricatta.server.register(check_cancel_callback, suricatta.server.CALLBACK_CHECK_CANCEL) + + +--- Lua equivalent of `sourcetype` as in `include/swupdate_status.h`. +-- +--- @type table +--- @class sourcetype +--- @field SOURCE_UNKNOWN number 0 +--- @field SOURCE_WEBSERVER number 1 +--- @field SOURCE_SURICATTA number 2 +--- @field SOURCE_DOWNLOADER number 3 +--- @field SOURCE_LOCAL number 4 +--- @field SOURCE_CHUNKS_DOWNLOADER number 5 + + +--- Lua equivalent of `RECOVERY_STATUS` as in `include/swupdate_status.h`. +-- +--- @type table +--- @class RECOVERY_STATUS +--- @field IDLE number 0 +--- @field START number 1 +--- @field RUN number 2 +--- @field SUCCESS number 3 +--- @field FAILURE number 4 +--- @field DOWNLOAD number 5 +--- @field DONE number 6 +--- @field SUBPROCESS number 7 +--- @field PROGRESS number 8 + + +--- Lua equivalent of `progress_msg` as in `include/progress_ipc.h`. +-- +--- @class progress_msg +--- @field magic number SWUpdate IPC magic number +--- @field status RECOVERY_STATUS Update status +--- @field dwl_percent number Percent of downloaded data +--- @field nsteps number Total steps count +--- @field cur_step number Current step +--- @field cur_percent number Percent in current step +--- @field cur_image string Name of the current image to be installed +--- @field hnd_name string Name of the running handler +--- @field source sourcetype The source that has triggered the update +--- @field info string Additional information about the installation +--- @field jsoninfo table If `info` is JSON, according Lua Table + + +--- Progress thread callback handling progress reporting to remote. +-- +-- Deliberately just uploading the JSON content while not respecting +-- the log configuration of the real `server_general.c` implementation. +-- This callback function is optional. +-- +-- Note: The CALLBACK_PROGRESS and CALLBACK_CHECK_CANCEL callback functions +-- are both executed under mutual exclusion in suricatta's Lua state which +-- is suspended in the call to suricatta.{install,download}(). While this is +-- safe, both callback functions should take care not to starve each other. +-- +--- @param message progress_msg The progress message +--- @return suricatta.status # Suricatta return code +function progress_callback(message) + if not gs.channel.progress then + return suricatta.status.OK + end + local logmessage + if message.dwl_percent > 0 and message.dwl_percent <= 100 and message.cur_step == 0 then + -- Rate limit progress messages sent to server. + if message.dwl_percent % 5 ~= 0 then + return suricatta.status.OK + end + if gs.job.typ == jobtype.INSTALL then + logmessage = escape( + string.format([[{"message": "File Processing...", "percent": %d}]], message.dwl_percent or 0) + ) + else + logmessage = escape( + string.format([[{"message": "Downloading...", "percent": %d}]], message.dwl_percent or 0) + ) + end + elseif message.dwl_percent == 100 and message.cur_step > 0 then + -- Rate limit progress messages sent to server. + if message.cur_percent % 5 ~= 0 then + return suricatta.status.OK + end + logmessage = escape( + string.format( + [[{"message": "Installing artifact %d/%d: '%s' with '%s'...", "percent": %d}]], + message.cur_step, + message.nsteps or 0, + message.cur_image or "", + message.hnd_name or "", + message.cur_percent or 0 + ) + ) + end + if logmessage ~= nil then + local res, _, data = gs.channel.progress.put({ + url = string.format("%s/%s", gs.channel_config.url, "log"), + content_type = "application/json", + method = suricatta.channel.method.PUT, + format = suricatta.channel.content.NONE, + request_body = logmessage, + }) + if not res then + suricatta.notify.error("HTTP Error %d while uploading log.", tonumber(data.http_response_code) or -1) + end + end + return suricatta.status.OK +end +suricatta.server.register(progress_callback, suricatta.server.CALLBACK_PROGRESS) + + +--- Install an update. +-- +-- Lua counterpart of `server_op_res_t server_install_update(void)`. +-- +--- @return suricatta.status # Suricatta return code +function install_update() + local res + + suricatta.notify.info("Installing artifact from %q", gs.job.url) + + -- Open progress reporting channel with default options. + res, gs.channel.progress = suricatta.channel.open({}) + if not res then + suricatta.notify.error("Cannot initialize progress reporting channel, will not send progress.") + gs.channel.progress = nil + end + + -- Chose an installation mode as I please... + -- Note: `drain_messages` is false, i.e., do not upload strictly all + -- progress messages to not artificially lengthen the installation + -- process due to progress message network I/O. Hence, only while + -- the update operation is in flight, progress messages are offloaded. + -- Once the operation has finished, the possibly remaining progress + -- messages are discarded. Instead of all progress messages, send a + -- final notification for the server's information. + res = suricatta.status.OK + local url = gs.job.url + local destfile + if math.random(2) == 2 then + suricatta.notify.info(">> Running in download + installation mode.") + else + suricatta.notify.info(">> Running in download and then local installation mode.") + -- Note: suricatta.get_tmpdir() returned path is '/' terminated. + destfile = ("%s%s"):format(suricatta.get_tmpdir(), "update.swu") + gs.job.typ = jobtype.DOWNLOAD + res, _, _ = suricatta.download({ channel = gs.channel.default, url = url, drain_messages = false }, destfile) + url = ("file://%s"):format(destfile) + gs.job.typ = jobtype.INSTALL + if not res then + suricatta.notify.error("Error downloading artifact!") + end + end + if res then + if not gs.job.typ then + gs.job.typ = jobtype.BOTH + end + res, _, _ = suricatta.install({ channel = gs.channel.default, url = url, drain_messages = false }) + if destfile then + os.remove(destfile) + end + end + + if gs.channel.progress then + local finres, _, findata = gs.channel.progress.put({ + url = string.format("%s/%s", gs.channel_config.url, "log"), + content_type = "application/json", + method = suricatta.channel.method.PUT, + format = suricatta.channel.content.NONE, + request_body = escape([[{"message": "Final Note: Installation done :)"}]]), + }) + if not finres then + suricatta.notify.error( + "HTTP Error %d while uploading final notification log.", + tonumber(findata.http_response_code) or -1 + ) + end + gs.channel.progress.close() + gs.channel.progress = nil + end + + gs.job = {} + + if not res then + suricatta.notify.error("Error installing artifact!") + return suricatta.status.EERR + end + + suricatta.notify.info("Update artifact installed successfully.") + return suricatta.status.OK +end +suricatta.server.register(install_update, suricatta.server.INSTALL_UPDATE) + + +--- Print the help text. +-- +-- Lua counterpart of `void server_print_help(void)`. +-- +--- @param defaults suricatta.channel.options Compile-time channel default options ∪ { polldelay = CHANNEL_DEFAULT_POLLING_INTERVAL } +--- @return suricatta.status # Suricatta return code +function print_help(defaults) + defaults = defaults or {} + io.stdout:write(string.format("\t -u * URL to the server instance, e.g., http://localhost:8080\n")) + io.stdout:write(string.format("\t -i * The device ID.\n")) + io.stdout:write(string.format("\t -p Polling delay (default: %ds).\n", defaults.polldelay)) + return suricatta.status.OK +end +suricatta.server.register(print_help, suricatta.server.PRINT_HELP) + + +--- Start the Suricatta server. +-- +-- Lua counterpart of `server_op_res_t server_start(char *fname, int argc, char *argv[])`. +-- +--- @param defaults table Lua suricatta module channel default options +--- @param argv table[] C's `argv` as Lua Table +--- @param fconfig table SWUpdate configuration file's [suricatta] section as Lua Table ∪ { polldelay = CHANNEL_DEFAULT_POLLING_INTERVAL } +--- @return suricatta.status # Suricatta return code +function server_start(defaults, argv, fconfig) + -- Use defaults, + -- ... shadowed by configuration file values, + -- ... shadowed by command line arguments. + local configuration = defaults or {} + table.merge(configuration, fconfig or {}) + device.id, configuration.id = configuration.id, nil + gs.polldelay.default, configuration.polldelay = configuration.polldelay, nil + gs.channel_config = configuration + for opt, arg in getopt(argv or {}, "u:i:p:") do + if opt == "u" then + gs.channel_config.url = tostring(arg) + elseif opt == "i" then + device.id = tostring(arg) + elseif opt == "p" then + gs.polldelay.default = tonumber(arg) + elseif opt == ":" then + io.stderr:write("Missing argument.") + print_help(defaults) + return suricatta.status.EINIT + end + end + gs.polldelay.current = gs.polldelay.default + + if not gs.channel_config.url or not device.id then + suricatta.notify.error("Mandatory configuration parameter missing.") + return suricatta.status.EINIT + end + + local res + res, gs.channel.default = suricatta.channel.open({ url = gs.channel_config.url, nofollow = true }) + if not res then + suricatta.notify.error("Cannot initialize channel.") + return suricatta.status.EINIT + end + + suricatta.notify.info("Running with device ID %s on %q.", device.id, gs.channel_config.url) + return suricatta.status.OK +end +suricatta.server.register(server_start, suricatta.server.SERVER_START) + + +--- Stop the Suricatta server. +-- +-- Lua counterpart of `server_op_res_t server_stop(void)`. +-- +--- @return suricatta.status # Suricatta return code +function server_stop() + gs.channel_config = {} + for channel, _ in pairs(gs.channel) do + gs.channel[channel].close() + gs.channel[channel] = {} + end + return suricatta.status.OK +end +suricatta.server.register(server_stop, suricatta.server.SERVER_STOP) + + +--- Query the polling interval from remote. +-- +-- Lua counterpart of `unsigned int server_get_polling_interval(void)`. +-- +--- @return number # Polling interval in seconds +function get_polling_interval() + -- Not implemented at server side, hence return device-local polling + -- interval that is configurable via IPC or the server-announced wait + -- time after having received a 503: busy while serving another client. + return gs.polldelay.current +end +suricatta.server.register(get_polling_interval, suricatta.server.GET_POLLING_INTERVAL) + + +--- Send device configuration/data to remote. +-- +-- Lua counterpart of `server_op_res_t server_send_target_data(void)`. +-- +--- @return suricatta.status # Suricatta return code +function send_target_data() + local res, _, data = gs.channel.default.put({ + url = string.format("%s/%s", gs.channel_config.url, "log"), + content_type = "application/json", + method = suricatta.channel.method.PUT, + format = suricatta.channel.content.NONE, + request_body = string.format([[{"message": "I'm %s and I'm fine."}]], tostring(device.id)), + }) + if not res then + suricatta.notify.error("HTTP Error %d while uploading target data.", tonumber(data.http_response_code) or -1) + end + return suricatta.status.OK +end +suricatta.server.register(send_target_data, suricatta.server.SEND_TARGET_DATA) + + +--- Lua-alike of `ipc_message` as in `include/network_ipc.h` +-- +-- Note: Some members are deliberately not passed through to the Lua realm +-- such as `ipc_message.data.len` as that's handled by the C-to-Lua bridge +-- transparently. +-- Also, this is not a direct equivalent as, e.g., the `json` field is not +-- present in `struct ipc_message`, but rather it's a "sensible" selection. +-- As another example, CMD_ENABLE is also not passed through and hence not +-- in `ipc_commands` as it's handled directly in `suricatta/suricatta.c`. +-- +--- @type table +--- @class ipc_commands +--- @field ACTIVATION number 0 +--- @field CONFIG number 1 +--- @field GET_STATUS number 3 +-- +--- @class ipc_message +--- @field magic number SWUpdate IPC magic number +--- @field commands ipc_commands IPC commands +--- @field cmd number Command number, one of `ipc_commands`'s values +--- @field msg string String data sent via IPC +--- @field json string If `msg` is JSON, JSON as Lua Table + + +--- Handle IPC messages sent to Suricatta Lua module. +-- +-- Lua counterpart of `server_op_res_t server_ipc(ipc_message *msg)`. +-- +--- @param message ipc_message The IPC message sent +--- @return string # IPC reply string +--- @return suricatta.status # Suricatta return code +function ipc(message) + if not (message or {}).json then + return escape([[{ "request": "IPC requests must be JSON formatted" }]], { ['"'] = '"' }), + suricatta.status.EBADMSG + end + message.msg = message.msg or "" + if message.cmd == message.commands.CONFIG then + suricatta.notify.debug("Got IPC configuration message: %s", message.msg) + if message.json.polling then + gs.polldelay.default = tonumber(message.json.polling) or gs.polldelay.default + return escape([[{ "request": "applied" }]], { ['"'] = '"' }), suricatta.status.OK + end + elseif message.cmd == message.commands.ACTIVATION then + suricatta.notify.debug("Got IPC activation message: %s", message.msg) + return escape([[{ "request": "inapplicable" }]], { ['"'] = '"' }), suricatta.status.OK + end + suricatta.notify.warn("Got unknown IPC message: %s", message.msg) + return escape([[{ "request": "unknown" }]], { ['"'] = '"' }), suricatta.status.EBADMSG +end +suricatta.server.register(ipc, suricatta.server.IPC) From patchwork Thu Jun 2 09:18:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Storm, Christian" X-Patchwork-Id: 1638272 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=TdMXvMlW; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::439; helo=mail-wr1-x439.google.com; envelope-from=swupdate+bncbdd6bwv65qpbbp4a4kkamgqeabwvkra@googlegroups.com; receiver=) Received: from mail-wr1-x439.google.com (mail-wr1-x439.google.com [IPv6:2a00:1450:4864:20::439]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LDL5b1Vv9z9s0w for ; Thu, 2 Jun 2022 19:17:55 +1000 (AEST) Received: by mail-wr1-x439.google.com with SMTP id c16-20020a5d4cd0000000b00213b1d4a96csf69986wrt.1 for ; Thu, 02 Jun 2022 02:17:54 -0700 (PDT) ARC-Seal: i=3; a=rsa-sha256; t=1654161472; cv=pass; d=google.com; s=arc-20160816; b=xdst3gV0DoFhPRYPricWwtGPm7pz63n/eQRvQteh+Z7ft0VIQ98WLr7grritzdz8Od xUXcKllQdqvUFXxZ1/1KPVzwn158hkp9Z0J7W3gKxw5Cd2wFQKUOieZ+71EML0RJNbLu EQnOzmupWERu8p1EFoQ/RRQ7XM/0sY8U4I9AEf1Vqp6i6UdfLyCz/JfNplvhYG1pIPRa zdd1ediqg90FjRLm5lEBFHOhX2OuD0P78O137ir3kdmmkaOyS44CulBhuOqEJNAgujNi sK4l9OACrfCuy8pcyWHcTGfDRn0Rgtu4bdj937QMg7evr+bfCA7dDL6hMyoMyxLJ/686 4E7g== ARC-Message-Signature: i=3; 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:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:dkim-signature; bh=aE79J/HUyhNFww9L5AsE0z+1ojoTH+6HNpXZLhevArs=; b=zmBldYPSCVRiomTa3BT7EzA4LY5ubfTPGt9/eQ6aSIlB27bQgATeKBwGjslHFNP9rN e9fXoL+dy+bhMOyqXAVusgjcyTwOTlw9wJJbb7cT304BkQ5XZcaK0ogS/GZqT9xrujsH qlTu1OUP/cGricpa8YwgHlSu+atT/c+auaj+h+5gmGbtLeXOSJrOzOQUHrfHmmBjwGa4 myLDEEF6rzo6PC5cvYVdy/gH32r0wKghzFzlOw7VMqa89J7mkzR9pEfmYd5Yn8hfI/wS PyF46iLW+a6J+c9Qn/LSW3epkf/SS4Wg8ZzfL795VEfFl7NEFsGh+dU73krLRVt8AvGx lUHw== ARC-Authentication-Results: i=3; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=SwdFNr5f; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7e1a::60b as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=aE79J/HUyhNFww9L5AsE0z+1ojoTH+6HNpXZLhevArs=; b=TdMXvMlW/Apt+ZwIulcJoPRpojfv2w0mJ2YvMLdmyhxLAsLxyQL5krABAxeN5bCW5R /H7Z78nE70AvCtXbYBljmc38w7+xC+IjhkGTCL/DG1o6rHftQ/sJ01YRGEXa0+or3mLt q8lgWM1bKJXxEvCI+Dv/CrlBD+3as/6S+J/b8CtPMiCCdYTZEFLPLQemnlwXCL+3IO0p g/pRPb+1rTHDt65Usvdg9w2SZ8WqGIhoPBx4YxA02G9y3jr3llUlEAGFth5haLdbG5rZ HK+0Hcm9J86lY5tttMiwiwAtKYkLKW7IKYtHwwRJOf52Oi8Wi8mwFgCuengwzoiVaENn oA0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version: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=aE79J/HUyhNFww9L5AsE0z+1ojoTH+6HNpXZLhevArs=; b=0l12bq1WHMhW8HGZPCEvhpx1tLaTCWc8mnMKUjFCz5KYe7Kqz2Q9eMmGgh7yfH28Q3 ZZy6c1e8cwOJt3glh6sKnP69Hp+BeOxCnEGpn08v9Z1nrV02sXJsCJEcPL6Y2+2Tu2lu mOrO6W1FqaOZ7FpAFdGtCclE9Otl8mzPEt9u4tyV5hhWHxYT81WzgFcaGf+Gvt1gj2Px L2psVx5dM3bEKhMe0SqFTiNvTYhlTExhdPX9upUfnHGVMtUEJGuNkX9ADETSt03WX9ip 9Xc6xjKa6Xo2XZQORdmLRNbsZLpHzeRke6JmBBe3eAVXG+ebJSsrEWgvEJ2z7uDL0rq0 3Uog== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM533vy+PJn4LuNABgfhZPaPN2kjEAqmr5rzLmBL348bgHZ4UHkmG+ F18sovUrSyKcjMF+PDKLJLI= X-Google-Smtp-Source: ABdhPJzMbIWHZTesqD256ExpUavWt+D3vvyxSPgpk9UlH4iMSeJQQIO78h8PW1NU1uXOtD+SQLLu/g== X-Received: by 2002:a5d:598f:0:b0:20c:83c9:b05b with SMTP id n15-20020a5d598f000000b0020c83c9b05bmr2836017wri.343.1654161471767; Thu, 02 Jun 2022 02:17:51 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:adf:f346:0:b0:20e:7267:9ee4 with SMTP id e6-20020adff346000000b0020e72679ee4ls8490912wrp.2.gmail; Thu, 02 Jun 2022 02:17:50 -0700 (PDT) X-Received: by 2002:a5d:66c4:0:b0:210:30c4:520e with SMTP id k4-20020a5d66c4000000b0021030c4520emr2909715wrw.558.1654161470725; Thu, 02 Jun 2022 02:17:50 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1654161470; cv=pass; d=google.com; s=arc-20160816; b=qWLEZQNYnm1hs5lcFOOoMFcHO0PdkfUTaTUOW3A6a+yo8eFs098RPrMBMVAVMolcoG sBSiRb5epzn+xcTmhMytWp4h/8X+7pkAVcw6eVelUIMQLX3oCJg2B3TL7UoW955/pgyz +y5tN9R8RidfLsRW99bvAp/vmITDOkZO0ClNdF3+tzB50lHIy7sBLH1jKlcF6kTGnxAb ZGX3RiXZD53XFVo0v8sBqaDQ8zfgaQwslb6bSFOmjH/lRyeV9mDYFec0uVgriTTjFcLx seXx9GvpHecCbmTc9K3OYLk+LjCDGhDoHBZ7/nZs0DxZn2q/4bx1jBH15LUwIUGlT4ou BCBw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=g4ROqTEC40zIJOVy9X6SLDA7lPhL6FdBE9aAA0gwkkc=; b=GXynI8ICMludnvb137hTIDtUckyWw8ZlJObW5WQ+QHXMTl7nNN3kT1mYsLy61gW0KU O3nuJLBNMjhJ/L9VAVqgWQ8sqx212hrX6O+xAQOzi6w7y490Ve5Z3iEVv02Sh5pNaKjm AiCb4/uVmEzVvWMN5MBfSE78rXoB4fImbFOEkyC8u0M1Asv+nAres/8b9E0/nUxgThcu 97PtM4SnbNht4dFxbd//cClGoJ3K+FCew318iZuM2NuAKo1kkoKLwUAUqaOjmooZo0wS nXl97oFCUGbXyySQrSvd4MWZtD0mtRviIR4O6zrYVatTBd2Ol4PidqjBuWsEHaD1G/i3 wjcQ== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=SwdFNr5f; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7e1a::60b as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com Received: from EUR05-DB8-obe.outbound.protection.outlook.com (mail-db8eur05on2060b.outbound.protection.outlook.com. [2a01:111:f400:7e1a::60b]) by gmr-mx.google.com with ESMTPS id u10-20020adfeb4a000000b002103e166ba7si212750wrn.6.2022.06.02.02.17.50 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Jun 2022 02:17:50 -0700 (PDT) Received-SPF: pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7e1a::60b as permitted sender) client-ip=2a01:111:f400:7e1a::60b; ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=iB3UfIruhNGeq25tl9Tn4ud2ZWLHRYlwkScbPCR/4584gvu1QMEAUkf7Br68NCvBV1L438Cna0tnYngJpj6mnv430CDv6K+YUAiRCYA2DQTJ8Hnw5Ljn+TSc4D28h4vEQIxnJ6YrN3XJZp38bVZFjVHpDPKOZt86NwsYLn5ozbrML86p8CAlO/4dItqrgjDKY13ko05WJ5QGRQcTmZhQpsrdFlvH06ZrlxSsPXcTgva2qqOQ90MYOQT8jqUb+Qz4XL2HLOVuD1uC1PdeAp58Qw3ymEcMuVtTOvLmE7bYLMj8lqUHHp3dNxX+nypqOIBeS4u7Gkho+v32nBN1tjQOoQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=g4ROqTEC40zIJOVy9X6SLDA7lPhL6FdBE9aAA0gwkkc=; b=GVfdgblV8EKeazxUw9SYaZp/4dPLvPuJdTUTYZT5IKuxZ0qHHYMti5m9Lk3UxT9BrMEnET+TF4LmTVcIzp8STPzYf+WnJ8RKThNkdWcXfr+K74wEvzTLgidUMtM7L+fZt1HvDVulVF+YiYPiMvxX1V84WaNnpU/tfdHzt0hK5aET9hWYO/zyP5sy6q49JJF6xFc9DFl63WF1qeoWI/tydxdrmRpHjhi1hY6G/j6buxnL6IOx462xAu8vCMf66vRXFcQL8U4w+C7UtYeNzU4zQ9Q42Ab3LawhZZktswf8+6NSz69Ou7tySXEu9PvgMlPJh3VLsPZ4eZ4FE5WrvNtNMQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 194.138.21.70) smtp.rcpttodomain=googlegroups.com smtp.mailfrom=siemens.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=siemens.com; dkim=none (message not signed); arc=none Received: from DB6P192CA0001.EURP192.PROD.OUTLOOK.COM (2603:10a6:4:b8::11) by VE1PR10MB3758.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:800:162::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13; Thu, 2 Jun 2022 09:17:49 +0000 Received: from DB5EUR01FT027.eop-EUR01.prod.protection.outlook.com (2603:10a6:4:b8:cafe::b6) by DB6P192CA0001.outlook.office365.com (2603:10a6:4:b8::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.12 via Frontend Transport; Thu, 2 Jun 2022 09:17:49 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 194.138.21.70) smtp.mailfrom=siemens.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=siemens.com; Received-SPF: Pass (protection.outlook.com: domain of siemens.com designates 194.138.21.70 as permitted sender) receiver=protection.outlook.com; client-ip=194.138.21.70; helo=hybrid.siemens.com; pr=C Received: from hybrid.siemens.com (194.138.21.70) by DB5EUR01FT027.mail.protection.outlook.com (10.152.5.1) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5314.13 via Frontend Transport; Thu, 2 Jun 2022 09:17:48 +0000 Received: from DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) by DEMCHDC9SJA.ad011.siemens.net (194.138.21.70) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.9; Thu, 2 Jun 2022 11:17:34 +0200 Received: from cosmos.fritz.box.net (139.22.41.90) by DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2375.24; Thu, 2 Jun 2022 11:17:34 +0200 From: Christian Storm To: CC: Christian Storm Subject: [swupdate] [PATCH 7/7] suricatta/lua: Add suricatta Lua module documentation Date: Thu, 2 Jun 2022 11:18:07 +0200 Message-ID: <20220602091807.54031-1-christian.storm@siemens.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220602091147.53323-1-christian.storm@siemens.com> References: <20220602091147.53323-1-christian.storm@siemens.com> MIME-Version: 1.0 X-Originating-IP: [139.22.41.90] X-ClientProxiedBy: DEMCHDC89XA.ad011.siemens.net (139.25.226.103) To DEMCHDC8A0A.ad011.siemens.net (139.25.226.106) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: d35b6b58-f508-4468-2cb0-08da4478c318 X-MS-TrafficTypeDiagnostic: VE1PR10MB3758:EE_ X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: NMgkavah4v/MU9jE+mCdkx01e/rDoGpgozRTmuqE+rbnU6RA+WgNPe6L5WyuNTVGhaWRB7xvyP3G6gh0OByez3SfgT23Ju0ybmyJRWptbNRW3hT5tes0yaETJ5vsYeTlpAFRONpr27cCsXYuxvVrzWgBy2TSCREyfsNET5OALOktRmWOR4mc1aphqKat0DJ7UOrgkGPj/3xuquDZ09WN+H7wxS52XeH/DbdzT3FzpQmIVzYhHarP1gIMwK+3qn75wqNo8Iumir6fFJk93342LtXTtSyI4YYU8eiZP9CFB7957dGR4frzQdHje9e1m2+llEl6zEI424pJhkIr4Du8tsDj48Km932oSQJpES2NzTx8h1Zd5UAmjxIYK3PlO6uqrR8zILPhlCwX0HCUnBybUBGUCFLRbocFb5GDWjxMyzd/ahHk8oVddxxZ38xb4xEPNrNCuyEB00YGjx64sIp4yaJEr1jeaa1+Cv9y+QV+DEe+aFfrpDPv8U/gDcMS0FIqsFoeyoNbHnA1vQ3JsYOhuLY4AcVF7nqSokUX3ZAu0yZt40mk+1NnxKuNbSKF9sNZG/YKrCLVzp4fw9IebOpTtD/qZvyD4JvIxoxqiOh/qlHS14FCfEdT9QBuo8cgIA+80aOYilQkNEORvHE02FYsVxgPffKqiBjWAjA/lVSkDQ//v3B+PBCtDXU0kX6kJ+KA0hbsHw7IMFYfWvka8HgHuC4T/IeFRQyLI+SVKMpEOCAlBwhf032+wVmhTjnGJR3Mv6MeJ97SDPV0bN9jnGVnJw== X-Forefront-Antispam-Report: CIP:194.138.21.70;CTRY:DE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:hybrid.siemens.com;PTR:hybrid.siemens.com;CAT:NONE;SFS:(13230001)(4636009)(46966006)(36840700001)(40470700004)(8936002)(70586007)(8676002)(4326008)(70206006)(82310400005)(5660300002)(30864003)(44832011)(966005)(40460700003)(6916009)(508600001)(6666004)(2906002)(26005)(36756003)(7596003)(7636003)(83380400001)(86362001)(956004)(47076005)(107886003)(336012)(186003)(16526019)(1076003)(36860700001)(316002)(82960400001)(356005)(2616005);DIR:OUT;SFP:1101; X-OriginatorOrg: siemens.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Jun 2022 09:17:48.5470 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: d35b6b58-f508-4468-2cb0-08da4478c318 X-MS-Exchange-CrossTenant-Id: 38ae3bcd-9579-4fd4-adda-b42e1495d55a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=38ae3bcd-9579-4fd4-adda-b42e1495d55a;Ip=[194.138.21.70];Helo=[hybrid.siemens.com] X-MS-Exchange-CrossTenant-AuthSource: DB5EUR01FT027.eop-EUR01.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: VE1PR10MB3758 X-Original-Sender: christian.storm@siemens.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@siemens.com header.s=selector2 header.b=SwdFNr5f; arc=pass (i=1 spf=pass spfdomain=siemens.com dmarc=pass fromdomain=siemens.com); spf=pass (google.com: domain of christian.storm@siemens.com designates 2a01:111:f400:7e1a::60b as permitted sender) smtp.mailfrom=christian.storm@siemens.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=siemens.com 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: , Document the Lua suricatta module. Signed-off-by: Christian Storm --- doc/source/suricatta.rst | 241 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 230 insertions(+), 11 deletions(-) diff --git a/doc/source/suricatta.rst b/doc/source/suricatta.rst index 6c8715c..e207597 100644 --- a/doc/source/suricatta.rst +++ b/doc/source/suricatta.rst @@ -21,9 +21,12 @@ failure in case booting the newly flashed root file system has failed and a switchback had to be performed. Suricatta is designed to be extensible in terms of the servers supported -as described in Section `Supporting different Servers`_. Currently, +as described in Section `The Suricatta Interface`_. Currently, support for the `hawkBit`_ server is implemented via the `hawkBit Direct Device Integration API`_ alongside a simple general purpose HTTP server. +The support for suricatta modules written in Lua is not a particular server +support implementation but rather an option for writing such in Lua instead +of C. .. _hawkBit Direct Device Integration API: http://sp.apps.bosch-iot-cloud.com/documentation/developerguide/apispecifications/directdeviceintegrationapi.html .. _hawkBit: https://projects.eclipse.org/projects/iot.hawkbit @@ -76,17 +79,17 @@ upstream server are skipped with an according message until a restart of SWUpdate has happened in order to not install the same update again. -Supporting different Servers ----------------------------- +The Suricatta Interface +----------------------- -Support for servers other than hawkBit can be realized by implementing -the "interfaces" described in ``include/channel.h`` and -``include/suricatta/server.h``. The former abstracts a particular -connection to the server, e.g., HTTP-based in case of hawkBit, while -the latter implements the logics to poll and install updates. -See ``corelib/channel_curl.c``/``include/channel_curl.h`` and -``suricatta/server_hawkbit.{c,h}`` for an example implementation -targeted towards hawkBit. +Support for servers other than hawkBit or the general purpose HTTP server can be +realized by implementing the "interfaces" described in ``include/channel.h`` and +``include/suricatta/server.h``, the latter either in C or in Lua. +The channel interface abstracts a particular connection to the server, e.g., +HTTP-based in case of hawkBit. The server interface defines the logics to poll +and install updates. See ``corelib/channel_curl.c`` / ``include/channel_curl.h`` +and ``suricatta/server_hawkbit.{c,h}`` for an example implementation in C targeted +towards hawkBit. ``include/channel.h`` describes the functionality a channel has to implement: @@ -267,3 +270,219 @@ with the events set as above, the formatted text in case of "success" will be: :: Formatted log: #13,Mon, 17 Sep 2018 10:55:18 CEST,1.0,ipse,333 + + +Support for Suricatta Modules in Lua +------------------------------------ + +The ``server_lua.c`` C-to-Lua bridge enables writing suricatta modules in Lua. It +provides the infrastructure in terms of the interface to SWUpdate "core" to the Lua +realm, enabling the "business logic" such as handling update flows and communicating +with backend server APIs to be modeled in Lua. To the Lua realm, the ``server_lua.c`` +C-to-Lua bridge provides the same functionality as the other suricatta modules +written in C have, realizing a separation of means and control. Effectively, it lifts +the interface outlined in Section `The Suricatta Interface`_ to the Lua realm. + + +As an example server implementation, see ``examples/suricatta/server_general.py`` for +a simple (mock) server of a backend that's modeled after the "General Purpose HTTP +Server" (cf. Section `Support for general purpose HTTP server`_). The matching Lua +suricatta module is found in ``examples/suricatta/swupdate_suricatta.lua``. Place it in +Lua's path so that a ``require("swupdate_suricatta")`` can load it or embed it into the +SWUpdate binary by enabling ``CONFIG_EMBEDDED_SURICATTA_LUA`` and setting +``CONFIG_EMBEDDED_SURICATTA_LUA_SOURCE`` accordingly. + +The interface specification in terms of a Lua (suricatta) module is found in +``suricatta/suricatta.lua``. + + +`suricatta` +........... + +The ``suricatta`` table is the module's main table housing the exposed functions and +definitions via the sub-tables described below. +In addition, the main functions ``suricatta.install()`` and ``suricatta.download()`` +as well as the convenience functions ``suricatta.getversion()``, ``suricatta.sleep()``, +and ``suricatta.get_tmpdir()`` are exposed: + +The function ``suricatta.install(install_channel)`` installs an update artifact from +a remote server or a local file. The ``install_channel`` table parameter designates +the channel to be used for accessing the artifact plus channel options diverging +from the defaults set at channel creation time. For example, an ``install_channel`` +table may look like this: + +.. code-block:: lua + + { channel = chn, url = "https://artifacts.io/update.swu" } + +where ``chn`` is the return value of a call to ``channel.open()``. The other table +attributes, like ``url`` in this example, are channel options diverging from or +omitted while channel creation time, see :ref:`suricatta.channel`. For installing +a local file, an ``install_channel`` table may look like this: + +.. code-block:: lua + + { channel = chn, url = "file:///path/to/file.swu" } + + +The function ``suricatta.download(download_channel, localpath)`` just downloads an +update artifact. The parameter ``download_channel`` is as for ``suricatta.install()``. +The parameter ``localpath`` designates the output path for the artifact. The +``suricatta.get_tmpdir()`` function (see below) is in particular useful for this case +to supply a temporary download location as ``localpath``. A just downloaded artifact +may be installed later using ``suricata.install()`` with an appropriate ``file://`` +URL, realizing a deferred installation. + +Both, ``suricatta.install()`` and ``suricatta.download()`` return ``true``, or, in +case of error, ``nil``, a ``suricatta.status`` value, and a table with messages in +case of errors, else an empty table. + +| + +The function ``suricatta.getversion()`` returns a table with SWUpdate's ``version`` +and ``patchlevel`` fields. This information can be used to determine API +(in-)compatibility of the Lua suricatta module with the SWUpdate version running it. + +The function ``suricatta.sleep(seconds)`` is a wrapper around `SLEEP(3)` for, e.g., +implementing a REST API call retry mechanism after a number of given seconds have +elapsed. + +The function ``suricatta.get_tmpdir()`` returns the path to SWUpdate's temporary +working directory where, e.g., the ``suricatta.download()`` function may place the +downloaded artifacts. + + +`suricatta.status` +.................. + +The ``suricatta.status`` table exposes the ``server_op_res_t`` enum values defined in +``include/util.h`` to the Lua realm. + + +`suricatta.notify` +.................. + +The ``suricatta.notify`` table provides the usual logging functions to the Lua +suricatta module matching their uppercase-named pendants available in the C realm. + +One notable exception is ``suricatta.notify.progress(message)`` which dispatches the +message to the progress interface (see :doc:`progress`). Custom progress client +implementations listening and acting on custom progress messages can be realized +using this function. + +All notify functions return ``nil``. + + +`suricatta.pstate` +.................. + +The ``suricatta.pstate`` table provides a binding to SWUpdate's (persistent) state +handling functions defined in ``include/state.h``, however, limited to the bootloader +environment variable ``STATE_KEY`` defined by ``CONFIG_UPDATE_STATE_BOOTLOADER`` and +defaulting to ``ustate``. In addition, it captures the ``update_state_t`` enum values. + +The function ``suricatta.pstate.save(state)`` requires one of ``suricatta.pstate``'s +"enum" values as parameter and returns ``true``, or, in case of error, ``nil``. +The function ``suricatta.pstate.get()`` returns ``true``, or, in case of error, ``nil``, +plus one of ``suricatta.pstate``'s "enum" values in the former case. + + +`suricatta.server` +.................. + +The ``suricatta.server`` table provides the sole function +``suricatta.server.register(function_p, purpose)``. It registers a Lua function +"pointed" to by ``function_p`` for the purpose ``purpose`` which is defined by +``suricatta.server``'s "enum" values. Those enum values correspond to the functions +defined in the interface outlined in the Section on `The Suricatta Interface`_. + +In addition to these functions, the two callback functions ``CALLBACK_PROGRESS`` and +``CALLBACK_CHECK_CANCEL`` can be registered optionally: The former can be used to upload +progress information to the server while the latter serves as ``dwlwrdata`` function +(see ``include/channel_curl.h``) to decide on whether an installation should be aborted +while the download phase. + +For details on the (callback) functions and their signatures, see the interface +specification ``suricatta/suricatta.lua`` and the documented example Lua suricatta +module found in ``examples/suricatta/swupdate_suricatta.lua``. + +The ``suricatta.server.register()`` function returns ``true``, or, in case of error, +``nil``. + + +.. _suricatta.channel: + +`suricatta.channel` +................... + +The ``suricatta.channel`` table captures channel handling for suricatta Lua modules. +The single function ``suricatta.channel.open(options)`` creates and opens a channel +to a server. Its single parameter ``options`` is a table specifying the channel's +default options such as `proxy`, `retries`, `usessl`, `strictssl`, or +`headers_to_send`. For convenience, options that may change per request such as +`url`, `content-type`, or `headers_to_send` may be set as defaults on channel +creation time while being selectively overruled on a per request basis. The channel +options currently supported to be set are listed in the ``suricatta.channel.options`` +table. In essence, the ``options`` parameter table is the Lua table equivalent of +``include/channel_curl.h``'s ``channel_data_t``. + + +The ``suricatta.channel.open(options)`` function returns a channel table which is +either passed to the ``suricatta.install()`` and ``suricatta.download()`` functions +or used directly for communication with a server. More specifically, it has the three +functions + +* ``get(options)`` for retrieving information from the server, +* ``put(options)`` for sending information to the server, and +* ``close()`` for closing the channel. + +The ``get()`` and ``put()`` functions' single parameter ``options`` is a per-request +channel option table as described above. + +The functions ``get()`` and ``put()`` return ``true``, or, in case of error, ``nil``, +a ``suricatta.status`` value, and an operation result table. +The latter contains the fields: + +* ``http_response_code`` carrying the HTTP error code, +* ``format`` as one of ``suricatta.channel.content``'s options, +* ``raw_reply`` if ``options`` contained ``format = suricatta.channel.content.RAW``, +* ``json_reply`` if ``options`` contained ``format = suricatta.channel.content.JSON``, and +* the HTTP headers received in the ``received_headers`` table, if any. + + +The ``suricatta.channel.content`` "enum" table defines the "format", i.e., the response +body content type and whether to parse it or not: + +* ``NONE`` means the response body is discarded. +* ``RAW`` means the raw server's reply is available as ``raw_reply``. +* ``JSON`` means the server's JSON reply is parsed into a Lua table and available + as ``json_reply``. + + +The ``suricatta.channel.method`` "enum" table defines the HTTP method to use for +a request issued with the ``put(options)`` function, i.e., `POST`, `PATCH`, or `PUT` as +specified in the ``options`` parameter table via the ``method`` attribute. +In addition to the HTTP method, the request body's content is set with the +``request_body`` attribute in the ``options`` parameter table. + + +As a contrived example, consider the following call to a channel's ``put()`` function + +.. code-block:: lua + + ... + local res, _, data = channel.put({ + url = string.format("%s/%s", base_url, device_id), + content_type = "application/json", + method = suricatta.channel.method.PATCH, + format = suricatta.channel.content.NONE, + request_body = "{ ... }" + }) + ... + +that issues a HTTP `PATCH` to some URL with a JSON content without having interest in +the response body. + +More examples of how to use a channel can be found in the example suricatta Lua +module ``examples/suricatta/swupdate_suricatta.lua``. +