Так уж случилось, что домашний роутер после двух лет безглючной работы начал время от времени подвисать. Проявлялось это в подвисании WAN порта и отсутствии интернета у всей подсети, LAN работает нормально. Решение оказалось не таким простым, как может показаться сначала.
Перед использованием данного руководства сделайте резервную копию настроек вашего роутера
Подбор правильного минимального решения требует для рассмотрения нескольких вариантов.
Первое решение, которое было в последствии отброшено по причине сложности и длительности реализации — arduino+ethernet shield/rapsberry PI + реле. Это тот случай, когда не нужно решать все задачи одним и тем же привычным способом, даже если очень хочется. Как минимум пришлось бы ждать либо ethernet shield, либо rapsberry PI. А ждать не хотелось. Далее логично было бы предположить, что раз нельзя сделать перезагрузку аппаратно, то может это можно сделать программно. Действительно, роутер подвисал не полностью, и предоставлял возможность без проблем войти в админку. 5 минут гугления.
curl -u 'login:password' 'http://192.168.1.1/setup.cgi?todo=reboot'
Ссылка замечательно работает в браузере, но через curl рецепт в упор не хотел работать и выдавал.
<HTML>
<HEAD><TITLE>401 Unauthorized</TITLE></HEAD>
<BODY BGCOLOR="#cc9999" TEXT="#000000" LINK="#2020ff" VLINK="#4040cc">
<H4>401 Unauthorized</H4>
Authorization required.
<HR>
<ADDRESS><A HREF=""></A></ADDRESS>
</BODY>
</HTML>
Вариации с post параметрами, использование библиотеки request не дали положительного результата, ответ сервера всё тот же. Разбираться в тонкостях авторизации очень не хотелось. Виртуальная машина + скрипт на sikuli — с пушки по воробьям. Окончательное решение: использовать nodejs+coffeescript+phantomjs. Так уж сложилось исторически, что домашний linux дистрибутив «для поэкспериментировать» у меня gentoo. До последнего момента в gentoo был битый ebuild для phantomjs: сначала он просто не собирался, а потом таки стал собираться, но работать при этом всё-равно отказывался. Обидно, но попытка не пытка.
Создаем рабочее окружение для поделки и устанавливаем зависимости:
mkdir /opt/ext/router_reboot_tool
cd /opt/ext/router_reboot_tool
npm install -g coffeescript
npm install phantomjs phpjs
И вот тут вскрывается интересный факт. При установке phantomjs он подтягивает готовый рабочий бинарник. Вот чудо, не надо будет плясать с бубном. Дальше уже дело техники.
check.coffee проверяет наличие интернета как умеет, и в случае падения вызывает reboot.sh:
#!/usr/bin/coffee
fs = require('fs')
exec= require('child_process').exec
php = require('phpjs')
_old_console_log = console.log
console.log = (t)->
_old_console_log "[#{php.date('d.m.Y H:i:s')}] #{t}"
check_internet = (get_result)->
exec 'ping -c1 8.8.8.8', (_skip,result)->
get_result /1 packets transmitted, 1 received/.test result
if fs.existsSync 'marker'
console.log 'marker detected'
fs.unlinkSync 'marker'
process.exit()
fail_count = 0
max_fail_count = 10
check_count = 0
main_loop = setInterval ()->
check_count++
if check_count > 60*5-10
console.log "wiped (new will started by cron)"
process.exit()
check_internet (r)->
return if fail_count > max_fail_count # дополнительная проверка. Лень пока-что разбираться почему нужна
# console.log r
if r
fail_count = 0
else
fail_count++
console.log "fail #{fail_count}"
if fail_count > max_fail_count
console.log "reboot"
clearInterval main_loop # как ни странно не срабатывает
fs.writeFileSync 'marker', ''
exec './reboot.sh', (_skip,result)->
console.log result
setTimeout ()->
fs.unlinkSync 'phantom_marker'
process.exit()
, 10000
, 1000
Дополнительная запись файла marker нужна для того, что бы случайно не отправить 2 запроса перезагрузки подряд. Такое может быть если интернет пропал как раз перед концом 5-минутного интервала. А так мы гарантировано имеем промежуток как минимум 5 минут между перезагрузками роутера.
Сюрприз №1. clearInterval не захотел убивать интервал и reboot.sh вызывался в некоторых случаях по 4 раза из-за чего у меня слетели настройки на роутере. Внезапно две команды reboot подряд с интервалом в секунду вызывают hard reset. Решение-костыль так и осталось в готовом решении.
reboot.sh сильно логикой не отличается умом и сообразительностью. Прост и банален. Потом его придется поправить.
#!/bin/bash
coffee -c ./phantom.coffee
./node_modules/phantomjs/bin/phantomjs ./phantom.js
phantom.coffee
console.log "start..."
# fs = require('fs')
# if fs.existsSync 'phantom_marker'
# console.log "phantom_marker"
# phantom.exit()
# fs.writeFileSync 'phantom_marker', ''
page = require('webpage').create()
page.onConsoleMessage = (msg)->
console.log msg
url = 'http://192.168.1.1/Reboot.htm'
page.settings.userName = 'login'
page.settings.password = 'password'
page.open url, ()->
page.evaluate ()->
document.getElementsByName('mtenReboot')[0].click()
console.log 'waiting...'
setTimeout ()->
console.log 'exit'
# fs.unlinkSync 'phantom_marker'
phantom.exit()
, 10000
В связи с тем, что хотелось добавить защиту от случайного запуска reboot.sh дважды, я добавил запись файла phantom_marker как индикатора того, что процесс уже запущен и не надо запускать еще один. Но тут меня ждал сюрприз №2.
Сюрприз №2. скрипты для phantomjs не поддерживают модули для nodejs (или просто модуль fs, или поддерживают, но какую-то старую версию модулей, подробно разбираться не имело смысла). Обидно, комментируем, вносим проверку в reboot.sh, которая менее красива, но работает.
Обновленный reboot.sh:
#!/bin/bash
if [ -a 'phantom_marker' ]
then
echo "phantom_marker present"
exit
fi
touch phantom_marker
coffee -c ./phantom.coffee
./node_modules/phantomjs/bin/phantomjs ./phantom.js
unlink phantom_marker
Финальные штрихи
chmod +x check.coffee
chmod +x reboot.sh
crontab -e
*/5 * * * * cd /opt/ext/router_reboot_tool && ./check.coffee 2>&1 >> ./log
/etc/init.d/vixie-cron restart
Вносим правило уже ручками (я верю, что есть способ как это сделать из консоли, но у меня не было задачи автоматизировать установку данной поделки). После обновления crontab перезагружаем cron, иначе ничего работать не будет.
Пару тестов с ручным выключением WAN показали, что скрипт работает как нужно. В сумме на поиск решения и на решения сюрпризов было потрачено 2-4 часа, которых у кого-то может не быть, потому решил выложить решение на общее обозрение. Модифицировать скрипты под другой роутер — несложно, надо просто заменить путь к странице с кнопкой reboot и само нажатие на кнопку.
Что хотелось бы доделать, но не дошли руки: убрать костыль для лишней проверки fail_count.
Автор: vird