mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-26 10:43:02 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb7c031270 | ||
|
|
1d23f73bfd | ||
|
|
119b92f94e | ||
|
|
96fad208cb | ||
|
|
0a3879f521 | ||
|
|
6f6bb412f9 | ||
|
|
b04bae1ad6 | ||
|
|
73edf1bbd6 | ||
|
|
2557eb4900 | ||
|
|
f71722c111 | ||
|
|
5ea99680c8 | ||
|
|
55b038d999 | ||
|
|
83b55ee4d8 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+161
@@ -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
|
||||
Executable
+26
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Executable
+635
@@ -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
@@ -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
@@ -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_ */
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user