[Another little step] [About] [Copertina] [Device drivers #4]

Articoli



Linux, telefoni e pigrizia.

La pigrizia è la madre della creazione.

Se c'è una cosa che mi dà fastidio è dover spostare le mani dalla tastiera del computer, cercare i numeri di telefono nelle varie agende e comporre il numero.
Pensate quello che volete, ma sono pigro.

Per rimediare a questo terribile stato di fatto mi sono posto l'obiettivo di far fare al computer ciò che non ho voglia di fare io stesso.
Dopo un po' di mumble mumble ho deciso di realizzare un piccolo database di nomi e numeri di telefono e un programmino che legga dalla mia cara riga di comando il nome della persona a cui voglio telefonare, componga il numero usando il modem e attenda la pressione di un tasto, per resettare il modem dopo che io abbia alzato la cornetta.

Partiamo dal database di nomi: non ho molte pretese e penso che possa bastarmi un elenco del tipo:

nome1,numero1
nome2,numero2
...,...

Dopo aver riempito un file con questi dati, ho scritto un pezzetto di codice per leggerlo e trovare il numero di telefono giusto.
Il codice Perl che fa questa ricerca è il seguente:

#!/usr/bin/perl -w

die "Uso: $0 nome" if ! defined @ARGV;

open(TEL, "$ENV{'HOME'}/.tel.numbers") ||
			die "Non trovo i numeri di telefono: $!";
$nomei = shift @ARGV;
$trovato = 0;
while(<TEL>){
	chomp;
	($nome, $tel) = split /,/;
	if($nome eq $nomei){
		$trovato = 1;
		last;
	}
}
close TEL;

die "Numero non trovato\n" if ! $trovato;
print "Chiamo $nome al numero $tel\n";

Ho cercato di scrivere qualcosa che fosse almeno indipendente dall'utente che lo usa, così il file dei nomi-numeri di telefono lo apro come:

open(TEL, "$ENV{'HOME'}/.tel.numbers")

Il Perl definisce un array associativo, %ENV, che contiene come indici i nomi delle variabili di environment e come elementi i loro rispettivi valori.
Quindi $ENV{'HOME'} è il valore della variabile di environment HOME, che contiene la path della home directory dell'utente.
Il file da consultare l'ho chiamato .tel.numbers, per evitare che mi disturbasse ad ogni ls.

Un altro array magico definito dal Perl è @ARGV. Questo array contiene la riga di comando, quindi si può verificare che l'utente abbia immesso almeno un parametro, testando semplicemente l'esistenza dell'array, che altrimenti risulterebbe indefinito:

die "Uso: $0 nome" if ! defined @ARGV;

e si può leggere il primo elemento facendolo sgusciare fuori dall'array :

$nomei = shift @ARGV;

A questo punto possiamo scorrere il file, alla ricerca del nome che si suppone l'utente abbia immesso sulla riga di comando:

while(<TEL>){
	chomp;
	($nome, $tel) = split /,/;
	if($nome eq $nomei){
		$trovato = 1;
		last;
	}
}

L'istruzione onomatopeica chomp mangia l'eventuale carattere di fine riga, mentre lo split spezza la riga all'altezza della virgola e restituisce una lista che viene assegnata alle variabili $nome e $tel.
Se la riga in esame contiene proprio il nome che cerchiamo, usciamo dal loop e assegnamo il valore 1 alla variabile-flag $trovato.

A questo punto non ci resta che comunicare al modem i nostri desideri:

open(MODEM, "+>/dev/cua1") ||
			die "Non posso usare il modem: $!";
system "/bin/stty raw -echo </dev/cua1";
$in='';
$out="atz\r";
syswrite(MODEM, $out, length($out));
sleep 2;
sysread(MODEM, $in, 1000);
$out="atdt$tel\r";
syswrite(MODEM, $out, length($out));
sleep 2;
sysread(MODEM, $in, 1000);
<>;
close MODEM;
system "/bin/stty cooked echo </dev/cua1";

Innanzi tutto dobbiamo aprire il modem.
Sotto Unix qualsiasi cosa è un file, anche una seriale. Quindi per comunicare con la seriale (alla quale è attacato il modem) devo aprire il suo pseudo-file:

open(MODEM, "+>/dev/cua1")

Il simbolo +> indica che il file deve essere aperto in lettura e scrittura, cosa che ci serve per poter leggere le risposte del modem ai nostri comandi.

Ciò di cui abbiamo ora bisogno è qualche informazione sulle seriali: per poter comunicare con il modem senza interferenze si deve configurare la linea in row mode.
Questa operazione può essere fatta comodamente usando un comando esterno, che Linux (come ogni altro Unix) è così gentile da metterci a disposizione:

system "/bin/stty raw -echo </dev/cua1";

In questo modo ho configurato il tty che mi interessa in modalità raw e noecho (incidentalmente uso /dev/cua1, perché ho il modem sulla seconda seriale).

Avrei potuto programmare la linea seriale direttamente in Perl, ma essendo cosa lunga e tediosa, ho preferito usare un comando esterno, probabilmente anche più portabile (ricordate, sono pigro).

A questo punto si può cominciare il colloquio con il modem: ogni comando viene spedito con una syswrite e il risultato letto con una sysread.
Ho usato la syswrite al posto della print per essere sicuro che i miei comandi non siano bufferizzati.

Ho aggiunto qualche sleep per dare al modem il tempo di capire cosa gli chiedo e spedirmi la risposta.

Dopo aver composto il numero, aspetto un input dall'utente:

<>;

l'utente a questo punto può sollevare la cornetta e premere enter per staccare il modem.
L'operazione di esclusione del modem si ottiene semplicemente chiudendo il file aperto, poi, perché non si dica che siamo scortesi, rimettiamo le cose a posto sulla linea con un altro stty:

system "/bin/stty cooked echo </dev/cua1";

Tutto qui.
C'è ancora un discorsetto da fare sulle persmissions: non tutti possono permettersi di sparare comandi così ad un modem.
Nel mio sistema la seconda linea seriale appare così:

$ ls -l /dev/cua1

crw-rw----   1 root     dialout    5,  65 Oct 21 17:28 /dev/cua1

Come si può vedere solo root e gli utenti del gruppo dialout possono leggere e scrivere questo file.
La soluzione è quella di listare tutti gli utenti che vogliono usare questo programmino, in /etc/group, come membri del gruppo dialout.

Infatti il mio /etc/group recita:

...
dialout:*:20:nando
...

A questo punto il mio utente personale fa parte anche del gruppo dialout e io posso scrivere inpunemente sulla seconda seriale, comunicare con il mio modem e telefonare senza usare la tastiera del telefono.

Ovviamente ci sono molte migliorie che si possono apportare a questo programmino: si potrebbe ad esempio controllare che non ci siano locks sulla seriale che stiamo per usare, controllando /var/lock/, si potrebbe cercare il primo modem libero tra quelli collegati, ecc.

Una possibilità interessante sarebbe quella di chiamare un certo numero di telefono usando una form sul Web. Questo telefono potrebbe essere collegato ad un relais, che potrebbe accendere... ooops!

Bhe, torniamo con i piedi per terra ;-)

A rileggerci al prossimo numero del Pluto Journal.

di Nando Santagata


[Another little step] [About] [Copertina] [Device drivers #4]