8.4. Using chat to Automate Dialing

One of the things that may have struck you as inconvenient in the previous example is that you had to establish the connection manually before you could fire up pppd. Unlike dip, pppd does not have its own scripting language for dialing the remote system and logging in, but relies on an external program or shell script to do this. The command to be executed can be given to pppd with the connect command-line option. pppd will redirect the command's standard input and output to the serial line.

The pppd software package is supplied with a very simple program called chat, which is capable of being used in this way to automate simple login sequences. We'll talk about this command in some detail.

If your login sequence is complex, you will need something more powerful than chat. One useful alternative you might consider is expect, written by Don Libes. It has a very powerful language based on Tcl, and was designed exactly for this sort of application. Those of you whose login sequence requires, for example, challenge/response authentication involving calculator-like key generators will find expect powerful enough to handle the task. Since there are so many possible variations on this theme, we won't describe how to develop an appropriate expect script in this book. Suffice it to say, you'd call your expect script by specifying its name using the pppd connect option. It's also important to note that when the script is running, the standard input and output will be attached to the modem, not to the terminal that invoked pppd. If you require user interaction, you should manage it by opening a spare virtual terminal, or arrange some other means.

The chat command lets you specify a UUCP-style chat script. Basically, a chat script consists of an alternating sequence of strings that we expect to receive from the remote system, and the answers we are to send. We will call them expect and send strings, respectively. This is a typical excerpt from a chat script:
ogin: b1ff ssword: s3|<r1t

This script tells chat to wait for the remote system to send the login prompt and return the login name b1ff. We wait only for ogin: so that it doesn't matter if the login prompt starts with an uppercase or lowercase l, or if it arrives garbled. The following string is another expect string that makes chat wait for the password prompt and send our response password.

This is basically what chat scripts are all about. A complete script to dial up a PPP server would, of course, also have to include the appropriate modem commands. Assume that your modem understands the Hayes command set, and the server's telephone number is 318714. The complete chat invocation to establish a connection with c3po would then be:
$ chat -v '' ATZ OK ATDT318714 CONNECT '' ogin: ppp word: GaGariN

By definition, the first string must be an expect string, but as the modem won't say anything before we have kicked it, we make chat skip the first expect by specifying an empty string. We then send ATZ, the reset command for Hayes-compatible modems, and wait for its response (OK). The next string sends the dial command along with the phone number to chat, and expects the CONNECT message in response. This is followed by an empty string again because we don't want to send anything now, but rather wait for the login prompt. The remainder of the chat script works exactly as described previously. This description probably looks a bit confusing, but we'll see in a moment that there is a way to make chat scripts a lot easier to understand.

The –v option makes chat log all activities to the syslog daemon local2 facility.[1]

Specifying the chat script on the command line bears a certain risk because users can view a process's command line with the ps command. You can avoid this risk by putting the chat script in a file like dial-c3po. You make chat read the script from the file instead of the command line by giving it the –f option, followed by the filename. This action has the added benefit of making our chat expect sequences easier to understand. To convert our example, our dial-c3po file would look like:
''      ATZ
OK      ATDT318714
CONNECT ''
ogin:   ppp
word:   GaGariN
When we use a chat script file in this way, the string we expect to receive is on the left and the response we will send is on the right. They are much easier to read and understand when presented this way.

The complete pppd incantation would now look like this:
# pppd connect "chat -f dial-c3po" /dev/ttyS3 38400 -detach \
        crtscts modem defaultroute

Besides the connect option that specifies the dialup script, we have added two more options to the command line: –detach, which tells pppd not to detach from the console and become a background process, and the modem keyword, which makes it perform modem-specific actions on the serial device, like disconnecting the line before and after the call. If you don't use this keyword, pppd will not monitor the port's DCD line and will therefore not detect whether the remote end hangs up unexpectedly.

The examples we have shown are rather simple; chat allows for much more complex scripts. For instance, it can specify strings on which to abort the chat with an error. Typical abort strings are messages like BUSY or NO CARRIER that your modem usually generates when the called number is busy or doesn't answer. To make chat recognize these messages immediately rather than timing out, you can specify them at the beginning of the script using the ABORT keyword:
$ chat -v ABORT BUSY ABORT 'NO CARRIER' '' ATZ OK ...

Similarly, you can change the timeout value for parts of the chat scripts by inserting TIMEOUT options.

Sometimes you also need to have conditional execution for parts of the chat script: when you don't receive the remote end's login prompt, you might want to send a BREAK or a carriage return. You can achieve this by appending a subscript to an expect string. The subscript consists of a sequence of send and expect strings, just like the overall script itself, which are separated by hyphens. The subscript is executed whenever the expected string it is appended to is not received in time. In the example above, we would modify the chat script as follows:
ogin:-BREAK-ogin: ppp ssword: GaGariN

When chat doesn't see the remote system send the login prompt, the subscript is executed by first sending a BREAK, and then waiting for the login prompt again. If the prompt now appears, the script continues as usual; otherwise, it will terminate with an error.

Notes

[1]

If you edit syslog.conf to redirect these log messages to a file, make sure this file isn't world readable, as chat also logs the entire chat script by default—including passwords.