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
pppdconnect 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.

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.

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.