Compare commits

..
13 Commits
Author SHA1 Message Date
Tobias WaldekranzandGitHub eb7c031270 Merge pull request #1332 from kernelkit/kernel-v25.02.x
Bump kernel and update ChangeLog for v25.08.2
2025-12-19 13:27:25 +01:00
Joachim Wiberg 1d23f73bfd doc: update ChangeLog for v25.08.2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-19 11:36:54 +01:00
ael-botandJoachim Wiberg 119b92f94e Upgrade Linux kernel to 6.12.63
Backported from origin/main a999a93e63

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-19 11:36:54 +01:00
Tobias WaldekranzandGitHub 96fad208cb Merge pull request #1328 from kernelkit/cand/v25.08.x
Candidate backports for v25.08.2
2025-12-19 10:34:57 +01:00
Tobias WaldekranzandJoachim Wiberg 0a3879f521 kernel: Enable test_lockup module
This will be us the ability to test a hardware watchdog's ability to
trigger on different kinds of lockups.
2025-12-18 14:12:03 +01:00
Tobias WaldekranzandJoachim Wiberg 6f6bb412f9 aarch64: kernel: Enable SBSA watchdog
Usable by all Server Base System Architecture (SBSA) compliant SoCs,
e.g., CN9130.
2025-12-18 14:12:02 +01:00
Joachim Wiberg b04bae1ad6 board/*/linux_defconfig: panic on lockups and hung tasks
Turn on OOPS-to-panic, soft/hard lockup panic, hung-task panic, and
extra workqueue watchdog reporting. This makes latent stalls visible
instead of silently freezing, improving diagnosis of issues like the
recent resource-pressure lockup.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:12:02 +01:00
Joachim Wiberg 73edf1bbd6 board/common: log disk/mem/filenr resource usage every hour
Dec  8 15:22:44 ix-00-00-00 watchdogd[2599]: Memory usage: 195036 kB, cached: 69740 kB, total: 423628 kB
Dec  8 15:22:44 ix-00-00-00 watchdogd[2599]: File system /var usage: blocks 4710/52564 inodes 80/65456
Dec  8 15:22:44 ix-00-00-00 watchdogd[2599]: File descriptor usage: 640/34603

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:12:01 +01:00
Joachim Wiberg 2557eb4900 confd: add support for toggling OSPF debug logs
Backported from origin/main 7edc3c19f7

Fixes #1281

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:12:01 +01:00
Joachim Wiberg f71722c111 bin: add system support data collection
Backported from origin/main 5420fb01f2

Includes the latest DRAM ECC status, with/without sudo, and temperature.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:12:00 +01:00
Joachim Wiberg 5ea99680c8 bin: backport fixes to shell/cli copy command
This is a backport of the following commits from origin/main: 3b24fab
e6a04fb 92e80b4 ad96965 3e03ece 21256a8 b7d91e4 d26e311 0977ab4 f83fbc6

---

cli: fix 'copy FILE running-config' use-case

When copying to the running datastore we cannot use sr_copy_config(),
instead we must use sr_replace_config().  This fix covers both the case
of 'copy startup-config running-config' and 'copy FILE running-config'.

Fixes #1203

---

cli: add 'validate', or '-n', dry run to copy command

This commit adds config file validation to the copy command, discussed
in #373.  Allowing users to test their config files before restoring a
backup.  The feature could also be used for the automatic rollback when
downgrading to an earlier version of the OS.

Fixes #373

---

cli: fix copy to missing startup-config file

Fixes #981

---

cli: restrict copy and erase commands

This is a follow-up to PR #717 where path traversal protection was
discussed.  A year later and it's clear that having a user-friendly
copy tool in the shell is a good thing, but that we proably want to
restrict what it can do when called from the CLI.

A sanitize flag (-s) is added to control the behavior, when used in the
shell without -s, both commands act like traditional UNIX tools and do
assume . for relative paths, and allow ../, whereas when running from
the CLI only /media/ is allowed and otherwise files are assumed to be
in $HOME or /cfg

---

cli: sanitize regular file to file copy

The regular file-to-file copy, was missing calls to cfg_adjust(), this
commit fixes that and adds some helpful comments for each use-case.

Also, drop insecure mktemp() in favor of our own version which uses the
basename of the remote source file.

---

bin: add bash completion for copy command

Add bash completion for the common datastores, like we already do in the
CLI, and update the usage text accordingly.

Also, make sure to install to /usr/bin, not /bin since we've now merged
the hierarchies since a while back.

---

bin: copy: Refactor

copy() made some...creative...use of control flow that made it quite
difficult to follow.

Take a first priciples approach to simplify the logic.

---

bin: copy: Always get startup from sysrepo

This will make sure to apply NACM rules for all the data. It also
makes it possible for a luser access a subset of the data, even if
they to do not have read access to /cfg/startup-config.cfg.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:12:00 +01:00
Joachim Wiberg 55b038d999 .github: only set latest tag on .0 releases
Prevent patch releases from stealing the "latest" tag from newer minor
versions. Only vXX.YY and vXX.YY.0 releases should be marked as latest.

Fixes #1187

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:11:59 +01:00
Joachim Wiberg 83b55ee4d8 Follw-up to d7abe46, use correct compatible string for override
Actually disable iitod on Styx DCP-SC-28p to work around #670, this
prohibits software control of LEDs, leaving the default HW control,
which has proven more stable on this platform.

Backported from origin/main b0dce8a05e

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-18 14:11:31 +01:00
60 changed files with 1722 additions and 387 deletions
+1 -1
View File
@@ -125,7 +125,7 @@ jobs:
if echo $ver | grep -qE 'v[0-9.]+(-alpha|-beta|-rc)[0-9]*'; then
echo "pre=true" >> $GITHUB_OUTPUT
echo "latest=false" >> $GITHUB_OUTPUT
elif echo $ver | grep -qE '^v[0-9.]+\.[0-9.]+(\.[0-9]+)?$'; then
elif echo $ver | grep -qE '^v[0-9]+\.[0-9]+(\.0)?$'; then
echo "pre=false" >> $GITHUB_OUTPUT
echo "latest=true" >> $GITHUB_OUTPUT
echo "cat=Releases" >> $GITHUB_OUTPUT
+9 -1
View File
@@ -401,6 +401,7 @@ CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_SYSFS=y
CONFIG_SOFT_WATCHDOG=y
CONFIG_GPIO_WATCHDOG=y
CONFIG_ARM_SBSA_WATCHDOG=y
CONFIG_ARMADA_37XX_WATCHDOG=y
CONFIG_I6300ESB_WDT=y
CONFIG_MFD_MAX77620=y
@@ -549,7 +550,14 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_FS=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_TIMEOUT=20
CONFIG_DETECT_HUNG_TASK=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_HARDLOCKUP_DETECTOR=y
CONFIG_HARDLOCKUP_DETECTOR_PREFER_BUDDY=y
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
# CONFIG_SCHED_DEBUG is not set
# CONFIG_RCU_TRACE is not set
CONFIG_FUNCTION_TRACER=y
@@ -0,0 +1,161 @@
#!/bin/sh
set -e
# Build a map of phandle -> device tree node path for all PHY nodes
build_phandle_map()
{
for phy_node in /sys/firmware/devicetree/base/cp*/config-space*/mdio*/switch*/mdio/ethernet-phy@* \
/sys/firmware/devicetree/base/cp*/config-space*/mdio*/ethernet-phy@*; do
[ -f "$phy_node/phandle" ] || continue
phandle=$(od -An -t x4 -N 4 "$phy_node/phandle" 2>/dev/null | tr -d ' ')
if [ -n "$phandle" ]; then
echo "$phandle:$phy_node"
fi
done
}
build_phy_map()
{
phandle_map=$(build_phandle_map)
# Build a mapping of PHY of_node path -> interface name
for iface in /sys/class/net/*; do
[ -d "$iface" ] || continue
iface_name=$(basename "$iface")
# Try regular phydev approach first (for non-DSA interfaces)
if [ -L "$iface/phydev/of_node" ]; then
phy_of_node=$(readlink -f "$iface/phydev/of_node" 2>/dev/null)
if [ -n "$phy_of_node" ]; then
echo "$phy_of_node:$iface_name"
continue
fi
fi
# For DSA interfaces, resolve via of_node's phy-handle
if [ -L "$iface/of_node" ]; then
iface_of_node=$(readlink -f "$iface/of_node" 2>/dev/null)
[ -n "$iface_of_node" ] || continue
# Try to read phy-handle property (4-byte phandle)
if [ -f "$iface_of_node/phy-handle" ]; then
phy_phandle=$(od -An -t x4 -N 4 "$iface_of_node/phy-handle" 2>/dev/null | tr -d ' ')
if [ -n "$phy_phandle" ]; then
# Look up the PHY node path from our phandle map
phy_of_node=$(echo "$phandle_map" | grep "^$phy_phandle:" | cut -d: -f2)
if [ -n "$phy_of_node" ]; then
echo "$phy_of_node:$iface_name"
fi
fi
fi
fi
done
}
zone_map()
{
type="$1"
case "$type" in
ap-ic-thermal)
echo "Application processor interconnect"
;;
ap-cpu[0-9]*-thermal)
cpu=${type#ap-cpu}
cpu=${cpu%-thermal}
echo "Application processor core $cpu"
;;
cp[0-9]*-ic-thermal)
cp=${type%%-*}
cp=${cp#cp}
echo "Communication processor $cp interconnect"
;;
*)
echo "$type"
;;
esac
}
thermal_zones()
{
echo "Thermal Zones"
echo "============="
echo
for zone in /sys/class/thermal/thermal_zone*; do
[ -d "$zone" ] || continue
name=$(basename "$zone")
type=$(cat "$zone/type" 2>/dev/null || echo "unknown")
data=$(cat "$zone/temp" 2>/dev/null)
desc=$(zone_map "$type")
if [ -n "$data" ] && [ "$data" != "N/A" ]; then
# Convert millidegrees to degrees Celsius
temp_c=$(awk "BEGIN {printf \"%.1f\", $data / 1000}")
printf "%-20s %8s°C %s\n" "$name" "$temp_c" "$desc"
else
printf "%-20s %8s %s\n" "$name" "N/A" "$desc"
fi
done
echo
}
hwmon()
{
tmpfile=$(mktemp)
echo "Hardware Monitors"
echo "================="
echo
phy_map=$(build_phy_map)
for hwmon in /sys/class/hwmon/hwmon*; do
[ -d "$hwmon" ] || continue
name=$(basename "$hwmon")
data=$(cat "$hwmon/temp1_input" 2>/dev/null)
# Try to find the associated network interface
iface=
if [ -L "$hwmon/of_node" ]; then
hwmon_of_node=$(readlink -f "$hwmon/of_node" 2>/dev/null)
if [ -n "$hwmon_of_node" ]; then
iface=$(echo "$phy_map" | grep "^$hwmon_of_node:" | cut -d: -f2)
fi
fi
if [ -n "$iface" ]; then
description="Phy $iface temperature"
else
description="N/A"
fi
if [ -n "$data" ] && [ "$data" != "N/A" ]; then
# Convert millidegrees to degrees Celsius
temp_c=$(awk "BEGIN {printf \"%.1f\", $data / 1000}")
# Format: sortkey|hwmon|temp|description (sortkey for natural sort by interface)
printf "%s|%-20s %8s°C %s\n" "$iface" "$name" "$temp_c" "$description" >> "$tmpfile"
else
printf "%s|%-20s %8s %s\n" "$iface" "$name" "N/A" "$description" >> "$tmpfile"
fi
done
# Sort by interface name naturally (e2 before e10), with N/A entries at the end
# Then strip the sort key before displaying
sort -V -t'|' -k1,1 "$tmpfile" | cut -d'|' -f2-
rm -f "$tmpfile"
echo
}
[ -n "$1" ] || { echo "usage: $0 OUT-DIR"; exit 1; }
work="$1"/system
mkdir -p "${work}"
thermal_zones > "${work}"/temperature.txt
hwmon >> "${work}"/temperature.txt
@@ -0,0 +1,26 @@
#!/bin/sh
set -e
ecc_stat()
{
local chan=
local base=
for chan in 0 1; do
base=$((0xf0020360 + 0x200 * chan))
echo "DRAM Channel $chan ECC Status"
echo -n " Log config: "; devmem $((base + 0x0)) 32
echo -n " 1b errors: "; devmem $((base + 0x4)) 32
echo -n " Info 0: "; devmem $((base + 0x8)) 32
echo -n " Info 1: "; devmem $((base + 0xc)) 32
echo
done
}
[ -n "$1" ] || { echo "usage: $0 OUT-DIR"; exit 1; }
work="$1"/marvell-cn913x
mkdir -p "${work}"
ecc_stat >"${work}"/ecc-stat
+33 -24
View File
@@ -90,9 +90,9 @@ reset-reason {
# Monitors file descriptor leaks based on /proc/sys/fs/file-nr
filenr {
# enabled = true
interval = 300
logmark = false
enabled = true
interval = 3600
logmark = true
warning = 0.9
critical = 1.0
# script = "/path/to/alt-reboot-action.sh"
@@ -102,33 +102,42 @@ filenr {
# The script is called with fsmon as the first argument and there
# are two environment variables FSMON_NAME, for the monitored path,
# and FSMON_TYPE indicating either 'blocks' or 'inodes'.
#fsmon /var {
# enabled = true
# interval = 300
# logmark = false
# warning = 0.95
# critical = 1.0
# script = "/path/to/alt-reboot-action.sh"
#}
# Monitors load average based on sysinfo() from /proc/loadavg
# The level is composed from the average of the 1 and 5 min marks.
loadavg {
# enabled = true
interval = 300
logmark = false
warning = 1.0
critical = 2.0
fsmon /var {
enabled = true
interval = 3600
logmark = true
warning = 0.95
critical = 1.0
# script = "/path/to/alt-reboot-action.sh"
}
fsmon /tmp {
enabled = true
interval = 3600
logmark = true
warning = 0.95
critical = 1.0
# script = "/path/to/alt-reboot-action.sh"
}
# Monitors load average based on sysinfo() from /proc/loadavg
# The level is composed from the average of the 1 and 5 min marks.
#loadavg {
# enabled = true
# interval = 300
# logmark = true
# warning = 1.0
# critical = 2.0
# script = "/path/to/alt-reboot-action.sh"
#}
# Monitors free RAM based on data from /proc/meminfo
meminfo {
# enabled = true
interval = 300
logmark = false
enabled = true
interval = 3600
logmark = true
warning = 0.9
critical = 0.95
critical = 0.97
# script = "/path/to/alt-reboot-action.sh"
}
+9 -1
View File
@@ -461,8 +461,16 @@ CONFIG_DEBUG_FS=y
# CONFIG_SLUB_DEBUG is not set
CONFIG_DEBUG_RODATA_TEST=y
CONFIG_DEBUG_WX=y
CONFIG_SOFTLOCKUP_DETECTOR=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_TIMEOUT=20
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_HARDLOCKUP_DETECTOR=y
CONFIG_HARDLOCKUP_DETECTOR_PREFER_BUDDY=y
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
# CONFIG_SCHED_DEBUG is not set
CONFIG_STACKTRACE=y
CONFIG_RCU_CPU_STALL_TIMEOUT=60
+7 -1
View File
@@ -266,7 +266,13 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_FS=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_TIMEOUT=20
CONFIG_DETECT_HUNG_TASK=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_HARDLOCKUP_DETECTOR=y
CONFIG_HARDLOCKUP_DETECTOR_PREFER_BUDDY=y
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_WQ_CPU_INTENSIVE_REPORT=y
CONFIG_TEST_LOCKUP=m
CONFIG_FUNCTION_TRACER=y
CONFIG_UNWINDER_FRAME_POINTER=y
+7 -1
View File
@@ -27,7 +27,7 @@ BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build
BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image.sh"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.44"
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.63"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/linux_defconfig"
BR2_LINUX_KERNEL_INSTALL_TARGET=y
@@ -111,6 +111,12 @@ BR2_PACKAGE_RAUC_JSON=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
BR2_PACKAGE_WATCHDOGD_GENERIC=y
BR2_PACKAGE_WATCHDOGD_LOADAVG=y
BR2_PACKAGE_WATCHDOGD_FILENR=y
BR2_PACKAGE_WATCHDOGD_MEMINFO=y
BR2_PACKAGE_WATCHDOGD_FSMON=y
BR2_PACKAGE_WATCHDOGD_TEMPMON
BR2_PACKAGE_LESS=y
BR2_PACKAGE_MG=y
BR2_PACKAGE_NANO=y
+7 -1
View File
@@ -27,7 +27,7 @@ BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build
BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image.sh"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.44"
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.63"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/linux_defconfig"
BR2_LINUX_KERNEL_INSTALL_TARGET=y
@@ -94,6 +94,12 @@ BR2_PACKAGE_RAUC_JSON=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
BR2_PACKAGE_WATCHDOGD_GENERIC=y
BR2_PACKAGE_WATCHDOGD_LOADAVG=y
BR2_PACKAGE_WATCHDOGD_FILENR=y
BR2_PACKAGE_WATCHDOGD_MEMINFO=y
BR2_PACKAGE_WATCHDOGD_FSMON=y
BR2_PACKAGE_WATCHDOGD_TEMPMON
BR2_PACKAGE_LESS=y
BR2_PACKAGE_MG=y
BR2_PACKAGE_NANO=y
+6
View File
@@ -123,6 +123,12 @@ BR2_PACKAGE_RAUC_JSON=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
BR2_PACKAGE_WATCHDOGD_GENERIC=y
BR2_PACKAGE_WATCHDOGD_LOADAVG=y
BR2_PACKAGE_WATCHDOGD_FILENR=y
BR2_PACKAGE_WATCHDOGD_MEMINFO=y
BR2_PACKAGE_WATCHDOGD_FSMON=y
BR2_PACKAGE_WATCHDOGD_TEMPMON
BR2_PACKAGE_LESS=y
BR2_PACKAGE_MG=y
BR2_PACKAGE_NANO=y
+7 -1
View File
@@ -26,7 +26,7 @@ BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INF
BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image.sh"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.44"
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.63"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig"
BR2_LINUX_KERNEL_INSTALL_TARGET=y
@@ -109,6 +109,12 @@ BR2_PACKAGE_RAUC_JSON=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
BR2_PACKAGE_WATCHDOGD_GENERIC=y
BR2_PACKAGE_WATCHDOGD_LOADAVG=y
BR2_PACKAGE_WATCHDOGD_FILENR=y
BR2_PACKAGE_WATCHDOGD_MEMINFO=y
BR2_PACKAGE_WATCHDOGD_FSMON=y
BR2_PACKAGE_WATCHDOGD_TEMPMON
BR2_PACKAGE_LESS=y
BR2_PACKAGE_MG=y
BR2_PACKAGE_NANO=y
+7 -1
View File
@@ -27,7 +27,7 @@ BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INF
BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image.sh"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.44"
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.63"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig"
BR2_LINUX_KERNEL_INSTALL_TARGET=y
@@ -93,6 +93,12 @@ BR2_PACKAGE_RAUC_JSON=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
BR2_PACKAGE_WATCHDOGD_GENERIC=y
BR2_PACKAGE_WATCHDOGD_LOADAVG=y
BR2_PACKAGE_WATCHDOGD_FILENR=y
BR2_PACKAGE_WATCHDOGD_MEMINFO=y
BR2_PACKAGE_WATCHDOGD_FSMON=y
BR2_PACKAGE_WATCHDOGD_TEMPMON=y
BR2_PACKAGE_LESS=y
BR2_PACKAGE_MG=y
BR2_PACKAGE_NANO=y
+27
View File
@@ -3,6 +3,33 @@ Change Log
All notable changes to the project are documented in this file.
[v25.08.2][] - 2025-12-19
-------------------------
### Changes
- Upgrade Linux kernel to 6.12.63 (LTS)
- Enable workaround for issue #670 by disabling iitod on styx platform. This
prohibits software control of LEDs, leaving the default HW control, which
has proven more stable on this platform
- Add support for configurable OSPF debug logging, issue #1281. Debug options
can now be enabled per category (bfd, packet, ism, nsm, default-information,
nssa). All debug options are disabled by default to prevent log flooding in
production environments. See the documentation for usage examples
- Add support data collection script, useful when troubleshooting issues on
deployed systems. Gathers system information, logs, and more. Issue #1287
- Enable kernel panic on lockups + hung tasks => console log + reboot. Also,
enable watchdogd resource monitors, logs: memory/file system + descriptor
usage. Issue #1318
- Enable CN9130 HW watchdog, and kernel `test_lockup` module, issue #1320
### Fixes
- Fix #981: copying any file, including `running-config`, to the persistent
back-end store for `startup-config`, does not take
- Fix #1203: copying any file, including `startup-config`, to `running-config`
does not take
[v25.08.1][] - 2025-10-03
-------------------------
+1 -1
View File
@@ -11,7 +11,7 @@ BIN_LICENSE = BSD-3-Clause
BIN_LICENSE_FILES = LICENSE
BIN_REDISTRIBUTE = NO
BIN_DEPENDENCIES = sysrepo libite
BIN_CONF_OPTS = --prefix= --disable-silent-rules
BIN_CONF_OPTS = --disable-silent-rules
BIN_AUTORECONF = YES
define BIN_CONF_ENV
@@ -1,4 +1,4 @@
From b4f8d056cfbf3af9116952f5f03ed2bfba13fcbb Mon Sep 17 00:00:00 2001
From c325dce2343f549b188f050ed6cc1fb6bdad824d Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 12 Mar 2024 10:27:24 +0100
Subject: [PATCH 01/30] [FIX] net: dsa: mv88e6xxx: Fix timeout on waiting for
@@ -1,4 +1,4 @@
From b9bf83b003601dc306d3a2cd7dd51f2795bbb451 Mon Sep 17 00:00:00 2001
From 1e235f31e3a37e1077a31136f9f1cbfcc41e2d1a Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 27 Mar 2024 15:52:43 +0100
Subject: [PATCH 02/30] net: dsa: mv88e6xxx: Improve indirect register access
@@ -1,4 +1,4 @@
From c6171e8b4f0a7acb1a8d57e9fe8900e132917e82 Mon Sep 17 00:00:00 2001
From 4cde6e8574c27bf2d1bb397c575e1d3a6d7c6e59 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Mon, 22 Apr 2024 23:18:01 +0200
Subject: [PATCH 03/30] net: dsa: mv88e6xxx: Honor ports being managed via
@@ -1,4 +1,4 @@
From 8138197f76730926e96d94b532d0215de20470af Mon Sep 17 00:00:00 2001
From 19290bb9a333978f38cf5790b5b568b2edae7824 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 24 Apr 2024 22:41:04 +0200
Subject: [PATCH 04/30] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user
@@ -1,4 +1,4 @@
From a02245c2aa6783aae016266ef3f226f85bf402da Mon Sep 17 00:00:00 2001
From 8a6823163110439b57cb88b9a370148fa34c6cd9 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 Nov 2023 19:44:32 +0100
Subject: [PATCH 05/30] net: dsa: mv88e6xxx: Add LED infrastructure
@@ -1,4 +1,4 @@
From cf091bc712f67eaa733194de9b84003d27221053 Mon Sep 17 00:00:00 2001
From dfdd2ba23cba1c48fe84201fd6ead98e5bda8bb3 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 Nov 2023 21:59:35 +0100
Subject: [PATCH 06/30] net: dsa: mv88e6xxx: Add LED support for 6393X
@@ -1,4 +1,4 @@
From 630151bff3fa0f6ef64c326ca874ef9858bdcb8a Mon Sep 17 00:00:00 2001
From 1874e05ed2d60863bc0d077596b4073f03fda1dc Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 28 May 2024 10:38:42 +0200
Subject: [PATCH 07/30] net: dsa: tag_dsa: Use tag priority as initial
@@ -1,4 +1,4 @@
From a87bfa946479faea1dce4d9ee1034922263dc758 Mon Sep 17 00:00:00 2001
From 001a92a13d26f1870b29b614120a1d1e2943906b Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 16 Jan 2024 16:00:55 +0100
Subject: [PATCH 08/30] net: dsa: Support MDB memberships whose L2 addresses
@@ -1,4 +1,4 @@
From 8465eec4cbeccb7eb683820e8e4f4601ce05509f Mon Sep 17 00:00:00 2001
From 7955dd42748c39e2dec0dd74b2275d36719470ea Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 21 Mar 2024 19:12:15 +0100
Subject: [PATCH 09/30] net: dsa: Support EtherType based priority overrides
@@ -1,4 +1,4 @@
From 6809a220eb75ef2ac5eed033028cd0a98948cca3 Mon Sep 17 00:00:00 2001
From 38de29685c16dca3a374d4762b34e5011335e6f7 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Fri, 22 Mar 2024 16:15:43 +0100
Subject: [PATCH 10/30] net: dsa: mv88e6xxx: Support EtherType based priority
@@ -1,4 +1,4 @@
From ecc33c81e71000a7db4abf1fd769783528883c0a Mon Sep 17 00:00:00 2001
From 3a1a9d89a6cd1e345ff40802ba06c4a0d7786b82 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 28 May 2024 11:04:22 +0200
Subject: [PATCH 11/30] net: dsa: mv88e6xxx: Add mqprio qdisc support
@@ -1,4 +1,4 @@
From 446ee49b7ecef20c17fa7fd8c2d196236fdbeb0f Mon Sep 17 00:00:00 2001
From 9ca8684220e48d43e8db03dbf58c84a7248af196 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 29 May 2024 13:20:41 +0200
Subject: [PATCH 12/30] net: dsa: mv88e6xxx: Use VLAN prio over IP when both
@@ -1,4 +1,4 @@
From 379d6362066d1e3944f2610b6769bcb170f5cfb5 Mon Sep 17 00:00:00 2001
From 6a03b1751882137bf8008d151f2d85b3975defe7 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 26 Nov 2024 19:45:59 +0100
Subject: [PATCH 13/30] [FIX] net: dsa: mv88e6xxx: Trap locally terminated
@@ -1,4 +1,4 @@
From c1c803aae5530c6ad8cba5e1b690309343d70581 Mon Sep 17 00:00:00 2001
From 8c1a4589df153e8a0029f5a44616c1e46f17a8d0 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 19 Sep 2023 18:38:10 +0200
Subject: [PATCH 14/30] net: phy: marvell10g: Support firmware loading on
@@ -1,4 +1,4 @@
From f78961c99e9027e093d5fc4b16b890acb0f4c906 Mon Sep 17 00:00:00 2001
From b252e0465607dd509b73e21404937875a9a11de4 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 21 Nov 2023 20:15:24 +0100
Subject: [PATCH 15/30] net: phy: marvell10g: Fix power-up when strapped to
@@ -1,4 +1,4 @@
From f91ec427a88888262de0e670ec1d6d5a2dd1ea19 Mon Sep 17 00:00:00 2001
From 361497a386716b9b9ab0822c5cb99165def31691 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 15 Nov 2023 20:58:42 +0100
Subject: [PATCH 16/30] net: phy: marvell10g: Add LED support for 88X3310
@@ -1,4 +1,4 @@
From 4bd8aa58f2cf82d8df62624a030ca822d1991e3f Mon Sep 17 00:00:00 2001
From bf56b0154d9ad9d5ab071596f67e533bd12da08d Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 12 Dec 2023 09:51:05 +0100
Subject: [PATCH 17/30] net: phy: marvell10g: Support LEDs tied to a single
@@ -1,4 +1,4 @@
From 462548c531a48ea97f16fe02f8214304bf5717eb Mon Sep 17 00:00:00 2001
From d73b87e8f153a1ec227ee5d764a223d71546d8d6 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 27 Mar 2024 10:10:19 +0100
Subject: [PATCH 18/30] net: phy: Do not resume PHY when attaching
@@ -1,4 +1,4 @@
From 550d6f5a0463f39f2959415495b86c79816d4055 Mon Sep 17 00:00:00 2001
From a16724e0f5ad607c5a066005870fffc90f5954e3 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 4 Mar 2024 16:47:28 +0100
Subject: [PATCH 19/30] net: bridge: avoid classifying unknown multicast as
@@ -1,4 +1,4 @@
From b1e6fb8acc320967fa2435dda641ec2031fd55cc Mon Sep 17 00:00:00 2001
From 66482102bdf087d6fdc14cb5ffada09e871736e8 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Tue, 5 Mar 2024 06:44:41 +0100
Subject: [PATCH 20/30] net: bridge: Ignore router ports when forwarding L2
@@ -1,4 +1,4 @@
From c0de0c952b98eea283925a7f15255a7501c7343c Mon Sep 17 00:00:00 2001
From dca2d29cf6473b4d01169b655801ab018edc6e87 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 4 Apr 2024 16:36:30 +0200
Subject: [PATCH 21/30] net: bridge: drop delay for applying strict multicast
@@ -1,4 +1,4 @@
From 778143529c0df2eeada5e2c682156d62103b92bc Mon Sep 17 00:00:00 2001
From 01ebb6534cdb82572dcd8b0c2ad84a96f7e3a80c Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Thu, 16 May 2024 14:51:54 +0200
Subject: [PATCH 22/30] net: bridge: Differentiate MDB additions from
@@ -1,4 +1,4 @@
From adc96c55eaea59edd98026c209f435238ceeac09 Mon Sep 17 00:00:00 2001
From 569aeb5b4438c403fba8e4ea64378a34cdb0c563 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Fri, 24 Nov 2023 23:29:55 +0100
Subject: [PATCH 23/30] nvmem: layouts: onie-tlv: Let device probe even when
@@ -1,4 +1,4 @@
From fecf7749e91bd1d2d54e2003daefe97ae449b04c Mon Sep 17 00:00:00 2001
From fd03c7f0132c458c7375d737f749d801a591aced Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 29 Apr 2024 15:14:51 +0200
Subject: [PATCH 24/30] usb: core: adjust log level for unauthorized devices
@@ -1,4 +1,4 @@
From c89e3e44747f2cddfaf47e0a55027a14579ca4ce Mon Sep 17 00:00:00 2001
From 0a112c6c854e7dba155d911c3ba72a3a2b6f206c Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 16 Jan 2025 12:35:12 +0100
Subject: [PATCH 25/30] net: dsa: mv88e6xxx: collapse disabled state into
@@ -1,4 +1,4 @@
From 34de4ecc90144fb98aae12cbc22c847eb0e1f92f Mon Sep 17 00:00:00 2001
From 5c939708c5195db4e0fc65f5e8d613cca5c6be26 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Wed, 12 Feb 2025 22:03:14 +0100
Subject: [PATCH 26/30] net: dsa: mv88e6xxx: Only activate LAG offloading when
@@ -1,4 +1,4 @@
From 10df1d67f996439e1f4dd41ec761c94631383930 Mon Sep 17 00:00:00 2001
From a2f7ac6427a3f2c0cc7783c67359ae956e374c26 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Sun, 11 Aug 2024 11:27:35 +0200
Subject: [PATCH 27/30] net: usb: r8152: add r8153b support for link/activity
@@ -1,4 +1,4 @@
From cd45846aea45d8a51d40472ee5993de43a98d4a6 Mon Sep 17 00:00:00 2001
From 7fa8a878f161e2f68ae8ac0fd1c2cb9e045162cb Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Sun, 10 Aug 2025 18:52:54 +0200
Subject: [PATCH 28/30] arm64: dts: mediatek: mt7986a: rename BPi R3 ports to
@@ -1,4 +1,4 @@
From b978886aa20a68f9d5be378797fa45054c249905 Mon Sep 17 00:00:00 2001
From fa784fa2fc2c80b516e723f91a327a1acaa86363 Mon Sep 17 00:00:00 2001
From: Mattias Walström <lazzer@gmail.com>
Date: Wed, 20 Aug 2025 21:38:24 +0200
Subject: [PATCH 29/30] drm/panel-simple: Add a timing for the Raspberry Pi 7"
@@ -1,4 +1,4 @@
From b6b066a94032f06e669685208969cf00172a361e Mon Sep 17 00:00:00 2001
From 92e8c7e06ba5f256cc36df4d0da4f9dd5566405e Mon Sep 17 00:00:00 2001
From: Mattias Walström <lazzer@gmail.com>
Date: Thu, 21 Aug 2025 11:20:23 +0200
Subject: [PATCH 30/30] input:touchscreen:edt-ft5x06: Add polled mode
+2 -2
View File
@@ -1,2 +1,2 @@
# Calculated with ../utils/kernel-refresh.sh
sha256 b650210ed3027b224969d148aa377452a9aad3ae7f2851abedd31adfef16bdae linux-6.12.44.tar.xz
# Calculated with utils/kernel-refresh.sh
sha256 9502c5ffe4b894383c97abfccf74430a84732f04ee476b9c0d87635b29df7db3 linux-6.12.63.tar.xz
+7 -2
View File
@@ -2,12 +2,17 @@ DISTCLEANFILES = *~ *.d
ACLOCAL_AMFLAGS = -I m4
bin_PROGRAMS = copy erase files
sbin_SCRIPTS = support
# Bash completion
bashcompdir = $(datadir)/bash-completion/completions
dist_bashcomp_DATA = copy.bash
copy_SOURCES = copy.c util.c util.h
copy_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE
copy_CFLAGS = -W -Wall -Wextra
copy_CFLAGS += $(libite_CFLAGS) $(sysrepo_CFLAGS)
copy_LDADD = $(libite_LIBS) $(sysrepo_LIBS)
copy_CFLAGS += $(libite_CFLAGS) $(libyang_CFLAGS) $(sysrepo_CFLAGS)
copy_LDADD = $(libite_LIBS) $(libyang_LIBS) $(sysrepo_LIBS)
erase_SOURCES = erase.c util.c util.h
erase_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE
+3 -2
View File
@@ -15,6 +15,7 @@ AC_PROG_INSTALL
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([libite], [libite >= 2.5.0])
PKG_CHECK_MODULES([libyang], [libyang >= 3.0.0])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
# Misc variable replacements for below Summary
@@ -55,8 +56,8 @@ cat <<EOF
System environment....: ${sysconfig_path:-${sysconfig}}
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
Linker................: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
CFLAGS................: $libite_CFLAGS $sysrepo_CFLAGS
LIBS..................: $libite_LIBS $sysrepo_LIBS
CFLAGS................: $libite_CFLAGS $libyang_CFLAGS $sysrepo_CFLAGS
LIBS..................: $libite_LIBS $libyang_LIBS $sysrepo_LIBS
------------- Compiler version --------------
$($CC --version || true)
+93
View File
@@ -0,0 +1,93 @@
# bash completion for copy command
# SPDX-License-Identifier: ISC
_copy_completion()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Options for the copy command
opts="-h -n -q -s -t -u -v"
local datastores_dst="running-config startup-config"
local datastores_src="factory-config operational-state running-config"
case "${prev}" in
-t)
# Timeout expects a number, no completion
return 0
;;
-u)
# Username, could complete with getent passwd but keep it simple
return 0
;;
esac
# If current word starts with -, complete options
if [[ ${cur} == -* ]]; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
# Determine position (source or destination)
# Count non-option arguments before current position
local arg_count=0
local i
for ((i=1; i < COMP_CWORD; i++)); do
case "${COMP_WORDS[i]}" in
-h|-n|-q|-s|-v)
# Flag without argument
;;
-t|-u)
# Flag with argument, skip next word
((i++))
;;
-*)
# Unknown flag, might have argument
;;
*)
# Non-option argument
((arg_count++))
;;
esac
done
# Helper function to add non-hidden files/dirs, filtering out dotfiles unless explicitly requested
_add_files() {
local IFS=$'\n'
local files
# If user is explicitly typing a dotfile path, include dotfiles
if [[ ${cur} == .* || ${cur} == */.* ]]; then
files=( $(compgen -f -- "${cur}") )
else
# Only show non-hidden files and directories
files=( $(compgen -f -- "${cur}" | grep -v '/\.[^/]*$' | grep -v '^\.[^/]') )
fi
COMPREPLY+=( "${files[@]}" )
}
case ${arg_count} in
0)
# First argument (source): complete with files and all datastores including factory-config
COMPREPLY=( $(compgen -W "${datastores_src}" -- ${cur}) )
_add_files
;;
1)
# Second argument (destination): complete with files and limited datastores (no factory-config)
COMPREPLY=( $(compgen -W "${datastores_dst}" -- ${cur}) )
_add_files
;;
*)
# No more arguments expected
return 0
;;
esac
return 0
}
complete -F _copy_completion copy
+435 -230
View File
@@ -10,6 +10,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sysrepo.h>
#include <sysrepo/netconf_acm.h>
@@ -18,58 +19,29 @@
struct infix_ds {
char *name; /* startup-config, etc. */
char *sysrepocfg; /* ds name in sysrepocfg */
int datastore; /* sr_datastore_t and -1 */
int rw; /* read-write:1 or not:0 */
bool rw; /* read-write:1 or not:0 */
char *path; /* local path or NULL */
};
struct infix_ds infix_config[] = {
{ "startup-config", "startup", SR_DS_STARTUP, 1, "/cfg/startup-config.cfg" },
{ "running-config", "running", SR_DS_RUNNING, 1, NULL },
{ "candidate-config", "candidate", SR_DS_CANDIDATE, 1, NULL },
{ "operational-config", "operational", SR_DS_OPERATIONAL, 1, NULL },
{ "factory-config", "factory-default", SR_DS_FACTORY_DEFAULT, 0, NULL }
const struct infix_ds infix_config[] = {
{ "startup-config", SR_DS_STARTUP, true, "/cfg/startup-config.cfg" },
{ "running-config", SR_DS_RUNNING, true, NULL },
/* { "candidate-config", SR_DS_CANDIDATE, true, NULL }, */
{ "operational-state", SR_DS_OPERATIONAL, false, NULL },
{ "factory-config", SR_DS_FACTORY_DEFAULT, false, NULL }
};
static const char *prognm = "copy";
static const char *remote_user;
static int timeout;
/*
* Print sysrepo session errors followed by an optional string.
*/
static void emsg(sr_session_ctx_t *sess, const char *fmt, ...)
{
const sr_error_info_t *err = NULL;
va_list ap;
size_t i;
int rc;
if (!sess)
goto end;
rc = sr_session_get_error(sess, &err);
if ((rc != SR_ERR_OK) || !err)
goto end;
// Show the first error only. Because probably next errors are
// originated from internal sysrepo code but is not from subscribers.
// for (i = 0; i < err->err_count; i++)
for (i = 0; i < (err->err_count < 1 ? err->err_count : 1); i++)
fprintf(stderr, ERRMSG "%s\n", err->err[i].message);
end:
if (fmt) {
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
}
static int dry_run;
static int sanitize;
/*
* Current system user, same as sysrepo user
*/
static char *getuser(void)
static const char *getuser(void)
{
const struct passwd *pw;
uid_t uid;
@@ -163,7 +135,7 @@ static void set_owner(const char *fn, const char *user)
}
}
static const char *infix_ds(const char *text, struct infix_ds **ds)
static const char *infix_ds(const char *text, const struct infix_ds **ds)
{
size_t i, len = strlen(text);
@@ -174,209 +146,424 @@ static const char *infix_ds(const char *text, struct infix_ds **ds)
}
}
*ds = NULL;
return text;
}
static bool is_uri(const char *str)
{
return strstr(str, "://") != NULL;
}
static int copy(const char *src, const char *dst, const char *remote_user)
static char *mktmp(void)
{
struct infix_ds *srcds = NULL, *dstds = NULL;
char temp_file[20] = "/tmp/copy.XXXXXX";
const char *tmpfn = NULL;
sr_session_ctx_t *sess;
const char *fn = NULL;
sr_conn_ctx_t *conn;
const char *user;
char adjust[256];
mode_t oldmask;
int rc = 0;
char *path;
int fd;
path = strdup("/tmp/copy-XXXXXX");
if (!path)
goto err;
oldmask = umask(0077);
fd = mkstemp(path);
umask(oldmask);
if (fd < 0)
goto err;
close(fd);
return path;
err:
free(path);
return NULL;
}
static void rmtmp(const char *path)
{
if (remove(path)) {
if (errno == ENOENT)
return;
fprintf(stderr, ERRMSG "removal of temporary file %s failed\n", path);
}
}
static void sysrepo_print_error(sr_session_ctx_t *sess)
{
const sr_error_info_t *erri = NULL;
int err;
err = sr_session_get_error(sess, &erri);
if (err || !erri || !erri->err_count)
return;
fprintf(stderr, ERRMSG "%s (%d)\n", erri->err->message, erri->err->err_code);
}
static sr_session_ctx_t *sysrepo_session(const struct infix_ds *ds)
{
static sr_session_ctx_t *sess;
sr_subscription_ctx_t *sub = NULL;
const char *user = getuser();
sr_conn_ctx_t *conn = NULL;
int err;
if (!ds) {
if (!sess)
return NULL;
conn = sr_session_get_connection(sess);
sr_session_stop(sess);
sr_disconnect(conn);
return NULL;
}
if (!sess) {
err = sr_connect(0, &conn);
if (err != SR_ERR_OK) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "could not connect to %s\n", ds->name);
goto err;
}
/* Always open running, because sr_nacm_init() does not work
* against the factory DS.
*/
err = sr_session_start(conn, SR_DS_RUNNING, &sess);
if (err != SR_ERR_OK) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "%s session setup failed\n", ds->name);
goto err_disconnect;
}
err = sr_nacm_init(sess, 0, &sub);
if (err != SR_ERR_OK) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "%s NACM setup failed\n", ds->name);
goto err_stop;
}
err = sr_nacm_set_user(sess, user);
if (err != SR_ERR_OK) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "%s NACM setup for %s failed\n", ds->name, user);
goto err_nacm_destroy;
}
}
err = sr_session_switch_ds(sess, ds->datastore);
if (err) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "%s activation failed\n", ds->name);
return NULL;
}
return sess;
err_nacm_destroy:
sr_nacm_destroy();
err_stop:
sr_session_stop(sess);
err_disconnect:
sr_disconnect(conn);
err:
sess = NULL;
return NULL;
}
static int sysrepo_export(const struct infix_ds *ds, const char *path)
{
sr_session_ctx_t *sess;
sr_data_t *data;
int err;
sess = sysrepo_session(ds);
if (!sess)
return 1;
err = sr_get_data(sess, "/*", 0, timeout * 1000, SR_OPER_DEFAULT, &data);
if (err) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "retrieval of %s data failed\n", ds->name);
return err;
}
err = lyd_print_path(path, data->tree, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
sr_release_data(data);
if (err) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "failed to store %s data\n", ds->name);
return err;
}
return 0;
}
static int sysrepo_import(const struct infix_ds *ds, const char *path)
{
const struct ly_ctx *ly;
sr_session_ctx_t *sess;
struct lyd_node *data;
int err;
sess = sysrepo_session(ds);
if (!sess)
return 1;
ly = sr_acquire_context(sr_session_get_connection(sess));
err = lyd_parse_data_path(ly, path, LYD_JSON,
LYD_PARSE_NO_STATE | LYD_PARSE_ONLY |
LYD_PARSE_STORE_ONLY | LYD_PARSE_STRICT, 0, &data);
if (err) {
fprintf(stderr, ERRMSG "failed to parse %s data\n", ds->name);
goto out;
}
err = dry_run ? 0 : sr_replace_config(sess, NULL, data, timeout * 1000);
if (err) {
sysrepo_print_error(sess);
fprintf(stderr, ERRMSG "failed import %s data\n", ds->name);
}
out:
sr_release_context(sr_session_get_connection(sess));
return err ? 1 : 0;
/* return sysrepo_do(sysrepo_import_op, ds, path) ? 1 : 0; */
}
static int subprocess(char * const *argv)
{
int pid, status;
pid = fork();
if (!pid) {
execvp(argv[0], argv);
exit(1);
}
if (pid < 0)
return 1;
if (waitpid(pid, &status, 0) < 0)
return 1;
if (!WIFEXITED(status))
return 1;
return WEXITSTATUS(status);
}
static int curl(char *op, const char *path, const char *uri)
{
char *argv[] = {
"curl", "-L", op, NULL, NULL, NULL, NULL, NULL,
};
int err = 1;
argv[3] = strdup(path);
argv[4] = strdup(uri);
if (!(argv[3] && argv[4]))
goto out;
if (remote_user) {
argv[5] = strdup("-u");
argv[6] = strdup(remote_user);
if (!(argv[5] && argv[6]))
goto out;
}
err = subprocess(argv);
out:
free(argv[6]);
free(argv[5]);
free(argv[4]);
free(argv[3]);
return err;
}
static int curl_upload(const char *srcpath, const char *uri)
{
char upload[] = "-T";
if (curl(upload, srcpath, uri)) {
fprintf(stderr, ERRMSG "upload to %s failed\n", uri);
return 1;
}
return 0;
}
static int curl_download(const char *uri, const char *dstpath)
{
char download[] = "-o";
if (curl(download, dstpath, uri)) {
fprintf(stderr, ERRMSG "download of %s failed\n", uri);
return 1;
}
return 0;
}
static int cp(const char *srcpath, const char *dstpath)
{
char *argv[] = {
"cp", NULL, NULL, NULL,
};
int err = 1;
argv[1] = strdup(srcpath);
argv[2] = strdup(dstpath);
if (!(argv[1] && argv[2]))
goto out;
err = subprocess(argv);
if (err)
fprintf(stderr, ERRMSG "failed to save %s\n", dstpath);
out:
free(argv[2]);
free(argv[1]);
return err;
}
static int put(const char *srcpath, const char *dst,
const struct infix_ds *ds, const char *path)
{
int err = 0;
if (ds)
err = sysrepo_import(ds, srcpath);
else if (is_uri(dst))
err = curl_upload(srcpath, dst);
if (err)
return err;
if (path) {
err = cp(srcpath, path);
if (!err)
set_owner(path, getuser());
}
return 0;
}
static int get(const char *src, const struct infix_ds *ds, const char *path)
{
int err = 0;
if (ds)
err = sysrepo_export(ds, path);
else if (is_uri(src))
err = curl_download(src, path);
return err;
}
static int resolve_src(const char **src, const struct infix_ds **ds, char **path, bool *rm)
{
*src = infix_ds(*src, ds);
if (*ds || is_uri(*src)) {
*path = mktmp();
if (!*path)
return 1;
*rm = true;
return 0;
} else {
*path = cfg_adjust(*src, NULL, sanitize);
}
if (!*path) {
fprintf(stderr, ERRMSG "no such file %s.", *src);
return 1;
}
*rm = false;
return 0;
}
static int resolve_dst(const char **dst, const struct infix_ds **ds, char **path)
{
*dst = infix_ds(*dst, ds);
if (*ds) {
if (!(*ds)->rw) {
fprintf(stderr, ERRMSG "%s is not writable", (*ds)->name);
return 1;
}
if (!(*ds)->path)
return 0;
*path = strdup((*ds)->path);
} else if (is_uri(*dst)) {
return 0;
} else {
*path = cfg_adjust(*dst, NULL, sanitize);
}
if (!*path) {
fprintf(stderr, ERRMSG "no such file: %s", *dst);
return 1;
}
if (!*ds && !access(*path, F_OK) && !yorn("Overwrite existing file %s", *path)) {
fprintf(stderr, "OK, aborting.\n");
return 1;
}
return 0;
}
static int copy(const char *src, const char *dst)
{
char *srcpath = NULL, *dstpath = NULL;
const struct infix_ds *srcds, *dstds;
bool rmsrc = false;
mode_t oldmask;
int err = 1;
/* rw for user and group only */
oldmask = umask(0006);
src = infix_ds(src, &srcds);
if (!src)
goto err;
dst = infix_ds(dst, &dstds);
if (!dst)
goto err;
if (!strcmp(src, dst)) {
fprintf(stderr, ERRMSG "source and destination are the same, aborting.\n");
goto err;
}
user = getuser();
err = resolve_src(&src, &srcds, &srcpath, &rmsrc);
if (err)
goto err;
/* 1. Regular ds copy */
if (srcds && dstds) {
/* Ensure the dst ds is writable */
if (!dstds->rw) {
fprintf(stderr, ERRMSG "not possible to write to \"%s\", skipping.\n", dst);
rc = 1;
goto err;
}
err = resolve_dst(&dst, &dstds, &dstpath);
if (err)
goto err;
if (sr_connect(SR_CONN_DEFAULT, &conn)) {
fprintf(stderr, ERRMSG "connection to datastore failed\n");
rc = 1;
goto err;
}
err = get(src, srcds, srcpath);
if (err)
goto err;
sr_log_syslog("klishd", SR_LL_WRN);
if (sr_session_start(conn, dstds->datastore, &sess)) {
fprintf(stderr, ERRMSG "unable to open transaction to %s\n", dst);
} else {
sr_nacm_set_user(sess, user);
rc = sr_copy_config(sess, NULL, srcds->datastore, timeout * 1000);
if (rc)
emsg(sess, ERRMSG "unable to copy configuration, err %d: %s\n",
rc, sr_strerror(rc));
else
set_owner(dstds->path, user);
}
rc = sr_disconnect(conn);
if (!srcds->path || !dstds->path)
goto err; /* done, not an error */
/* allow copy factory startup */
}
if (srcds) {
/* 2. Export from a datastore somewhere else */
if (strstr(dst, "://")) {
if (srcds->path)
fn = srcds->path;
else {
snprintf(adjust, sizeof(adjust), "/tmp/%s.cfg", srcds->name);
fn = tmpfn = adjust;
rc = systemf("sysrepocfg -d %s -X%s -f json", srcds->sysrepocfg, fn);
}
if (rc)
fprintf(stderr, ERRMSG "failed exporting %s to %s\n", src, fn);
else {
rc = systemf("curl %s -LT %s %s", remote_user, fn, dst);
if (rc)
fprintf(stderr, ERRMSG "failed uploading %s to %s\n", src, dst);
else
set_owner(dst, user);
}
goto err;
}
if (dstds && dstds->path)
fn = dstds->path;
else
fn = cfg_adjust(dst, src, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid destination path.\n");
rc = -1;
goto err;
}
if (!access(fn, F_OK) && !yorn("Overwrite existing file %s", fn)) {
fprintf(stderr, "OK, aborting.\n");
return 0;
}
if (srcds->path)
rc = systemf("cp %s %s", srcds->path, fn);
else
rc = systemf("sysrepocfg -d %s -X%s -f json", srcds->sysrepocfg, fn);
if (rc)
fprintf(stderr, ERRMSG "failed copy %s to %s\n", src, fn);
else
set_owner(fn, user);
} else if (dstds) {
if (!dstds->sysrepocfg) {
fprintf(stderr, ERRMSG "not possible to import to this datastore.\n");
rc = 1;
goto err;
}
if (!dstds->rw) {
fprintf(stderr, ERRMSG "not possible to write to %s", dst);
goto err;
}
/* 3. Import from somewhere to a datastore */
if (strstr(src, "://")) {
tmpfn = mktemp(temp_file);
fn = tmpfn;
} else {
fn = cfg_adjust(src, NULL, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid source file location.\n");
rc = 1;
goto err;
}
}
if (tmpfn)
rc = systemf("curl %s -Lo %s %s", remote_user, fn, src);
if (rc) {
fprintf(stderr, ERRMSG "failed downloading %s", src);
} else {
rc = systemf("sysrepocfg -d %s -I%s -f json", dstds->sysrepocfg, fn);
if (rc)
fprintf(stderr, ERRMSG "failed loading %s from %s", dst, src);
}
} else {
if (strstr(src, "://") && strstr(dst, "://")) {
fprintf(stderr, ERRMSG "copy from remote to remote is not supported.\n");
goto err;
}
if (strstr(src, "://")) {
fn = cfg_adjust(dst, src, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid destination file location.\n");
rc = 1;
goto err;
}
if (!access(fn, F_OK)) {
if (!yorn("Overwrite existing file %s", fn)) {
fprintf(stderr, "OK, aborting.\n");
return 0;
}
}
rc = systemf("curl %s -Lo %s %s", remote_user, fn, src);
} else if (strstr(dst, "://")) {
fn = cfg_adjust(src, NULL, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid source file location.\n");
rc = 1;
goto err;
}
if (access(fn, F_OK))
fprintf(stderr, ERRMSG "no such file %s, aborting.", fn);
else
rc = systemf("curl %s -LT %s %s", remote_user, fn, dst);
} else {
if (!access(dst, F_OK)) {
if (!yorn("Overwrite existing file %s", dst)) {
fprintf(stderr, "OK, aborting.\n");
return 0;
}
}
rc = systemf("cp %s %s", src, dst);
}
}
err = put(srcpath, dst, dstds, dstpath);
err:
if (tmpfn)
rc = remove(tmpfn);
/* If either src or dst came from sysrepo, close the session */
sysrepo_session(NULL);
sync(); /* ensure command is flushed to disk */
if (rmsrc)
rmtmp(srcpath);
free(dstpath);
free(srcpath);
sync();
umask(oldmask);
return rc;
return err;
}
static int usage(int rc)
@@ -384,30 +571,48 @@ static int usage(int rc)
printf("Usage: %s [OPTIONS] SRC DST\n"
"\n"
"Options:\n"
" -h This help text\n"
" -u USER Username for remote commands, like scp\n"
" -t SEEC Timeout for the operation, or default %d sec\n"
" -v Show version\n", prognm, timeout);
" -h This help text\n"
" -n Dry-run, validate configuration without applying\n"
" -s Sanitize paths for CLI use (restrict path traversal)\n"
" -t SEC Timeout for the operation, or default %d sec\n"
" -u USER Username for remote commands, like scp\n"
" -v Show version\n"
"\n"
"Files:\n"
" SRC JSON configuration file, or a datastore\n"
" DST A file or datastore, except factory-config\n"
"\n"
"Datastores:\n"
" running-config The running datastore, current active config\n"
" startup-config The non-volatile config used at startup\n"
" factory-config The device's factory default configuration\n"
"\n", prognm, timeout);
return rc;
}
int main(int argc, char *argv[])
{
const char *user = NULL, *src = NULL, *dst = NULL;
const char *src = NULL, *dst = NULL;
int c;
timeout = fgetint("/etc/default/confd", "=", "CONFD_TIMEOUT");
while ((c = getopt(argc, argv, "ht:u:v")) != EOF) {
while ((c = getopt(argc, argv, "hnst:u:v")) != EOF) {
switch(c) {
case 'h':
return usage(0);
case 'n':
dry_run = 1;
break;
case 's':
sanitize = 1;
break;
case 't':
timeout = atoi(optarg);
break;
case 'u':
user = optarg;
remote_user = optarg;
break;
case 'v':
puts(PACKAGE_VERSION);
@@ -424,5 +629,5 @@ int main(int argc, char *argv[])
src = argv[optind++];
dst = argv[optind++];
return copy(src, dst, user);
return copy(src, dst);
}
+26 -24
View File
@@ -4,40 +4,38 @@
#include <alloca.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static const char *prognm = "erase";
static int sanitize;
static int do_erase(const char *path)
static int do_erase(const char *name)
{
char *fn;
char *path;
int rc = 0;
if (access(path, F_OK)) {
size_t len = strlen(path) + 10;
fn = alloca(len);
if (!fn) {
fprintf(stderr, ERRMSG "failed allocating memory.\n");
return -1;
}
cfg_adjust(path, NULL, fn, len);
} else
fn = (char *)path;
if (!yorn("Remove %s, are you sure", fn))
return 0;
if (remove(fn)) {
fprintf(stderr, ERRMSG "failed removing %s: %s\n", fn, strerror(errno));
return -1;
path = cfg_adjust(name, NULL, sanitize);
if (!path) {
fprintf(stderr, ERRMSG "file not found.\n");
rc = 1;
goto out;
}
return 0;
if (!yorn("Remove %s, are you sure?", path))
goto out;
if (remove(path)) {
fprintf(stderr, ERRMSG "failed removing %s: %s\n", path, strerror(errno));
rc = 11;
}
out:
free(path);
return rc;
}
static int usage(int rc)
@@ -46,6 +44,7 @@ static int usage(int rc)
"\n"
"Options:\n"
" -h This help text\n"
" -s Sanitize paths for CLI use (restrict path traversal)\n"
" -v Show version\n", prognm);
return rc;
@@ -55,10 +54,13 @@ int main(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "hv")) != EOF) {
while ((c = getopt(argc, argv, "hsv")) != EOF) {
switch(c) {
case 'h':
return usage(0);
case 's':
sanitize = 1;
break;
case 'v':
puts(PACKAGE_VERSION);
return 0;
+635
View File
@@ -0,0 +1,635 @@
#!/bin/sh
# Support utilities for troubleshooting Infix systems
# Program name for usage messages (supports being renamed by users)
prognm=$(basename "$0")
#
# The collect command gathers system information and outputs a tarball.
# Data is collected to /var/lib/support (or $HOME as fallback) and then
# streamed to stdout. The temporary directory is cleaned up automatically.
#
# The following text is primarily intended for users of older Infix
# systems that do not yet have this script in the root fileystems.
#
# 1. Copy this script to the target device's home directory:
#
# scp support user@device:
#
# 2. SSH to the device and make it executable:
#
# ssh user@device chmod +x support
#
# 3. Run the script from your home directory:
#
# ./support collect > support-data.tar.gz
#
# Or directly via SSH from your workstation:
#
# ssh user@device './support collect' > support-data.tar.gz
#
# Optionally, the output can be encrypted with GPG using a password for
# secure transmission to support personnel, see below.
#
# Examples:
# ./support collect > support-data.tar.gz
# ./support collect -s 5 > support-data.tar.gz
# ./support collect -p > support-data.tar.gz.gpg
# ./support collect -p mypass > support-data.tar.gz.gpg
#
# ssh user@device ./support collect > support-data.tar.gz
# ssh user@device ./support collect -p mypass > support-data.tar.gz.gpg
#
# Note, interactive password prompt (-p without argument) may echo characters
# over SSH due to local terminal echo. Use -p PASSWORD for remote execution,
# or pipe the password: echo "password" | ssh user@device ./support collect -p
# meaning you can even: echo "$SECRET_VARIABLE" | ... which in some cases can
# come in handy.
#
# TODO:
# Add more commands, e.g., verify (run health checks), upload <file>,
# test <network|disk|routing>, backup, watch (dashboard view), diff
#
cmd_collect()
{
# Default values
LOG_TAIL_SEC=30
PASSWORD=""
# Parse options
while [ $# -gt 0 ]; do
case "$1" in
--log-sec|-s)
if [ -z "$2" ] || [ "$2" -le 0 ] 2>/dev/null; then
echo "Error: --log-sec requires a positive number" >&2
exit 1
fi
LOG_TAIL_SEC="$2"
shift 2
;;
--password|-p)
# If next arg exists and doesn't start with -, use it as password
if [ -n "$2" ] && [ "${2#-}" = "$2" ]; then
PASSWORD="$2"
shift 2
else
# Prompt for password interactively from stdin, no echo!
# Disable echo BEFORE printing the prompt
old_stty=$(stty -g 2>/dev/null)
stty -echo 2>/dev/null || true
printf "Enter encryption password: " >&2
read -r PASSWORD
echo "" >&2
# Restore terminal settings
if [ -n "$old_stty" ]; then
stty "$old_stty" 2>/dev/null || true
else
stty echo 2>/dev/null || true
fi
if [ -z "$PASSWORD" ]; then
echo "Error: Empty password not allowed" >&2
exit 1
fi
shift
fi
;;
*)
echo "Error: Unknown option '$1'" >&2
echo "Usage: $prognm collect [-s N] [-p PASSWORD]" >&2
exit 1
;;
esac
done
# If WORK_DIR not set globally, try /var/lib/support first (more space,
# persistent across user sessions). Fall back to $HOME if we can't create/write there
if [ -z "$WORK_DIR" ]; then
if [ -w /var/lib/support ] 2>/dev/null; then
# Already writable, use it
WORK_DIR="/var/lib/support"
elif [ ! -e /var/lib/support ]; then
# Doesn't exist, try to create it
if mkdir -p /var/lib/support 2>/dev/null; then
WORK_DIR="/var/lib/support"
fi
elif [ -d /var/lib/support ]; then
# Exists but not writable, try to fix permissions (requires root)
# Try chmod first (might just be permission issue), then chown if needed
if chmod 755 /var/lib/support 2>/dev/null && \
chown "$(id -u):$(id -g)" /var/lib/support 2>/dev/null; then
# Verify it's actually writable now
if [ -w /var/lib/support ] 2>/dev/null; then
WORK_DIR="/var/lib/support"
fi
fi
fi
# Fall back to $HOME if we couldn't set up /var/lib/support
if [ -z "$WORK_DIR" ]; then
WORK_DIR="${HOME}"
echo "Warning: Cannot write to /var/lib/support, using home directory instead." >&2
echo " (This may fill up your home directory on systems with limited space)" >&2
fi
fi
# Create unique collection directory
COLLECT_DIR="${WORK_DIR}/support-$(hostname -s)-$(date -Iseconds)"
EXEC_LOG="${COLLECT_DIR}/collection.log"
# Cleanup on exit
cleanup()
{
echo "[$(date -Iseconds)] Cleanup called (signal: ${1:-EXIT})" >> "${EXEC_LOG}" 2>&1 || echo "[$(date -Iseconds)] Cleanup called (signal: ${1:-EXIT})" >&2
if [ -d "${COLLECT_DIR}" ]; then
echo "[$(date -Iseconds)] Removing collection directory: ${COLLECT_DIR}" >> "${EXEC_LOG}" 2>&1 || echo "[$(date -Iseconds)] Removing: ${COLLECT_DIR}" >&2
rm -rf "${COLLECT_DIR}"
else
echo "[$(date -Iseconds)] Collection directory already gone: ${COLLECT_DIR}" >> "${EXEC_LOG}" 2>&1 || echo "[$(date -Iseconds)] Already gone: ${COLLECT_DIR}" >&2
fi
}
trap cleanup EXIT INT TERM
# Create collection directory
if ! mkdir -p "${COLLECT_DIR}"; then
echo "Error: Cannot create collection directory: ${COLLECT_DIR}" >&2
echo " Check permissions for ${WORK_DIR}" >&2
exit 1
fi
# Helper function to run commands with output to specific file
collect()
{
output_file="$1"
shift
cmd_desc="$*"
mkdir -p "${COLLECT_DIR}/$(dirname "$output_file")"
echo "[$(date -Iseconds)] Collecting: $cmd_desc -> ${output_file}" >> "${EXEC_LOG}" 2>&1
if "$@" > "${COLLECT_DIR}/${output_file}" 2>> "${EXEC_LOG}"; then
echo "[$(date -Iseconds)] Success: ${output_file}" >> "${EXEC_LOG}" 2>&1
else
exit_code=$?
echo "[$(date -Iseconds)] Failed (exit ${exit_code}): ${output_file}" >> "${EXEC_LOG}" 2>&1
# Create placeholder file indicating failure
echo "Command failed with exit code ${exit_code}: $cmd_desc" > "${COLLECT_DIR}/${output_file}"
fi
}
# Start collection log
echo "=== Infix Support Data Collection ===" > "${EXEC_LOG}"
echo "Started: $(date -Iseconds)" >> "${EXEC_LOG}"
echo "Hostname: $(hostname)" >> "${EXEC_LOG}"
echo "Work directory: ${WORK_DIR}" >> "${EXEC_LOG}"
echo "Collection directory: ${COLLECT_DIR}" >> "${EXEC_LOG}"
echo "" >> "${EXEC_LOG}"
# Inform user that collection is starting (to stderr for SSH visibility)
echo "Starting support data collection from $(hostname)..." >&2
echo "Collecting to: ${WORK_DIR}" >&2
echo "This may take up to a minute. Please wait..." >&2
# System identification
collect system-info.txt uname -a
collect hostname.txt hostname
collect uptime.txt uptime
# Configuration files
collect running-config.json sysrepocfg -f json -d running -X
collect operational-config.json sysrepocfg -f json -d operational -X
# Sysrepo YANG modules
if command -v sysrepoctl >/dev/null 2>&1; then
collect sysrepo-modules.txt sysrepoctl -l
fi
# Startup config (may not exist on first boot)
if [ -f /cfg/startup-config.cfg ]; then
cp /cfg/startup-config.cfg "${COLLECT_DIR}/startup-config.cfg" 2>> "${EXEC_LOG}"
else
echo "No startup-config.cfg found" > "${COLLECT_DIR}/startup-config.cfg"
fi
# System logs and runtime data
if [ -d /var/log ]; then
if ls -A /var/log >/dev/null 2>&1; then
tar czf "${COLLECT_DIR}/logs.tar.gz" -C / var/log 2>> "${EXEC_LOG}" || \
echo "Failed to collect /var/log" > "${COLLECT_DIR}/logs.tar.gz.error"
else
echo "No logs in /var/log" > "${COLLECT_DIR}/logs.tar.gz.error"
fi
fi
# Collect crash dumps if they exist
if [ -d /var/crash ]; then
if ls -A /var/crash >/dev/null 2>&1; then
tar czf "${COLLECT_DIR}/crash.tar.gz" -C / var/crash 2>> "${EXEC_LOG}" || \
echo "Failed to collect /var/crash" > "${COLLECT_DIR}/crash.tar.gz.error"
else
echo "No crash dumps in /var/crash" > "${COLLECT_DIR}/crash.tar.gz.error"
fi
fi
# Tail /var/log/messages to capture live logging
if [ -f /var/log/messages ]; then
echo "Tailing /var/log/messages for ${LOG_TAIL_SEC} seconds (please wait)..." >&2
{
echo "=== Starting tail of /var/log/messages for ${LOG_TAIL_SEC} seconds ==="
timeout "${LOG_TAIL_SEC}" tail -F /var/log/messages 2>/dev/null || true
echo ""
echo "=== End of ${LOG_TAIL_SEC}-second tail ==="
} > "${COLLECT_DIR}/logs-tail-${LOG_TAIL_SEC}s.txt" 2>> "${EXEC_LOG}"
echo "Log tail complete." >&2
fi
if [ -d /run/net ]; then
if ls -A /run/net >/dev/null 2>&1; then
tar czf "${COLLECT_DIR}/run-net.tar.gz" -C / run/net 2>> "${EXEC_LOG}" || \
echo "Failed to collect /run/net" > "${COLLECT_DIR}/run-net.tar.gz.error"
else
echo "No data in /run/net" > "${COLLECT_DIR}/run-net.tar.gz.error"
fi
fi
if [ -d /run/finit ]; then
if ls -A /run/finit >/dev/null 2>&1; then
tar czf "${COLLECT_DIR}/run-finit.tar.gz" -C / run/finit 2>> "${EXEC_LOG}" || \
echo "Failed to collect /run/finit" > "${COLLECT_DIR}/run-finit.tar.gz.error"
else
echo "No data in /run/finit" > "${COLLECT_DIR}/run-finit.tar.gz.error"
fi
fi
# Kernel and system state
collect system/dmesg.txt dmesg
collect system/free.txt free -h
collect system/stat.txt cat /proc/stat
collect system/softirqs.txt cat /proc/softirqs
collect system/locks.txt cat /proc/locks
collect system/meminfo.txt cat /proc/meminfo
collect system/file-nr.txt cat /proc/sys/fs/file-nr
collect system/ps.txt ps -o pid,rss,comm
collect system/df.txt df -h
# Finit/init state
if command -v initctl >/dev/null 2>&1; then
collect initctl/cgroup.txt initctl cgroup
collect initctl/status.txt initctl status
fi
# CPU statistics
if command -v mpstat >/dev/null 2>&1; then
collect system/mpstat.txt mpstat -P ALL 1 1
else
echo "mpstat not available" > "${COLLECT_DIR}/mpstat.txt"
fi
# Top output (two samples)
collect system/top.txt sh -c 'top -b -n 2 -d 2'
# Interrupt statistics (before and after 2 second delay)
collect system/interrupts1.txt cat /proc/interrupts
sleep 2
collect system/interrupts2.txt cat /proc/interrupts
# Network information
collect network/ip/addr.json ip -s -d -j addr show
collect network/ip/route.json ip -s -d -j route show
collect network/ip/link.json ip -s -d -j link show
collect network/ip/neigh.json ip -s -d -j neigh show
collect network/ifconfig.txt ifconfig -a
# Collect ethtool information for all ethernet interfaces
if command -v ethtool >/dev/null 2>&1; then
# Get list of ethernet interfaces (any interface with link/ether)
ip -o link show | grep 'link/ether' | awk -F': ' '{print $2}' > "${COLLECT_DIR}/.iface-list" 2>> "${EXEC_LOG}"
if [ -s "${COLLECT_DIR}/.iface-list" ]; then
while IFS= read -r iface; do
# ethtool typically needs root
collect "network/ethtool/${iface}.txt" ethtool "$iface"
collect "network/ethtool/stats-${iface}.txt" ethtool -S "$iface"
collect "network/ethtool/module-${iface}.txt" ethtool -m "$iface"
done < "${COLLECT_DIR}/.iface-list"
fi
rm -f "${COLLECT_DIR}/.iface-list"
fi
if command -v bridge >/dev/null 2>&1; then
collect network/bridge/link.json bridge -d -s -j link show
collect network/bridge/fdb.json bridge -d -s -j fdb show
fi
# Firewall rules
if command -v nft >/dev/null 2>&1; then
collect network/nftables.txt nft list ruleset
fi
# FRR routing information
if command -v vtysh >/dev/null 2>&1; then
collect frr/running-config.txt vtysh -c "show running-config"
collect frr/ip-route.txt vtysh -c "show ip route"
collect frr/ospf-interfaces.txt vtysh -c "show ip ospf interfaces"
collect frr/ospf-neighbor.txt vtysh -c "show ip ospf neighbor"
collect frr/ospf-routes.txt vtysh -c "show ip ospf routes"
collect frr/bgp-summary.txt vtysh -c "show ip bgp summary"
collect frr/bfd-peers.txt vtysh -c "show bfd peers"
fi
# Container information
if command -v podman >/dev/null 2>&1; then
# List all containers
collect podman/ps.txt podman ps -a
# Collect podman system info
collect podman/info.json podman info --format=json
# Get list of containers and inspect each
if podman ps -a --format '{{.Names}}' > "${COLLECT_DIR}/.container-list" 2>> "${EXEC_LOG}"; then
while IFS= read -r container; do
if [ -n "$container" ]; then
safe_name=$(echo "$container" | tr '/' '_')
collect "podman/inspect-${safe_name}.json" podman inspect "$container"
fi
done < "${COLLECT_DIR}/.container-list"
rm -f "${COLLECT_DIR}/.container-list"
fi
fi
# Additional system information
collect system/lsmod.txt lsmod
if command -v lspci >/dev/null 2>&1; then
collect system/lspci.txt lspci -v
else
echo "lspci not available" > "${COLLECT_DIR}/lspci.txt"
fi
if command -v lsusb >/dev/null 2>&1; then
collect system/lsusb.txt lsusb -v
else
echo "lsusb not available" > "${COLLECT_DIR}/lsusb.txt"
fi
# Disk I/O stats
if command -v iostat >/dev/null 2>&1; then
collect system/iostat.txt iostat -x 1 2
else
echo "iostat not available" > "${COLLECT_DIR}/iostat.txt"
fi
# Process resource usage
if command -v pstree >/dev/null 2>&1; then
collect system/pstree.txt pstree -p
else
collect system/pstree.txt ps fax
fi
# Environment and versions
collect system/env.txt env
# Network sockets
if command -v netstat >/dev/null 2>&1; then
collect system/netstat.txt netstat -tunlp
else
collect system/netstat.txt ss -tunlp
fi
for script in $(find "/etc/support.d" -type f -executable 2>/dev/null | sort); do
echo "[$(date -Iseconds)] Running ${script}..." >> "${EXEC_LOG}" 2>&1
if "${script}" "${COLLECT_DIR}" >> "${EXEC_LOG}" 2>&1; then
echo "[$(date -Iseconds)] Success: ${script}" >> "${EXEC_LOG}" 2>&1
else
exit_code=$?
echo "[$(date -Iseconds)] Failed (exit ${exit_code}): ${script}" >> "${EXEC_LOG}" 2>&1
fi
done
# Completion timestamp in log
echo "" >> "${EXEC_LOG}"
echo "Completed: $(date -Iseconds)" >> "${EXEC_LOG}"
# Notify user that collection is done
echo "Collection complete. Creating archive..." >&2
# Create final tar.gz and output to stdout
# Use -C to change to parent directory so paths in archive don't include full path
echo "[$(date -Iseconds)] Changing to work directory: ${WORK_DIR}" >> "${EXEC_LOG}" 2>&1
if ! cd "${WORK_DIR}"; then
echo "[$(date -Iseconds)] ERROR: Failed to cd to ${WORK_DIR}" >> "${EXEC_LOG}" 2>&1
echo "Error: Cannot change to work directory ${WORK_DIR}" >&2
exit 1
fi
echo "[$(date -Iseconds)] Successfully changed to: $(pwd)" >> "${EXEC_LOG}" 2>&1
echo "[$(date -Iseconds)] Creating archive from: $(basename "${COLLECT_DIR}")" >> "${EXEC_LOG}" 2>&1
# Check if password encryption is requested
if [ -n "$PASSWORD" ]; then
if ! command -v gpg >/dev/null 2>&1; then
echo "Error: --password specified but gpg is not available" >&2
exit 1
fi
echo "Encrypting with GPG..." >&2
echo "[$(date -Iseconds)] Starting tar with GPG encryption" >> "${EXEC_LOG}" 2>&1
tar czf - "$(basename "${COLLECT_DIR}")" 2>> "${EXEC_LOG}" | \
gpg --batch --yes --passphrase "$PASSWORD" --pinentry-mode loopback -c 2>> "${EXEC_LOG}"
tar_exit=$?
echo "[$(date -Iseconds)] tar+gpg pipeline exit code: $tar_exit" >> "${EXEC_LOG}" 2>&1
echo "" >&2
echo "WARNING: Remember to share the encryption password out-of-band!" >&2
echo " Do not send it in the same email as the encrypted file." >&2
if [ $tar_exit -ne 0 ]; then
echo "[$(date -Iseconds)] ERROR: tar+gpg failed with exit code $tar_exit" >> "${EXEC_LOG}" 2>&1
exit $tar_exit
fi
else
echo "[$(date -Iseconds)] Starting tar (no encryption)" >> "${EXEC_LOG}" 2>&1
tar czf - "$(basename "${COLLECT_DIR}")" 2>> "${EXEC_LOG}"
tar_exit=$?
echo "[$(date -Iseconds)] tar exit code: $tar_exit" >> "${EXEC_LOG}" 2>&1
if [ $tar_exit -ne 0 ]; then
echo "[$(date -Iseconds)] ERROR: tar failed with exit code $tar_exit" >> "${EXEC_LOG}" 2>&1
exit $tar_exit
fi
fi
echo "[$(date -Iseconds)] Archive creation completed successfully" >> "${EXEC_LOG}" 2>&1
}
cmd_clean()
{
dry_run=0
days=7
# Parse options
while [ $# -gt 0 ]; do
case "$1" in
--dry-run|-n)
dry_run=1
shift
;;
--days|-d)
if [ -z "$2" ] || [ "$2" -le 0 ] 2>/dev/null; then
echo "Error: --days requires a positive number" >&2
exit 1
fi
days="$2"
shift 2
;;
*)
echo "Error: Unknown option '$1'" >&2
echo "Usage: $prognm clean [--dry-run] [--days N]" >&2
exit 1
;;
esac
done
if [ "$dry_run" -eq 1 ]; then
echo "Dry run - no files will be deleted"
echo ""
fi
echo "Looking for support directories older than ${days} days..."
echo ""
# If WORK_DIR is set globally, only search there
# Otherwise search in both /var/lib/support and $HOME
if [ -n "$WORK_DIR" ]; then
search_dirs="$WORK_DIR"
else
search_dirs="/var/lib/support ${HOME}"
fi
total_count=0
for search_dir in $search_dirs; do
if [ ! -d "$search_dir" ]; then
continue
fi
# Use find to locate old support directories
# The pattern matches: support-YYYY-MM-DD* format
find "$search_dir" -maxdepth 1 -type d -name "support-*-20*" -mtime "+${days}" 2>/dev/null | while IFS= read -r dir; do
size=$(du -sh "$dir" 2>/dev/null | awk '{print $1}')
mtime=$(stat -c %y "$dir" 2>/dev/null | cut -d' ' -f1)
if [ "$dry_run" -eq 1 ]; then
echo "Would remove: $dir ($size, modified: $mtime)"
else
echo "Removing: $dir ($size, modified: $mtime)"
rm -rf "$dir"
fi
done
# Count directories found in this location
count=$(find "$search_dir" -maxdepth 1 -type d -name "support-*-20*" -mtime "+${days}" 2>/dev/null | wc -l)
total_count=$((total_count + count))
done
echo ""
if [ "$total_count" -eq 0 ]; then
echo "No support directories older than ${days} days found in /var/lib/support or home directory."
elif [ "$dry_run" -eq 1 ]; then
echo "Found ${total_count} directories. Run without --dry-run to remove them."
else
echo "Removed ${total_count} directories."
fi
}
usage()
{
echo "Usage: $prognm [global-options] <command> [options]"
echo ""
echo "Note: Run with 'sudo' for complete data collection (dmesg, ethtool, etc.)"
echo ""
echo "Global options:"
echo " -u, --unprivileged Allow running without root (some data will be missing)"
echo " -w, --work-dir PATH Use PATH as working directory for collection/cleanup"
echo " (default: /var/lib/support with fallback to \$HOME)"
echo ""
echo "Commands:"
echo " collect [options] Collect system information for support/troubleshooting"
echo " NOTE: may take up to a minute to finish, please wait!"
echo " clean [options] Remove old support collection directories"
echo ""
echo "Options for collect:"
echo " -s, --log-sec SEC Tail /var/log/messages for SEC seconds (default: 30)"
echo " -p, --password [PASS] Encrypt output with GPG. If PASS is omitted, prompts"
echo " interactively or reads from stdin, so possible to do"
echo " echo "\$MYSECRET" | ... (recommended for security)"
echo ""
echo "Options for clean:"
echo " -n, --dry-run Show what would be deleted without deleting"
echo " -d, --days N Remove directories older than N days (default: 7)"
echo ""
echo "Examples:"
echo " sudo $prognm collect > support-data.tar.gz"
echo " sudo $prognm collect -p > support-data.tar.gz.gpg"
echo " sudo $prognm collect --password mypass > support-data.tar.gz.gpg"
echo " sudo $prognm --work-dir /tmp/ram collect > support-data.tar.gz"
echo " ssh user@device 'sudo $prognm collect' > support-data.tar.gz"
echo " $prognm -u collect > support-data.tar.gz (degraded)"
echo " sudo $prognm clean --dry-run"
echo " sudo $prognm clean --days 30"
echo " sudo $prognm --work-dir /tmp/ram clean"
exit 1
}
# Main command dispatcher
# Parse global options first
WORK_DIR=""
ALLOW_UNPRIVILEGED=0
while [ $# -gt 0 ]; do
case "$1" in
-u|--unprivileged)
ALLOW_UNPRIVILEGED=1
shift
;;
-w|--work-dir)
if [ -z "$2" ]; then
echo "Error: --work-dir requires a path argument" >&2
exit 1
fi
WORK_DIR="$2"
shift 2
;;
-*)
# Unknown option - might be a command-specific option
break
;;
*)
# Not an option, must be the command
break
;;
esac
done
if [ $# -lt 1 ]; then
usage
fi
command="$1"
shift
case "$command" in
collect|clean)
# Check if running as root (uid 0)
if [ "$(id -u)" -ne 0 ] && [ "$ALLOW_UNPRIVILEGED" -eq 0 ]; then
echo "Error: This command should be run with 'sudo' for complete data collection." >&2
echo " Use -u/--unprivileged to run as a regular user in degraded mode." >&2
exit 1
fi
if [ "$command" = "collect" ]; then
cmd_collect "$@"
else
cmd_clean "$@"
fi
;;
help|--help|-h)
usage
;;
*)
echo "Error: Unknown command '$command'" >&2
echo "" >&2
usage
;;
esac
+83 -34
View File
@@ -5,6 +5,7 @@
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
@@ -67,52 +68,100 @@ int has_ext(const char *fn, const char *ext)
return 0;
}
static const char *basenm(const char *fn)
int dirlen(const char *path)
{
const char *ptr;
const char *slash;
if (!fn)
return "";
slash = strrchr(path, '/');
if (slash)
return slash - path;
ptr = strrchr(fn, '/');
if (!ptr)
ptr = fn;
return ptr;
return 0;
}
char *cfg_adjust(const char *fn, const char *tmpl, char *buf, size_t len)
const char *basenm(const char *path)
{
if (strstr(fn, "../"))
return NULL; /* relative paths not allowed */
const char *slash;
if (fn[0] == '/') {
strlcpy(buf, fn, len);
return buf; /* allow absolute paths */
}
if (!path)
return NULL;
/* Files in /cfg must end in .cfg */
if (!strncmp(fn, "/cfg/", 5)) {
strlcpy(buf, fn, len);
if (!has_ext(fn, ".cfg"))
strlcat(buf, ".cfg", len);
slash = strrchr(path, '/');
if (slash)
return slash[1] ? slash + 1 : NULL;
return buf;
return path;
}
static int path_allowed(const char *path)
{
const char *accepted[] = {
"/media/",
"/cfg/",
getenv("HOME"),
NULL
};
for (int i = 0; accepted[i]; i++) {
if (!strncmp(path, accepted[i], strlen(accepted[i])))
return 1;
}
return 0;
}
char *cfg_adjust(const char *path, const char *template, bool sanitize)
{
char *expanded = NULL, *resolved = NULL;
const char *basename;
int dlen;
dlen = dirlen(path);
basename = basenm(path) ? : basenm(template);
if (!basename)
goto err;
if (sanitize) {
if (strstr(path, "../"))
goto err;
if (path[0] == '/') {
if (!path_allowed(path))
goto err;
}
}
if (asprintf(&expanded, "%s%.*s/%s%s",
path[0] == '/' ? "" : "/cfg/",
dlen, path,
basename,
strchr(basename, '.') ? "" : ".cfg") < 0)
goto err;
if (sanitize) {
resolved = realpath(expanded, NULL);
if (!resolved) {
if (errno == ENOENT)
goto out;
else
goto err;
}
/* File exists, make sure that the resolved symlink
* still matches the whitelist.
*/
if (!path_allowed(resolved))
goto err;
/* Files ending with .cfg belong in /cfg */
if (has_ext(fn, ".cfg")) {
snprintf(buf, len, "/cfg/%s", fn);
return buf;
free(expanded);
expanded = resolved;
}
if (strlen(fn) > 0 && fn[0] == '.' && tmpl) {
if (fn[1] == '/' && fn[2] != 0)
strlcpy(buf, fn, len);
else
strlcpy(buf, basenm(tmpl), len);
} else
strlcpy(buf, fn, len);
out:
return expanded;
return buf;
err:
free(resolved);
free(expanded);
return NULL;
}
+6 -4
View File
@@ -1,17 +1,19 @@
/* SPDX-License-Identifier: ISC */
#ifndef BIN_UTIL_H_
#define BIN_UTIL_H_
#include <stdbool.h>
#include <stdio.h>
#include <libite/lite.h>
#define ERRMSG "Error: "
#define INFMSG "Note: "
int yorn (const char *fmt, ...);
int yorn (const char *fmt, ...);
int files (const char *path, const char *stripext);
int files (const char *path, const char *stripext);
int has_ext (const char *fn, const char *ext);
char *cfg_adjust (const char *fn, const char *tmpl, char *buf, size_t len);
const char *basenm (const char *fn);
int has_ext (const char *fn, const char *ext);
char *cfg_adjust (const char *path, const char *template, bool sanitize);
#endif /* BIN_UTIL_H_ */
+35 -10
View File
@@ -130,15 +130,7 @@ int parse_ospf_areas(sr_session_ctx_t *session, struct lyd_node *areas, FILE *fp
int parse_ospf(sr_session_ctx_t *session, struct lyd_node *ospf)
{
const char *static_debug = "! OSPF default debug\
debug ospf bfd\n\
debug ospf packet all detail\n\
debug ospf ism\n\
debug ospf nsm\n\
debug ospf default-information\n\
debug ospf nssa\n\
! OSPF configuration\n";
struct lyd_node *areas, *default_route;
struct lyd_node *areas, *default_route, *debug;
const char *router_id;
int bfd_enabled = 0;
int num_areas = 0;
@@ -151,7 +143,40 @@ debug ospf nssa\n\
}
fputs(FRR_STATIC_CONFIG, fp);
fputs(static_debug, fp);
/* Handle OSPF debug configuration */
debug = lydx_get_child(ospf, "debug");
if (debug) {
int any_debug = 0;
if (lydx_get_bool(debug, "bfd")) {
fputs("debug ospf bfd\n", fp);
any_debug = 1;
}
if (lydx_get_bool(debug, "packet")) {
fputs("debug ospf packet all detail\n", fp);
any_debug = 1;
}
if (lydx_get_bool(debug, "ism")) {
fputs("debug ospf ism\n", fp);
any_debug = 1;
}
if (lydx_get_bool(debug, "nsm")) {
fputs("debug ospf nsm\n", fp);
any_debug = 1;
}
if (lydx_get_bool(debug, "default-information")) {
fputs("debug ospf default-information\n", fp);
any_debug = 1;
}
if (lydx_get_bool(debug, "nssa")) {
fputs("debug ospf nssa\n", fp);
any_debug = 1;
}
if (any_debug)
fputs("!\n", fp);
}
areas = lydx_get_child(ospf, "areas");
router_id = lydx_get_cattr(ospf, "explicit-router-id");
+1 -1
View File
@@ -22,7 +22,7 @@ MODULES=(
"ieee802-dot1q-types@2022-10-29.yang"
"infix-ip@2024-09-16.yang"
"infix-if-type@2025-02-12.yang"
"infix-routing@2024-11-27.yang"
"infix-routing@2025-12-02.yang"
"ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2025-05-05.yang"
"infix-dhcp-common@2025-01-29.yang"
+46
View File
@@ -20,6 +20,13 @@ module infix-routing {
contact "kernelkit@googlegroups.com";
description "Deviations and augments for ietf-routing and ietf-ospf.";
revision 2025-12-02 {
description "Add configurable OSPF debug logging container.
Allows users to enable specific OSPF debug categories
(bfd, packet, ism, nsm, default-information, nssa) for troubleshooting.
All debug options default to disabled to prevent log flooding.";
reference "Issue #1281";
}
revision 2024-11-27 {
description "Deviate address-family in OSPF";
reference "internal";
@@ -278,6 +285,45 @@ module infix-routing {
}
}
}
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf" {
description "Add support for configurable OSPF debug logging.
Allows users to enable specific OSPF debug categories for troubleshooting.
All debug options default to disabled to prevent log flooding in
production environments.";
container debug {
description "OSPF debug logging configuration";
leaf bfd {
type boolean;
default false;
description "Enable OSPF BFD (Bidirectional Forwarding Detection) debug messages";
}
leaf packet {
type boolean;
default false;
description "Enable detailed OSPF packet debug messages (all packet types)";
}
leaf ism {
type boolean;
default false;
description "Enable OSPF Interface State Machine debug messages";
}
leaf nsm {
type boolean;
default false;
description "Enable OSPF Neighbor State Machine debug messages";
}
leaf default-information {
type boolean;
default false;
description "Enable OSPF default route origination debug messages";
}
leaf nssa {
type boolean;
default false;
description "Enable OSPF NSSA (Not-So-Stubby Area) debug messages";
}
}
}
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface" {
deviate add {
must "count(../../../../ospf:areas/ospf:area/ospf:interfaces/ospf:interface[ospf:name=current()/name]) <= 1" {
+11 -15
View File
@@ -28,7 +28,7 @@
const uint8_t kplugin_infix_major = 1;
const uint8_t kplugin_infix_minor = 0;
static void cd_home(kcontext_t *ctx)
static const char *cd_home(kcontext_t *ctx)
{
const char *user = "root";
ksession_t *session;
@@ -43,6 +43,8 @@ static void cd_home(kcontext_t *ctx)
pw = getpwnam(user);
chdir(pw->pw_dir);
return user;
}
static int systemf(const char *fmt, ...)
@@ -118,7 +120,7 @@ int infix_erase(kcontext_t *ctx)
cd_home(ctx);
return systemf("erase %s", path);
return systemf("erase -s %s", path);
}
int infix_files(kcontext_t *ctx)
@@ -146,8 +148,8 @@ int infix_copy(kcontext_t *ctx)
{
kpargv_t *pargv = kcontext_pargv(ctx);
const char *src, *dst;
const char *username;
char user[256] = "";
char validate[8] = "";
kparg_t *parg;
src = kparg_value(kpargv_find(pargv, "src"));
@@ -159,26 +161,20 @@ int infix_copy(kcontext_t *ctx)
if (parg)
snprintf(user, sizeof(user), "-u %s", kparg_value(parg));
cd_home(ctx);
username = ksession_user(kcontext_session(ctx));
parg = kpargv_find(pargv, "validate");
if (parg)
strlcpy(validate, "-n", sizeof(validate));
return systemf("sudo -u %s copy %s %s %s", username, user, src, dst);
/* Ensure we run the copy command as the logged-in user, not root (klishd) */
return systemf("doas -u %s copy -s %s %s %s %s", cd_home(ctx), validate, user, src, dst);
}
int infix_shell(kcontext_t *ctx)
{
const char *user = "root";
ksession_t *session;
const char *user = cd_home(ctx);
pid_t pid;
int rc;
session = kcontext_session(ctx);
if (session) {
user = ksession_user(session);
if (!user)
user = "root";
}
pid = fork();
if (pid == -1)
return -1;
+1
View File
@@ -167,6 +167,7 @@
<PARAM name="src" ptype="/DATASTORE" help="Source datastore or file, e.g., tftp://ip/file"/>
<PARAM name="dst" ptype="/RW_DATASTORE" help="Destination datastore or file, e.g., scp://ip/file"/>
<SWITCH name="optional" min="0">
<COMMAND name="validate" help="Validate configuration without applying"/>
<PARAM name="user" ptype="/STRING" help="Remote username (interactive password)"/>
</SWITCH>
<ACTION sym="copy@infix" in="tty" out="tty" interrupt="true"/>