Skip to content

smart-reboot: add network-idle aware scheduled reboot utility#28586

Open
minicom365 wants to merge 6 commits intoopenwrt:masterfrom
minicom365:smart-reboot-package
Open

smart-reboot: add network-idle aware scheduled reboot utility#28586
minicom365 wants to merge 6 commits intoopenwrt:masterfrom
minicom365:smart-reboot-package

Conversation

@minicom365
Copy link
Copy Markdown

This adds smart-reboot, a lightweight OpenWrt utility that performs scheduled reboot only when monitored interfaces are considered idle.

Features

  • UCI-based configuration (/etc/config/smart-reboot)
  • Cron-triggered check at configured HH:MM
  • Idle decision based on RX/TX byte delta over a sampling window
  • Configurable threshold and monitored interfaces
  • Init script integration with automatic enable/restart on install

Testing

  • Build target: ipq806x/generic
  • Architecture: arm_cortex-a15_neon-vfpv4
  • Verified package install and service startup

Notes

  • This PR contains backend package only.
  • LuCI frontend is submitted separately in OpenWrt LuCI repository.

Signed-off-by: minicom365 3387910@naver.com

Add smart-reboot package for scheduled reboot when monitored interfaces are idle.

- Introduce UCI config and init integration
- Run check at configured HH:MM via cron
- Reboot only if sampled traffic delta is below threshold
- Auto-enable and restart service after install

Signed-off-by: Minicom Developer <3387910@naver.com>
Rewrite /usr/sbin/smart-reboot-check from shell to ucode as suggested in review.

- Keep existing behavior and configuration semantics
- Preserve locking, idle traffic sampling and reboot decision logic
- Keep last_auto_reboot update before reboot
- Add runtime dependency on ucode
- Bump PKG_RELEASE to 4

Signed-off-by: minicom365 <3387910@naver.com>
@minicom365
Copy link
Copy Markdown
Author

Thanks for the suggestion. I rewrote utils/smart-reboot/files/usr/sbin/smart-reboot-check in ucode and force-pushed the PR branch.

Changes included:

  • Shell implementation replaced with ucode implementation
  • Existing behavior preserved (lock handling, idle sampling, reboot decision, last_auto_reboot update)
  • Added runtime dependency on ucode
  • Bumped PKG_RELEASE to 4

Please take another look when you have time. 감사합니다.

let sample_seconds = int(cfg_get("sample_seconds", "120"));
let byte_threshold = int(cfg_get("byte_threshold", "262144"));
let all_ifaces = cfg_get("all_ifaces", "0");
let ifaces = parse_ifaces(cmd_out(`uci -q get ${CFG}.${SECTION}.ifaces`));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use the uci module.

Comment on lines +24 to +27
function cfg_get(key, def) {
let val = cmd_out(`uci -q get ${CFG}.${SECTION}.${key}`);
return length(val) ? val : def;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use the uci module

ifaces = list_all_ifaces();
}
else if (!length(ifaces)) {
let wan_dev = cmd_out("uci -q get network.wan.device");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

let delta = end_bytes - start_bytes;

if (delta <= byte_threshold) {
logger(`Network idle detected (delta=${delta} bytes, threshold=${byte_threshold}), rebooting now`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use the log module

system("/sbin/reboot");
}
else {
logger(`Skip reboot, network is active (delta=${delta} bytes, threshold=${byte_threshold})`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

Comment thread utils/smart-reboot/Makefile Outdated
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Smart reboot based on network idle state
DEPENDS:=+busybox +uci +ucode
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Formatting is off here

/etc/config/smart-reboot
endef

define Build/Compile
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think add a true here can prevent warning

@minicom365
Copy link
Copy Markdown
Author

Applied follow-up review updates and force-pushed.

Latest commit: a557b3936

  • switched to uci ucode module access (no shell uci calls)
  • switched to log ucode module logging (no logger command)
  • added +ucode-mod-uci +ucode-mod-log runtime deps
  • kept behavior the same (lock/sampling/threshold/reboot)
  • adjusted Makefile formatting and explicit Build/Compile no-op (true)

Thanks again for the detailed feedback.

let wan_dev = cursor.get("network", "wan", "device");
if (length(wan_dev))
ifaces = [ wan_dev ];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does it have to be the wan device?
Some users have multiple wan, so making this configurable is a good idea.

Address review comments by using ucode modules directly.

- Use uci module instead of shelling out to uci commands
- Use log module instead of logger command
- Keep behavior unchanged (lock, sampling, threshold, reboot)
- Add required ucode module dependencies
- Make Build/Compile explicitly no-op with true
- Fix Makefile formatting

Signed-off-by: minicom365 <3387910@naver.com>
@minicom365 minicom365 force-pushed the smart-reboot-package branch 2 times, most recently from b9d909e to 8d31c86 Compare February 17, 2026 03:45
Do not implicitly fallback to network.wan.device when no monitored interfaces are configured.

- Require explicit interface selection through settings.ifaces
- Keep all_ifaces mode as before
- Exit safely with a log message when no interfaces are configured

Signed-off-by: minicom365 <3387910@naver.com>
@minicom365
Copy link
Copy Markdown
Author

Good point, thanks. I removed the hardcoded network.wan.device fallback and force-pushed.

Latest commit: a2543558e

New behavior when all_ifaces=0:

  • use explicitly configured smart-reboot.settings.ifaces
  • if none is configured, exit safely with a log message (no implicit WAN fallback)

This should better support multi-WAN/custom setups.

start_service() {
[ -f "$CRON_FILE" ] || touch "$CRON_FILE"
gen_schedule
/etc/init.d/cron restart >/dev/null 2>&1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is a restart necessary or does a reload work just as well?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

cron restart is not strictly necessary; reload is sufficient and less disruptive when supported. Updated to prefer reload and fall back to restart if reload is unavailable/fails.

Use cron reload when available after updating /etc/crontabs/root.

Fallback to a full cron restart if reload is not supported or fails.

Signed-off-by: 최민 <3387910@naver.com>
function main() {
log.openlog("smart-reboot", log.LOG_PID);

if (system(`lock -n ${LOCK_FILE} >/dev/null 2>&1`) != 0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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


let enabled = cfg_get("enabled", "0");
if (enabled != "1") {
system(`lock -u ${LOCK_FILE}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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


let now = localtime(time());
if (now.hour != hour || now.min != minute) {
system(`lock -u ${LOCK_FILE}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logger(`Skip reboot, network is active (delta=${delta} bytes, threshold=${byte_threshold})`);
}

system(`lock -u ${LOCK_FILE}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

}

let start_bytes = sum_iface_bytes(ifaces);
system(`sleep ${sample_seconds}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

}
else if (!length(ifaces)) {
logger("No monitored interfaces configured. Set smart-reboot.settings.ifaces or enable all_ifaces.");
system(`lock -u ${LOCK_FILE}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Apply review suggestions to avoid external sleep and lock commands.

Signed-off-by: 최민 <3387910@naver.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants