как удаленно перезагрузить линуксовый ADSL модем
В этом треде оказались административно объединены две малосвязанные вещи --- нативная компиляция expect с выложенными бинарниками достаточно древних версий expect и tcl, и весьма любопытный скрипт от cyrax вынесенный в название темы. Читателям, которые интересуются собственно темой треда, рекомендую пропустить первые 11 постов и идти сразу сюда: expect на shell, или авто-телнеттинг
----------
Наблюдается многократно обсуждавшаяся проблема, что при разрыве соединения (предположительно со стороны провайдера), соединение не восстанавливается и в логе возникает (иногда многочасовая) вереница сообщений:
May 9 11:27:33 pppd[6319]: Timeout waiting for PADO packets
May 9 11:27:33 pppd[6319]: Unable to complete PPPoE Discovery
Олег рекомендовал выставить в дополнительных опциях pppd "maxfail 0", у меня в этой строке в веб-интерфейсе не стоит ничего, однако pppd запускается коммандой pppd file /tmp/ppp/options.wan0, и файл /tmp/ppp/options.wan0 содержит:
PHP Code:
noauth refuse-eap
user xxx
password xxx
nomppe nomppc
plugin rp-pppoe.so rp_pppoe_service 'stream' nic-vlan1
mru 1492 mtu 1492
maxfail 0
usepeerdns
persist
ipcp-accept-remote ipcp-accept-local noipdefault
ktune
default-asyncmap nopcomp noaccomp
novj nobsdcomp nodeflate
lcp-echo-interval 10
lcp-echo-failure 6
unit 0
Т.е. эта опция включена по дефолту
Проблема решается в 95% случаев путем перезагрузки ADSL модема.
Теперь собственно мой вопрос. Я могу сделать какую-нибудь сторожевую собаку, которая будет проверять лог на предмет этой ошибки и дальше хотелось бы удаленно перезагрузить модем. На модеме стоит тоже линукс, поддерживается логин через ssh и telnet. Как его перезагрузить...?
Один вариант мне видится таким --- создать специального юзера и сделать ему логин-шелл=/sbin/reboot , но честно говоря это означает модификацию прошивки модема, к чему я морально не готов...
В идеале хотелось бы что то вроде удаленного sh -c , но ничего подобного не могу найти
P.S. Провайдер --- стрим, ADSL модем Dlink DSL-500T, wl500gP (7g)
expect на shell, или авто-телнеттинг
Привет,
да, давно не заходил сюда.
В общем хочу поделиться недавно написанным скриптом для тех, кому нужен expect (например для автоматической работы через телнет или вроде того).
Конечно есть пакет py_expect, но он на питоне и это не всегда удобно или возможно. Вот для таких случаев и будет полезен мой скрипт.
Для начала - пример, как это работает. В качестве примера - получение даты с другого устройства, например на DSL модеме.
Code:
#!/bin/sh
. /usr/local/sbin/mini-tools/expect.lib.sh
# цепляем библиотеку (ее код дан ниже)
expect_open -c telnet 192.168.10.1
# использование: expect_open [-c | -e <filename> ] <command> ....
#опция -с включает дублирование вывода на консоль
#опция -e включает дублирование вывода в <filename>
#без опций - вывода нет
expect login
# ждем строку включающую в себя login - запрос имени пользователя
send_line admin
# отправляем строку с именем пользователя "admin" и перевод строки
expect_eol
# ждем перевод строки, который мы же и отправили ;)
expect Password
send_line password
expect_eol
expect_exact '#'
# ждем строку состоящую только из # - запрос от shell'а
expect_echo_start
# для примера - включение вывода на консоль с этого момента
send_line "date +%s;date +%m%d%H%M%Y.%S"
expect_eol
expect_capture_start
# начало захвата вывода в переменную $EXPECT_OUTPUT, очищает переменную
expect_eol
# ждем перевода строки, т.е. в $EXPECT_OUTPUT будет вывод от первого date
if [ $EXPECT_OUTPUT -le 86400000 ]; then
echo 'Modem time is invalid'
elif [ $EXPECT_OUTPUT -le `date +%s` ]; then
echo 'Modem time is out-of-date'
else
expect_capture_start
expect_eol
#захватываем строку от второй команды date
expect_capture_stop
echo "Modem time is: $EXPECT_OUTPUT"
# date -s $EXPECT_OUTPUT
fi
expect_close
# закрываем запущенный процесс (телнет)
Ну а теперь все остальное. Немного замудрено конечно с пайпами, но зато не надо в разных файлах ввод и вывод делать.
Из того, что точно криво сделано - это вхождение строки проверяю через вызов grep, т.к. ~= дает ошибку, а другого способа не знаю.
И еще не знаю как убрать CR когда он попадает в строку - это вызывает проблему с expect_exact если им ждать вывод с переводом строки.
Из того, что не доделано - таймаут. Мне он не оч нужен, но это не сложно, и если будут желающие, то постараюсь прикрутить.
expect.lib.sh
Code:
#!/bin/sh
local EXPECT_APP_PIPE_IN
local EXPECT_APP_PIPE_OUT
local EXPECT_APP_ECHO
local EXPECT_INJECT_LINE
local EXPECT_OUTPUT
local EXPECT_CAPTURE=false
local EXPECT_APP_ID
expect_open () { # $1=echo_file $2+=app
case "$1" in
'-e') shift; expect_echo_start "$1"; shift;;
'-c') shift; expect_echo_start;;
*) expect_echo_stop;
esac
local RUN="$@"
set `dd if=/dev/urandom bs=1 count=4 2>/dev/null|od -D`
RND=${2##0000000 }
RND=${RND%% 0000004}
readonly EXPECT_APP_PIPE_IN=/tmp/tmp.$$.$RND.pipe-in
mkfifo $EXPECT_APP_PIPE_IN
readonly EXPECT_APP_PIPE_OUT=/tmp/tmp.$$.$RND.pipe-out
mkfifo $EXPECT_APP_PIPE_OUT
readonly EXPECT_INJECT_LINE="-INJECT-$$-$RND-"
$RUN <$EXPECT_APP_PIPE_IN >$EXPECT_APP_PIPE_OUT &
EXPECT_APP_ID=$!
exec 8>$EXPECT_APP_PIPE_IN #/proc/$EXPECT_APP_ID/fd/1
exec 9<$EXPECT_APP_PIPE_OUT
usleep 100000 #need to get the spawned app running as it is in background
}
expect_handle_input () { #$1 - handler #$2+ - handler params
local BUFFER
local LINE
local N_LINE
local RET_RES
local HANDLER=$1
shift
local PARAMS="$@"
local HAD_DATA_LINE=false
export HAD_DATA_LINE
local STOP_LINE=false
export STOP_LINE
while true; do
echo $EXPECT_INJECT_LINE >$EXPECT_APP_PIPE_OUT
while read N_LINE <&9; do
STOP_LINE=false
LINE="${N_LINE%$EXPECT_INJECT_LINE}"
if [ "$N_LINE" == "$LINE" ]; then
HAD_DATA_LINE=true
LINE="$BUFFER${LINE%%' '}"
BUFFER=""
echo "$LINE" >$EXPECT_APP_ECHO
if $EXPECT_CAPTURE; then EXPECT_OUTPUT="$EXPECT_OUTPUT$LINE"; fi
# FULL-LINE
$HANDLER 0 "$LINE" "$@"
elif [ "$LINE" ]; then
BUFFER="$BUFFER$LINE"
LINE="${BUFFER}"
# PART-LINE
$HANDLER 1 "$LINE" "$@"
else
STOP_LINE=true
# HAD_DATA_LINE? PART-LINE
$HANDLER 2 "$LINE" "$@"
fi
RET_RES=$?;
if [ $RET_RES -ne 1 ]; then
if [ "$BUFFER" ]; then
echo -n "$LINE" >$EXPECT_APP_ECHO
if $EXPECT_CAPTURE; then EXPECT_OUTPUT="$EXPECT_OUTPUT$LINE"; fi
fi
return $RET_RES
elif $STOP_LINE || [ "$BUFFER" ]; then
break
fi
done
if [ ! -d /proc/$EXPECT_APP_ID ]; then
break
fi
usleep 50000
done
echo "$BUFFER" >$EXPECT_APP_ECHO
if $EXPECT_CAPTURE; then EXPECT_OUTPUT="$EXPECT_OUTPUT$LINE"; fi
return 2
}
__input_handler_match_any () {
if $STOP_LINE && $HAD_DATA_LINE; then
return 0
fi
return 1
}
__input_handler_match_all () {
return 1
}
__input_handler_match_eol () {
if [ $1 == 0 ]; then
return 0
fi
return 1
}
__input_handler_grep () {
if echo "$2" | grep "$3" > /dev/null ; then
return 0
fi
return 1
}
__input_handler_match () {
local T="${2%% }"
if [ "${T## }" == "$3" ]; then
return 0
fi
return 1
}
expect_any_line () {
expect_handle_input __input_handler_match_any
}
expect () {
expect_handle_input __input_handler_grep "$1"
}
expect_exact () {
expect_handle_input __input_handler_match "$1"
}
expect_eol () {
expect_handle_input __input_handler_match_eol
}
send () {
echo -n "$@" >&8
}
send_line () {
echo "$@" >&8
}
expect_capture_start () {
EXPECT_OUTPUT=''
EXPECT_CAPTURE=true
}
expect_capture_continue () {
EXPECT_CAPTURE=true
}
expect_capture_stop () {
EXPECT_CAPTURE=false
}
expect_echo_start () {
if [ "$1" ]; then
EXPECT_APP_ECHO="$1"
else
EXPECT_APP_ECHO=/proc/$$/fd/1
fi
}
expect_echo_stop() {
EXPECT_APP_ECHO='/dev/null'
}
expect_close_now() {
exec 8>&-
exec 9<&-
rm $EXPECT_APP_PIPE_IN
rm $EXPECT_APP_PIPE_OUT
}
expect_close() {
exec 8>&-
if [ "$EXPECT_APP_ECHO" != '/dev/null' ]; then
expect_handle_input __input_handler_match_all
fi
exec 9<&-
rm $EXPECT_APP_PIPE_IN
rm $EXPECT_APP_PIPE_OUT
}
expect_clean_up() {
exec 8>&-
exec 9<&-
rm /tmp/tmp.$$.*.pipe-in
rm /tmp/tmp.$$.*.pipe-out
}
Надеюсь будет полезно кому-нить.
Если что - пишите, но быстрой реакции не обещаю.