diff --git a/src/confd/configure.ac b/src/confd/configure.ac index 4283ce89..0ac88b4f 100644 --- a/src/confd/configure.ac +++ b/src/confd/configure.ac @@ -35,6 +35,7 @@ AS_IF([test "x$with_login_shell" != "xno"], [ PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES([augeas], [augeas >= 1.12.0]) +PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.50 gio-2.0 gio-unix-2.0]) PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0]) PKG_CHECK_MODULES([libite], [libite >= 2.5.0]) PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36]) diff --git a/src/confd/src/confd/Makefile.am b/src/confd/src/confd/Makefile.am index c13e22e7..30535dbe 100644 --- a/src/confd/src/confd/Makefile.am +++ b/src/confd/src/confd/Makefile.am @@ -8,6 +8,7 @@ confd_plugin_la_LDFLAGS = -module -avoid-version -shared confd_plugin_la_CFLAGS = \ $(augeas_CFLAGS) \ + $(glib_CFLAGS) \ $(jansson_CFLAGS) \ $(libite_CFLAGS) \ $(sysrepo_CFLAGS) \ @@ -15,15 +16,35 @@ confd_plugin_la_CFLAGS = \ confd_plugin_la_LIBADD = \ $(augeas_LIBS) \ + $(glib_LIBS) \ $(jansson_LIBS) \ $(libite_LIBS) \ $(sysrepo_LIBS) +rauc_installer_sources = \ + rauc-installer.c \ + rauc-installer.h + +BUILT_SOURCES = $(rauc_installer_sources) +EXTRA_DIST = ../lib/de.pengutronix.rauc.Installer.xml + +$(rauc_installer_sources): ../lib/de.pengutronix.rauc.Installer.xml + $(AM_V_GEN) gdbus-codegen \ + --generate-c-code rauc-installer \ + --c-generate-autocleanup all \ + --c-namespace Rauc \ + --interface-prefix de.pengutronix.rauc. \ + $< + +nodist_confd_plugin_la_SOURCES = \ + $(rauc_installer_sources) + confd_plugin_la_SOURCES = \ ietf-interfaces.c \ ietf-system.c \ infix-dhcp.c \ infix-factory.c \ + infix-system-software.c \ \ ../lib/common.h \ ../lib/helpers.c ../lib/helpers.h \ diff --git a/src/confd/src/confd/core.c b/src/confd/src/confd/core.c index 1d58602f..fb3e686f 100644 --- a/src/confd/src/confd/core.c +++ b/src/confd/src/confd/core.c @@ -104,6 +104,9 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv) if (rc) goto err; rc = infix_factory_init(&confd); + if (rc) + goto err; + rc = infix_system_sw_init(&confd); if (rc) goto err; diff --git a/src/confd/src/confd/core.h b/src/confd/src/confd/core.h index b16908fa..b5ccac9b 100644 --- a/src/confd/src/confd/core.h +++ b/src/confd/src/confd/core.h @@ -134,4 +134,7 @@ int infix_dhcp_init(struct confd *confd); /* infix-factory.c */ int infix_factory_init(struct confd *confd); +/* infix-system-software.c */ +int infix_system_sw_init(struct confd *confd); + #endif /* CONFD_CORE_H_ */ diff --git a/src/confd/src/confd/infix-system-software.c b/src/confd/src/confd/infix-system-software.c new file mode 100644 index 00000000..f132dd74 --- /dev/null +++ b/src/confd/src/confd/infix-system-software.c @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include "core.h" +#include "../lib/common.h" +#include "../lib/lyx.h" +#include "rauc-installer.h" + +#include + +#define SW_STATE_PATH_ "/ietf-system:system-state/infix-system:software" + +static const struct lys_module *infix_system; + +static RaucInstaller *infix_system_sw_new_rauc(void) +{ + RaucInstaller *rauc; + GError *raucerr = NULL; + + rauc = rauc_installer_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, + "de.pengutronix.rauc", "/", NULL, &raucerr); + if (raucerr) { + ERROR("Unable to connect to RAUC: %s", raucerr->message); + g_error_free(raucerr); + return NULL; + } + + return rauc; +} + +static sr_error_t infix_system_sw_state_fill_installer(struct lyd_node *inst, + RaucInstaller *rauc) +{ + struct lyd_node *progress; + GVariant *props, *val; + const char *strval; + + strval = rauc_installer_get_operation(rauc); + if (strval && strval[0] && + lyd_new_term(inst, NULL, "operation", strval, 0, NULL)) + return SR_ERR_INTERNAL; + + strval = rauc_installer_get_last_error(rauc); + if (strval && strval[0] && + lyd_new_term(inst, NULL, "last-error", strval, 0, NULL)) + return SR_ERR_INTERNAL; + + props = rauc_installer_get_progress(rauc); + if (props) { + if (lyd_new_inner(inst, NULL, "progress", 0, &progress)) + return SR_ERR_INTERNAL; + + g_variant_get(props, "(@isi)", &val, &strval, NULL); + + if (lyd_new_term(progress, NULL, "percentage", + g_variant_print(val, FALSE), 0, NULL)) + return SR_ERR_INTERNAL; + + if (strval && strval[0] && + lyd_new_term(progress, NULL, "message", strval, 0, NULL)) + return SR_ERR_INTERNAL; + } + return SR_ERR_OK; +} + +static sr_error_t infix_system_sw_state_fill_slot(struct lyd_node *slot, + GVariant *props) +{ + static const char *strprops[] = { + "bootname", + "class", + "state", + "sha256", + NULL + }; + struct lyd_node *section; + const char **strprop; + const char *strval; + GVariant *val; + + for (strprop = strprops; *strprop; strprop++) { + if (g_variant_lookup(props, *strprop, "s", &strval) && + lyd_new_term(slot, NULL, *strprop, strval, 0, NULL)) + return SR_ERR_INTERNAL; + } + + if (g_variant_lookup(props, "size", "@t", &val) && + lyd_new_term(slot, NULL, "size", g_variant_print(val, FALSE), 0, NULL)) + return SR_ERR_INTERNAL; + + + if (lyd_new_inner(slot, NULL, "bundle", 0, §ion)) + return SR_ERR_INTERNAL; + + if (g_variant_lookup(props, "bundle.compatible", "s", &strval) && + lyd_new_term(section, NULL, "compatible", strval, 0, NULL)) + return SR_ERR_INTERNAL; + + if (g_variant_lookup(props, "bundle.version", "s", &strval) && + lyd_new_term(section, NULL, "version", strval, 0, NULL)) + return SR_ERR_INTERNAL; + + + if (lyd_new_inner(slot, NULL, "installed", 0, §ion)) + return SR_ERR_INTERNAL; + + if (g_variant_lookup(props, "installed.timestamp", "s", &strval) && + lyd_new_term(section, NULL, "datetime", strval, 0, NULL)) + return SR_ERR_INTERNAL; + + if (g_variant_lookup(props, "installed.count", "@u", &val) && + lyd_new_term(section, NULL, "count", g_variant_print(val, FALSE), 0, NULL)) + return SR_ERR_INTERNAL; + + + if (lyd_new_inner(slot, NULL, "activated", 0, §ion)) + return SR_ERR_INTERNAL; + + if (g_variant_lookup(props, "activated.timestamp", "s", &strval) && + lyd_new_term(section, NULL, "datetime", strval, 0, NULL)) + return SR_ERR_INTERNAL; + + if (g_variant_lookup(props, "activated.count", "@u", &val) && + lyd_new_term(section, NULL, "count", g_variant_print(val, FALSE), 0, NULL)) + return SR_ERR_INTERNAL; + + return SR_ERR_OK; +} + +static sr_error_t infix_system_sw_state_fill(struct lyd_node *sw, + RaucInstaller *rauc) +{ + struct lyd_node *slot, *inst; + GVariant *slots, *props; + GError *raucerr = NULL; + GVariantIter slotiter; + sr_error_t srerr; + char *slotname; + const char *val; + + val = rauc_installer_get_compatible(rauc); + if (val && val[0] && lyd_new_term(sw, NULL, "compatible", val, 0, NULL)) + return SR_ERR_INTERNAL; + + val = rauc_installer_get_variant(rauc); + if (val && val[0] && lyd_new_term(sw, NULL, "variant", val, 0, NULL)) + return SR_ERR_INTERNAL; + + val = rauc_installer_get_boot_slot(rauc); + if (val && val[0] && lyd_new_term(sw, NULL, "booted", val, 0, NULL)) + return SR_ERR_INTERNAL; + + if (lyd_new_inner(sw, NULL, "installer", 0, &inst)) + return SR_ERR_INTERNAL; + + srerr = infix_system_sw_state_fill_installer(inst, rauc); + if (srerr) + return srerr; + + if (!rauc_installer_call_get_slot_status_sync(rauc, &slots, NULL, &raucerr)) { + /* Slot status is not available while an installation + * is in progress, so we don't consider that an error. + */ + g_error_free(raucerr); + return SR_ERR_OK; + } + + for (g_variant_iter_init(&slotiter, slots); + g_variant_iter_next(&slotiter, "(s@a{sv})", &slotname, &props);) { + if (lyd_new_list(sw, NULL, "slot", 0, &slot, slotname)) + return SR_ERR_INTERNAL; + + srerr = infix_system_sw_state_fill_slot(slot, props); + if (srerr) + break; + } + + return srerr; +} + +static int infix_system_sw_state(sr_session_ctx_t *session, uint32_t sub_id, + const char *module, const char *path, + const char *request_path, uint32_t request_id, + struct lyd_node **parent, void *priv) +{ + sr_error_t srerr = SR_ERR_INTERNAL; + RaucInstaller *rauc; + struct lyd_node *sw; + + DEBUG(""); + + rauc = infix_system_sw_new_rauc(); + if (!rauc) + return SR_ERR_INTERNAL; + + if (lyd_new_inner(*parent, infix_system, "software", 0, &sw)) + goto out; + + srerr = infix_system_sw_state_fill(sw, rauc); +out: + g_object_unref(rauc); + return srerr; +} + + +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; + + DEBUG("url:%s", url); + + rauc = infix_system_sw_new_rauc(); + if (!rauc) + return SR_ERR_INTERNAL; + + rauc_installer_call_install_sync(rauc, url, 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); + return srerr; +} + +int infix_system_sw_init(struct confd *confd) +{ + const struct ly_ctx *ly; + int rc = 0; + + ly = sr_acquire_context(sr_session_get_connection(confd->session)); + infix_system = ly_ctx_get_module_implemented(ly, "infix-system"); + sr_release_context(sr_session_get_connection(confd->session)); + if (!infix_system) { + ERROR("infix-system module not found"); + rc = -ENOENT; + goto fail; + } + + REGISTER_OPER(confd->session, "ietf-system", SW_STATE_PATH_, + infix_system_sw_state, NULL, 0, &confd->sub); + + REGISTER_RPC(confd->session, "/infix-system:install-bundle", + infix_system_sw_install, NULL, &confd->sub); + +fail: + return rc; +} diff --git a/src/confd/src/lib/de.pengutronix.rauc.Installer.xml b/src/confd/src/lib/de.pengutronix.rauc.Installer.xml new file mode 100644 index 00000000..2a92de4f --- /dev/null +++ b/src/confd/src/lib/de.pengutronix.rauc.Installer.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/confd/yang/infix-system-software@2023-06-27.yang b/src/confd/yang/infix-system-software@2023-06-27.yang new file mode 100644 index 00000000..b735a673 --- /dev/null +++ b/src/confd/yang/infix-system-software@2023-06-27.yang @@ -0,0 +1,147 @@ +submodule infix-system-software { + yang-version 1.1; + belongs-to infix-system { + prefix ixsys; + } + + import ietf-yang-types { + prefix yang; + } + + import ietf-netconf-acm { + prefix nacm; + } + + import ietf-system { + prefix sys; + } + + organization "KernelKit"; + contact "kernelkit@googlegroups.com"; + description "Software status and upgrade."; + + revision 2023-06-27 { + description "Initial revision."; + reference "internal"; + } + + grouping rauc-stage-log { + leaf datetime { + type yang:date-and-time; + } + + leaf count { + type uint32; + } + } + + grouping installer-state { + leaf operation { + type string; + } + + container progress { + leaf percentage { + type uint8 { + range "0 .. 100"; + } + } + + leaf message { + type string; + } + } + + leaf last-error { + type string; + } + } + + augment "/sys:system-state" { + container software { + leaf compatible { + type string; + } + + leaf variant { + type string; + } + + leaf booted { + type string; + } + + container installer { + uses installer-state; + + notification state-changed { + uses installer-state; + } + } + + list slot { + key "name"; + + leaf name { + type string; + } + + leaf bootname { + type string; + } + + leaf class { + type string; + } + + leaf state { + type string; + } + + container bundle { + leaf compatible { + type string; + } + + leaf version { + type string; + } + } + + leaf size { + type uint64; + } + + leaf sha256 { + type string { + pattern '[a-fA-F0-9]{64}'; + } + } + container installed { + uses rauc-stage-log; + } + + container activated { + uses rauc-stage-log; + } + } + } + } + + rpc install-bundle { + nacm:default-deny-all; + description + "Upgrade the system's software by installing the specified bundle."; + input { + leaf url { + type string; + mandatory true; + description + "The location of the software bundle, specified as a Uniform + Resource Locator (URL). + + Supported protocols include FTP, HTTP(S) and SCP."; + } + } + } +} diff --git a/src/confd/yang/infix-system@2023-04-11.yang b/src/confd/yang/infix-system@2023-04-11.yang index c5e0dad7..25f517b2 100644 --- a/src/confd/yang/infix-system@2023-04-11.yang +++ b/src/confd/yang/infix-system@2023-04-11.yang @@ -7,6 +7,8 @@ module infix-system { prefix sys; } + include infix-system-software; + organization "KernelKit"; contact "kernelkit@googlegroups.com"; description "Infix augments and deviations to ietf-system.";