Here is the final part on how I got my modem to automatically answer and drop phone calls from certain numbers that had been troubling me.
My Intel 536EP is famous for not supporting CLI, and slightly famous for the odd Intel engineer saying it does support CLI. No-one in the world has made it work.
Luckily, my sister in law returned my USR 56K external modem on Sunday, so I stick that in ttyS0 and remove the graphics stylus I wasn’t using anyway. This modem gives no problem recognizing CLI.
I decide to write my CLI Dropper in bash, for fun, and get more grief then you could imagine. It sounds easy, a quick stty, echo a few strings to the modem port and then enter a while loop to read back the response. Sadly bash’s bufferred IO reads too much (naturally) but then tries to
put back the extra stuff using seek on a pipe. Well! This fails on a tty, and so lots of the output gets lost. It took me quite a while and use of strace to work this out.
Instead now, I use cat to pipe the output of the modem into a bash segment. Because cat is reading from a tty it is unbufferred, but also bash’s seek works, so everything is fine.
cat /dev/ttyS0 | while read line
So now I just keep a list of banned phone numbers in /etc/clidrop. Banned callers get treated to about 5 seconds of modem noise before getting hung up on. All calls are logged in /var/log/messages, so I can look back and see what went on.
Here is my shell script, complete and tested:
#! /bin/sh
: ${MODEM:=/dev/ttyS0}
# modem on stdout
exec > "$MODEM"
stty igncr 38400 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke < $MODEM
send() {
echo -ne "$*\r"
}
command() {
send "$*"
echo ">$*" >&2
read response
test "$response" == "$*" && read response
echo "<$response" >&2
}
checkNumber() {
echo "Check $1" >&2
grep "^$1\%CONTENT%quot; /etc/clidrop &> /dev/null
}
# nasty cat hack cos bash "read" can’t unread extra lines from a tty
# but cat doesn’t buffer if it reads from a tty
cat "$MODEM" | (
command "AT S7=45 S0=0 L1 V1 X4 &c1 E1 Q0"
command "ATL0M0" # turn of speaker
command "AT#CID=1"
while read -s response
do
case "$response" in
"NMBR = "*)
NUMBER="`expr "$response" : ‘NMBR = \(.*\)’`"
if checkNumber "$NUMBER"
then
logger -t CLIDROP "Drop call from $NUMBER"
echo "<$response DENIED" >&2
send ATA
sleep 5
send ""
else
logger -t CLIDROP "Permit call from $NUMBER"
echo "<$response PERMITTED" >&2
fi;;
*) echo "<$response" >&2;;
esac
done
My /etc/clidrop file is has these numbers in:
08700500524
01413018000
Why does bash try to seek on input stream after a read? It’s so that it can pretent that it did not read more than it returned, and then any internal or external commands spawned will be able to get stdin from that point. Of course such external commands are not so nice, sed …/q will have slurped more of stdin than it ought to and so some of the remainder won’t be available to the calling bash script; so why is bash trying to be nice? Ah well… it’s what comes of pretending that a collection of commands and bash are a homogenous programming environment like perl.