Skip to content

Upload image and upgrade #1081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions board/common/rootfs/etc/nginx/restconf.app
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# /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)/ {
client_max_body_size 200M;
client_body_buffer_size 1M;
grpc_pass grpc://[::1]:10080;
grpc_set_header Host $host;
grpc_set_header X-Real-IP $remote_addr;
Expand Down
1 change: 1 addition & 0 deletions src/confd/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
PKG_CHECK_MODULES([libite], [libite >= 2.6.1])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
PKG_CHECK_MODULES([libssl], [libssl >= 1.0.0])

# Control build with automake flags
AM_CONDITIONAL(CONTAINERS, [test "x$enable_containers" != "xno"])
Expand Down
4 changes: 3 additions & 1 deletion src/confd/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ confd_plugin_la_CFLAGS = \
$(libite_CFLAGS) \
$(sysrepo_CFLAGS) \
$(libsrx_CFLAGS) \
$(libssl_CFLAGS) \
$(CFLAGS)

confd_plugin_la_LIBADD = \
Expand All @@ -20,7 +21,8 @@ confd_plugin_la_LIBADD = \
$(jansson_LIBS) \
$(libite_LIBS) \
$(sysrepo_LIBS) \
$(libsrx_LIBS)
$(libsrx_LIBS) \
$(libssl_LIBS)

confd_plugin_la_SOURCES = \
base64.c base64.h \
Expand Down
147 changes: 121 additions & 26 deletions src/confd/src/infix-system-software.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <srx/common.h>
#include <srx/lyx.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <sys/statvfs.h>

#include "core.h"
#include "rauc-installer.h"
Expand All @@ -21,38 +24,130 @@ static RaucInstaller *infix_system_sw_new_rauc(void)
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,
const char *path, const sr_val_t *input,
const size_t input_cnt, sr_event_t event,
unsigned request_id, sr_val_t **output,
size_t *output_cnt, void *priv)
{
char *url = input->data.string_val;
sr_error_t srerr = SR_ERR_OK;
GError *raucerr = NULL;
RaucInstaller *rauc;
RaucInstaller *rauc = NULL;
char *install_source = NULL;

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
}
}
}

DEBUG("url:%s", url);
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();
if (!rauc)
return SR_ERR_INTERNAL;
if (!rauc) {
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) {
sr_session_set_netconf_error(session, "application", "operation-failed",
NULL, NULL, raucerr->message, 0);
g_error_free(raucerr);
srerr = SR_ERR_OPERATION_FAILED;
}

g_object_unref(rauc);
cleanup:
if (rauc) {
g_object_unref(rauc);
}

return srerr;
}


/*
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,
const char *path, const sr_val_t *input,
const size_t input_cnt, sr_event_t event,
Expand All @@ -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];

if (i != 0)
strlcat(boot_order, " ", sizeof(boot_order));
strlcat(boot_order, val->data.string_val, sizeof(boot_order));
}

if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) {
if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) {
ERROR("Set-boot-order: Failed to set boot order in U-Boot");
return SR_ERR_INTERNAL;
}
} else if (fexist("/mnt/aux/grub/grubenv")) {
if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) {
ERROR("Set-boot-order: Failed to set boot order in Grub");
return SR_ERR_INTERNAL;
}
} else {
ERROR("No supported boot loader found");
return SR_ERR_UNSUPPORTED;
}
strlcat(boot_order, " ", sizeof(boot_order));
strlcat(boot_order, val->data.string_val, sizeof(boot_order));
}

if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) {
if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) {
ERROR("Set-boot-order: Failed to set boot order in U-Boot");
return SR_ERR_INTERNAL;
}
} else if (fexist("/mnt/aux/grub/grubenv")) {
if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) {
ERROR("Set-boot-order: Failed to set boot order in Grub");
return SR_ERR_INTERNAL;
}
} else {
ERROR("No supported boot loader found");
return SR_ERR_UNSUPPORTED;
}

return SR_ERR_OK;
}
Expand Down
36 changes: 31 additions & 5 deletions src/confd/yang/confd/infix-system-software.yang
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,42 @@ submodule infix-system-software {
description
"Upgrade the system's software by installing the specified bundle.";
input {
leaf url {
type string;
choice source {
mandatory true;
description
"The location of the software bundle, specified as a Uniform
Resource Locator (URL). Currently supported protocols include
FTP, HTTP(S) and SCP.";
"Source of the software bundle to install.";
case url {
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";
}
}
}

Comment on lines +231 to +242
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated install-bundle RPC (above) looks great! But what is this, looks like an earlier attempt or test code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated install-bundle RPC (above) looks great! But what is this, looks like an earlier attempt or test code?

Yeah, this was for testing the concept.

rpc set-boot-order {
nacm:default-deny-all;
description
Expand Down