diff --git a/board/aarch64/friendlyarm-nanopi-r2s/Config.in b/board/aarch64/friendlyarm-nanopi-r2s/Config.in index bb5b2a4b..077a16ca 100644 --- a/board/aarch64/friendlyarm-nanopi-r2s/Config.in +++ b/board/aarch64/friendlyarm-nanopi-r2s/Config.in @@ -4,6 +4,7 @@ config BR2_PACKAGE_FRIENDLYARM_NANOPI_R2S select SDCARD_AUX select BR2_PACKAGE_INPUT_EVENT_DAEMON select BR2_PACKAGE_LINUX_FIRMWARE_RTL_815X + select BR2_PACKAGE_INPUT_EVENT_DAEMON help FriendlyElec NanoPi R2S is a compact router board based on the Rockchip RK3328 SoC with dual Gigabit Ethernet ports. diff --git a/board/aarch64/friendlyarm-nanopi-r2s/friendlyarm-nanopi-r2s.mk b/board/aarch64/friendlyarm-nanopi-r2s/friendlyarm-nanopi-r2s.mk index 24ecbd6c..08e2212c 100644 --- a/board/aarch64/friendlyarm-nanopi-r2s/friendlyarm-nanopi-r2s.mk +++ b/board/aarch64/friendlyarm-nanopi-r2s/friendlyarm-nanopi-r2s.mk @@ -4,6 +4,9 @@ define FRIENDLYARM_NANOPI_R2S_LINUX_CONFIG_FIXUPS $(call KCONFIG_ENABLE_OPT,CONFIG_ROCKCHIP_IOMMU) $(call KCONFIG_ENABLE_OPT,CONFIG_ROCKCHIP_PM_DOMAINS) $(call KCONFIG_ENABLE_OPT,CONFIG_ROCKCHIP_IODOMAIN) + # TODO: for some reason this just locks up the device + # so we'll have to rely on the softdog :-( + # $(call KCONFIG_ENABLE_OPT,CONFIG_DW_WATCHDOG) # PHY drivers $(call KCONFIG_ENABLE_OPT,CONFIG_PHY_ROCKCHIP_EMMC) @@ -51,6 +54,10 @@ define FRIENDLYARM_NANOPI_R2S_LINUX_CONFIG_FIXUPS $(call KCONFIG_SET_OPT,CONFIG_NVMEM_ROCKCHIP_EFUSE,m) $(call KCONFIG_SET_OPT,CONFIG_NVMEM_ROCKCHIP_OTP,m) + # Input layer + $(call KCONFIG_ENABLE_OPT,CONFIG_INPUT_MISC) + $(call KCONFIG_ENABLE_OPT,CONFIG_INPUT_RK805_PWRKEY) + # Network: STMMAC Ethernet (WAN port - RK3328 GMAC with RTL8211E PHY) $(call KCONFIG_ENABLE_OPT,CONFIG_NET_VENDOR_STMICRO) $(call KCONFIG_ENABLE_OPT,CONFIG_STMMAC_ETH) diff --git a/board/aarch64/friendlyarm-nanopi-r2s/rootfs/usr/share/product/friendlyarm,nanopi-r2s/usr/sbin/wan-monitor.sh b/board/aarch64/rootfs/usr/sbin/wan-monitor.sh similarity index 100% rename from board/aarch64/friendlyarm-nanopi-r2s/rootfs/usr/share/product/friendlyarm,nanopi-r2s/usr/sbin/wan-monitor.sh rename to board/aarch64/rootfs/usr/sbin/wan-monitor.sh diff --git a/package/finit/0003-initctl-escape-special-characters-in-JSON-output.patch b/package/finit/0003-initctl-escape-special-characters-in-JSON-output.patch new file mode 100644 index 00000000..e0ce598e --- /dev/null +++ b/package/finit/0003-initctl-escape-special-characters-in-JSON-output.patch @@ -0,0 +1,131 @@ +From c4463ec64f9732bd67b8cfd2b73adc10e50d1178 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Sun, 18 Jan 2026 23:26:37 +0100 +Subject: [PATCH] initctl: escape special characters in JSON output +Organization: Wires + +Strings like command, description, and environment may contain characters +that need escaping for valid JSON, e.g., embedded quotes in command line +arguments like -V "NanoPi R2S". + +Add json_escape() helper to handle quotes, backslashes, and control chars. + +Signed-off-by: Joachim Wiberg +--- + src/initctl.c | 78 +++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 69 insertions(+), 9 deletions(-) + +diff --git a/src/initctl.c b/src/initctl.c +index 25c52cf..435e2da 100644 +--- a/src/initctl.c ++++ b/src/initctl.c +@@ -1036,6 +1036,62 @@ static int svc_compare(svc_t *svc, char *arg) + return 0; + } + ++/* ++ * Escape a string for safe JSON output. Handles quotes, backslashes, ++ * and control characters. Returns pointer to static buffer. ++ */ ++static char *json_escape(const char *str) ++{ ++ static char buf[1024]; ++ char *ptr = buf; ++ size_t left = sizeof(buf) - 1; ++ ++ if (!str) ++ return ""; ++ ++ while (*str && left > 1) { ++ char c = *str++; ++ ++ switch (c) { ++ case '"': ++ case '\\': ++ *ptr++ = '\\'; ++ *ptr++ = c; ++ left -= 2; ++ break; ++ case '\n': ++ *ptr++ = '\\'; ++ *ptr++ = 'n'; ++ left -= 2; ++ break; ++ case '\r': ++ *ptr++ = '\\'; ++ *ptr++ = 'r'; ++ left -= 2; ++ break; ++ case '\t': ++ *ptr++ = '\\'; ++ *ptr++ = 't'; ++ left -= 2; ++ break; ++ default: ++ if ((unsigned char)c < 0x20) { ++ /* Other control chars: use \uXXXX */ ++ int n = snprintf(ptr, left, "\\u%04x", (unsigned char)c); ++ ptr += n; ++ left -= n; ++ } else { ++ *ptr++ = c; ++ left--; ++ } ++ break; ++ } ++ } ++ *ptr = '\0'; ++ ++ return buf; ++} ++ + static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev) + { + long now = jiffies(); +@@ -1051,14 +1107,16 @@ static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev) + fprintf(fp, + "%s" + "%s{\n" +- "%s \"identity\": \"%s\",\n" +- "%s \"description\": \"%s\",\n" ++ "%s \"identity\": \"%s\",\n", ++ prev ? ",\n" : indent, prev ? indent : "", ++ indent, svc_ident(svc, NULL, 0)); ++ fprintf(fp, ++ "%s \"description\": \"%s\",\n", ++ indent, json_escape(svc->desc)); ++ fprintf(fp, + "%s \"type\": \"%s\",\n" + "%s \"forking\": %s,\n" + "%s \"status\": \"%s\",\n", +- prev ? ",\n" : indent, prev ? indent : "", +- indent, svc_ident(svc, NULL, 0), +- indent, svc->desc, + indent, svc_typestr(svc), + indent, svc->forking ? "true" : "false", + indent, svc_status(svc)); +@@ -1080,15 +1138,17 @@ static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev) + } + + fprintf(fp, +- "%s \"origin\": \"%s\",\n" ++ "%s \"origin\": \"%s\",\n", ++ indent, svc->file[0] ? svc->file : "built-in"); ++ svc_command(svc, buf, sizeof(buf), 0); ++ fprintf(fp, + "%s \"command\": \"%s\",\n", +- indent, svc->file[0] ? svc->file : "built-in", +- indent, svc_command(svc, buf, sizeof(buf), 0)); ++ indent, json_escape(buf)); + + svc_environ(svc, buf, sizeof(buf), 0); + if (buf[0]) + fprintf(fp, +- "%s \"environment\": \"%s\",\n", indent, buf); ++ "%s \"environment\": \"%s\",\n", indent, json_escape(buf)); + + svc_cond(svc, buf, sizeof(buf), 0); + if (buf[0]) +-- +2.43.0 + diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py index b1e25cd6..834cade5 100755 --- a/src/statd/python/cli_pretty/cli_pretty.py +++ b/src/statd/python/cli_pretty/cli_pretty.py @@ -161,7 +161,7 @@ class PadDhcpServer: class PadSensor: name = 30 - value = 20 + value = 22 status = 10 @classmethod @@ -186,14 +186,6 @@ class PadLldp: port_id = 20 -class PadDiskUsage: - mount = 18 - size = 12 - used = 12 - avail = 12 - percent = 6 - - def format_memory_bytes(bytes_val): """Convert bytes to human-readable format""" if bytes_val == 0: @@ -221,11 +213,6 @@ def format_uptime_seconds(seconds): else: return f"{seconds // 86400}d" - @classmethod - def table_width(cls): - """Total width of disk usage table""" - return cls.mount + cls.size + cls.used + cls.avail + cls.percent - class Column: """Column definition for SimpleTable""" @@ -2134,7 +2121,7 @@ def show_hardware(json): sensors = [c for c in components if c.get("class") == "iana-hardware:sensor"] wifi_radios = [c for c in components if c.get("class") == "infix-hardware:wifi"] - width = max(PadSensor.table_width(), 100) + width = max(PadSensor.table_width(), 62) # Display full-width inverted heading print(Decore.invert(f"{'HARDWARE COMPONENTS':<{width}}")) @@ -2158,12 +2145,12 @@ def show_hardware(json): Decore.title("WiFi radios", width) radios_table = SimpleTable([ - Column('NAME'), - Column('MANUFACTURER'), + Column('NAME', flexible=True), + Column('MANUFACTURER', flexible=True), Column('BANDS', 'right'), Column('STANDARDS', 'right'), Column('MAX AP', 'right') - ]) + ], min_width=width) for component in wifi_radios: phy = component.get("name", "") @@ -2208,10 +2195,10 @@ def show_hardware(json): Decore.title("USB Ports", width) usb_table = SimpleTable([ - Column('NAME'), + Column('NAME', flexible=True), Column('STATE'), Column('OPER') - ]) + ], min_width=width) for component in usb_ports: port = USBport(component) @@ -2939,7 +2926,7 @@ def show_system(json): except (ValueError, KeyError): pass - width = PadDiskUsage.table_width() + width = 62 print(Decore.invert(f"{'SYSTEM INFORMATION':<{width}}")) print(f"{'OS Name':<20}: {platform.get('os-name', 'Unknown')}") print(f"{'OS Version':<20}: {platform.get('os-version', 'Unknown')}") @@ -3022,23 +3009,23 @@ def show_system(json): disk_filtered = [d for d in disk if d.get("mount") != "/"] if disk_filtered: Decore.title("Disk Usage", width) - hdr = (f"{'MOUNTPOINT':<{PadDiskUsage.mount}}" - f"{'SIZE':>{PadDiskUsage.size}}" - f"{'USED':>{PadDiskUsage.used}}" - f"{'AVAIL':>{PadDiskUsage.avail}}" - f"{'USE%':>{PadDiskUsage.percent}}") - print(Decore.invert(hdr)) + disk_table = SimpleTable([ + Column('MOUNTPOINT', flexible=True), + Column('SIZE', 'right'), + Column('USED', 'right'), + Column('AVAIL', 'right'), + Column('USE%', 'right') + ], min_width=width) + for d in disk_filtered: - mount = d.get("mount", "?") - size = d.get("size", "?") - used = d.get("used", "?") - avail = d.get("available", "?") - percent = d.get("percent", "?") - print(f"{mount:<{PadDiskUsage.mount}}" - f"{size:>{PadDiskUsage.size}}" - f"{used:>{PadDiskUsage.used}}" - f"{avail:>{PadDiskUsage.avail}}" - f"{percent:>{PadDiskUsage.percent}}") + disk_table.row( + d.get("mount", "?"), + d.get("size", "?"), + d.get("used", "?"), + d.get("available", "?"), + d.get("percent", "?") + ) + disk_table.print() def show_dhcp_server(json, stats): diff --git a/src/statd/python/yanger/ietf_system.py b/src/statd/python/yanger/ietf_system.py index 5cff7ed7..0ca57ef3 100644 --- a/src/statd/python/yanger/ietf_system.py +++ b/src/statd/python/yanger/ietf_system.py @@ -183,22 +183,21 @@ def add_services(out): services = [] for d in data: - if "pid" not in d or "status" not in d or "identity" not in d or "description" not in d: + try: + services.append({ + "pid": d["pid"], + "name": d["identity"], + "status": d["status"], + "description": d["description"], + "statistics": { + "memory-usage": str(d.get("memory", 0)), + "uptime": str(d.get("uptime", 0)), + "restart-count": int(d.get("restarts", 0)) + } + }) + except KeyError: continue - entry = { - "pid": d["pid"], - "name": d["identity"], - "status": d["status"], - "description": d["description"], - "statistics": { - "memory-usage": str(d.get("memory", 0)), - "uptime": str(d.get("uptime", 0)), - "restart-count": int(d.get("restarts", 0)) - } - } - services.append(entry) - insert(out, "infix-system:services", "service", services) def add_software(out): diff --git a/test/case/statd/system/cli/show-hardware b/test/case/statd/system/cli/show-hardware index 6d1312e8..cfe9da1e 100644 --- a/test/case/statd/system/cli/show-hardware +++ b/test/case/statd/system/cli/show-hardware @@ -1,11 +1,11 @@ -HARDWARE COMPONENTS  -──────────────────────────────────────────────────────────────────────────────────────────────────── +HARDWARE COMPONENTS  +────────────────────────────────────────────────────────────── Board Information Model : Standard PC (i440FX + PIIX, 1996) Manufacturer : QEMU Base MAC Address : 00:a0:85:00:03:00 -──────────────────────────────────────────────────────────────────────────────────────────────────── +────────────────────────────────────────────────────────────── USB Ports -NAME STATE OPER  -USB1 locked enabled -USB2 locked enabled +NAME STATE OPER  +USB1 locked enabled +USB2 locked enabled