Compare commits

...
3 Commits
Author SHA1 Message Date
Mattias Walström cc0edaf59c confd: Add support to upload images directly
This add support for upgrading images with curl only, without FTP or
similiar. The image needs to be base64 encoded.

Example:

TEMP_JSON=/tmp/restconf.txt
echo -n '{"infix-system:input":{"image":"' > "$TEMP_JSON"
base64 -w 0 "$BUNDLE_FILE" >> "$TEMP_JSON"
echo '"}}' >> "$TEMP_JSON"

curl -k \
  -X POST \
  -u "admin:admin" \
  -H "Content-Type: application/yang-data+json" \
  -H "Accept: application/yang-data+json" \
  -d @"$TEMP_JSON" \
  "https://192.168.30.1/restconf/operations/infix-system:install-bundle"
2025-07-11 16:33:02 +02:00
Mattias Walström 60b3a97f0e confd: Coding style 2025-07-11 16:33:01 +02:00
Mattias Walström e534c0bd2d nginx: Add support for large files in restconf app. 2025-07-11 16:26:33 +02:00
5 changed files with 161 additions and 31 deletions
@@ -1,5 +1,11 @@
# /telemetry/optics is for streaming (not used atm) # /telemetry/optics is for streaming (not used atm)
# Proxy buffer settings for large files
proxy_buffering off; # Disable buffering for streaming
proxy_request_buffering off; # Stream request body immediately
proxy_max_temp_file_size 0; # No temp files
location ~ ^/(restconf|yang|.well-known)/ { location ~ ^/(restconf|yang|.well-known)/ {
client_max_body_size 200M;
client_body_buffer_size 1M;
grpc_pass grpc://[::1]:10080; grpc_pass grpc://[::1]:10080;
grpc_set_header Host $host; grpc_set_header Host $host;
grpc_set_header X-Real-IP $remote_addr; grpc_set_header X-Real-IP $remote_addr;
+1
View File
@@ -78,6 +78,7 @@ PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
PKG_CHECK_MODULES([libite], [libite >= 2.6.1]) PKG_CHECK_MODULES([libite], [libite >= 2.6.1])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36]) PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0]) PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
PKG_CHECK_MODULES([libssl], [libssl >= 1.0.0])
# Control build with automake flags # Control build with automake flags
AM_CONDITIONAL(CONTAINERS, [test "x$enable_containers" != "xno"]) AM_CONDITIONAL(CONTAINERS, [test "x$enable_containers" != "xno"])
+3 -1
View File
@@ -12,6 +12,7 @@ confd_plugin_la_CFLAGS = \
$(libite_CFLAGS) \ $(libite_CFLAGS) \
$(sysrepo_CFLAGS) \ $(sysrepo_CFLAGS) \
$(libsrx_CFLAGS) \ $(libsrx_CFLAGS) \
$(libssl_CFLAGS) \
$(CFLAGS) $(CFLAGS)
confd_plugin_la_LIBADD = \ confd_plugin_la_LIBADD = \
@@ -20,7 +21,8 @@ confd_plugin_la_LIBADD = \
$(jansson_LIBS) \ $(jansson_LIBS) \
$(libite_LIBS) \ $(libite_LIBS) \
$(sysrepo_LIBS) \ $(sysrepo_LIBS) \
$(libsrx_LIBS) $(libsrx_LIBS) \
$(libssl_LIBS)
confd_plugin_la_SOURCES = \ confd_plugin_la_SOURCES = \
base64.c base64.h \ base64.c base64.h \
+120 -25
View File
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: BSD-3-Clause */ /* SPDX-License-Identifier: BSD-3-Clause */
#include <srx/common.h> #include <srx/common.h>
#include <srx/lyx.h> #include <srx/lyx.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <sys/statvfs.h>
#include "core.h" #include "core.h"
#include "rauc-installer.h" #include "rauc-installer.h"
@@ -21,24 +24,111 @@ static RaucInstaller *infix_system_sw_new_rauc(void)
return rauc; return rauc;
} }
static int base64_decode_inplace(char *input, size_t input_len, size_t *output_len)
{
BIO *bio, *b64;
int decode_len;
bio = BIO_new_mem_buf(input, input_len);
if (!bio) {
return -1;
}
b64 = BIO_new(BIO_f_base64());
if (!b64) {
BIO_free(bio);
return -1;
}
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bio = BIO_push(b64, bio);
/* Decode directly into the same buffer */
decode_len = BIO_read(bio, input, input_len);
BIO_free_all(bio);
if (decode_len < 0) {
return -1;
}
*output_len = decode_len;
return 0;
}
static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id, static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id,
const char *path, const sr_val_t *input, const char *path, const sr_val_t *input,
const size_t input_cnt, sr_event_t event, const size_t input_cnt, sr_event_t event,
unsigned request_id, sr_val_t **output, unsigned request_id, sr_val_t **output,
size_t *output_cnt, void *priv) size_t *output_cnt, void *priv)
{ {
char *url = input->data.string_val;
sr_error_t srerr = SR_ERR_OK; sr_error_t srerr = SR_ERR_OK;
GError *raucerr = NULL; GError *raucerr = NULL;
RaucInstaller *rauc; RaucInstaller *rauc = NULL;
char *install_source = NULL;
DEBUG("url:%s", url); const char *url = NULL;
char *binary_data = NULL;
size_t binary_len = 0;
size_t decoded_len = 0;
for (size_t i = 0; i < input_cnt; i++) {
if (strcmp(input[i].xpath, "/infix-system:install-bundle/url") == 0) {
if (input[i].type == SR_STRING_T) {
url = input[i].data.string_val;
}
} else if (strcmp(input[i].xpath, "/infix-system:install-bundle/image") == 0) {
if (input[i].type == SR_BINARY_T) {
binary_data = (char *)input[i].data.binary_val; // Cast away const for in-place decode
binary_len = strlen(binary_data); // Length of base64 string
}
}
}
if (url) {
DEBUG("Installing from URL: %s", url);
install_source = (char *)url;
} else if (binary_data) {
const char *temp_dir = "/tmp";
char path[256];
FILE *fp;
DEBUG("Installing from uploaded binary data (%zu bytes base64)", binary_len);
if (base64_decode_inplace(binary_data, binary_len, &decoded_len) != 0) {
sr_session_set_netconf_error(session, "application", "invalid-value",
NULL, NULL, "Failed to decode base64 image data", 0);
srerr = SR_ERR_INVAL_ARG;
goto cleanup;
}
fmkpath(0775, "%s/images", temp_dir);
snprintf(path, sizeof(path), "%s/images/install_bundle", temp_dir);
fp = fopen(path, "wb");
if (!fp) {
ERROR("Could not open %s", path);
return SR_ERR_NO_MEMORY;
}
fwrite(binary_data, sizeof(char), decoded_len, fp);
fclose(fp);
install_source = path;
} else {
ERROR("Unknown source");
return 0;
}
rauc = infix_system_sw_new_rauc(); rauc = infix_system_sw_new_rauc();
if (!rauc) if (!rauc) {
return SR_ERR_INTERNAL; sr_session_set_netconf_error(session, "application", "operation-failed",
NULL, NULL, "Failed to initialize RAUC installer", 0);
srerr = SR_ERR_INTERNAL;
goto cleanup;
}
rauc_installer_call_install_sync(rauc, url, NULL, &raucerr); rauc_installer_call_install_sync(rauc, install_source, NULL, &raucerr);
if (raucerr) { if (raucerr) {
sr_session_set_netconf_error(session, "application", "operation-failed", sr_session_set_netconf_error(session, "application", "operation-failed",
NULL, NULL, raucerr->message, 0); NULL, NULL, raucerr->message, 0);
@@ -46,13 +136,18 @@ static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id,
srerr = SR_ERR_OPERATION_FAILED; srerr = SR_ERR_OPERATION_FAILED;
} }
g_object_unref(rauc); cleanup:
if (rauc) {
g_object_unref(rauc);
}
return srerr; return srerr;
} }
/* /*
boot order can only be: primary, secondary and net, limited by model boot order can only be: primary, secondary and net, limited by model
*/ */
static int infix_system_sw_set_boot_order(sr_session_ctx_t *session, uint32_t sub_id, static int infix_system_sw_set_boot_order(sr_session_ctx_t *session, uint32_t sub_id,
const char *path, const sr_val_t *input, const char *path, const sr_val_t *input,
const size_t input_cnt, sr_event_t event, const size_t input_cnt, sr_event_t event,
@@ -63,24 +158,24 @@ static int infix_system_sw_set_boot_order(sr_session_ctx_t *session, uint32_t su
const sr_val_t *val = &input[i]; const sr_val_t *val = &input[i];
if (i != 0) if (i != 0)
strlcat(boot_order, " ", sizeof(boot_order)); strlcat(boot_order, " ", sizeof(boot_order));
strlcat(boot_order, val->data.string_val, sizeof(boot_order)); strlcat(boot_order, val->data.string_val, sizeof(boot_order));
} }
if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) { if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) {
if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) { if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) {
ERROR("Set-boot-order: Failed to set boot order in U-Boot"); ERROR("Set-boot-order: Failed to set boot order in U-Boot");
return SR_ERR_INTERNAL; return SR_ERR_INTERNAL;
} }
} else if (fexist("/mnt/aux/grub/grubenv")) { } else if (fexist("/mnt/aux/grub/grubenv")) {
if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) { if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) {
ERROR("Set-boot-order: Failed to set boot order in Grub"); ERROR("Set-boot-order: Failed to set boot order in Grub");
return SR_ERR_INTERNAL; return SR_ERR_INTERNAL;
} }
} else { } else {
ERROR("No supported boot loader found"); ERROR("No supported boot loader found");
return SR_ERR_UNSUPPORTED; return SR_ERR_UNSUPPORTED;
} }
return SR_ERR_OK; return SR_ERR_OK;
} }
@@ -204,16 +204,42 @@ submodule infix-system-software {
description description
"Upgrade the system's software by installing the specified bundle."; "Upgrade the system's software by installing the specified bundle.";
input { input {
leaf url { choice source {
type string;
mandatory true; mandatory true;
description description
"The location of the software bundle, specified as a Uniform "Source of the software bundle to install.";
Resource Locator (URL). Currently supported protocols include case url {
FTP, HTTP(S) and SCP."; leaf url {
type string;
description
"The location of the software bundle, specified as a Uniform
Resource Locator (URL). Currently supported protocols include
FTP, HTTP(S) and SCP.";
}
}
case upload {
leaf image {
type binary;
description
"The image data to install, provided as base64-encoded binary data.";
}
}
} }
} }
} }
rpc install-upload-bundle {
nacm:default-deny-all;
description "Upgrade the system's software by installing from a supplied blob.";
input {
leaf image {
type binary;
mandatory true;
description "The image to install";
}
}
}
rpc set-boot-order { rpc set-boot-order {
nacm:default-deny-all; nacm:default-deny-all;
description description