summaryrefslogtreecommitdiff
path: root/appl/cmd/ip/ppp
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/ip/ppp')
-rw-r--r--appl/cmd/ip/ppp/mkfile27
-rw-r--r--appl/cmd/ip/ppp/modem.b468
-rw-r--r--appl/cmd/ip/ppp/modem.m41
-rw-r--r--appl/cmd/ip/ppp/pppclient.b216
-rw-r--r--appl/cmd/ip/ppp/pppclient.m31
-rw-r--r--appl/cmd/ip/ppp/pppdial.b283
-rw-r--r--appl/cmd/ip/ppp/pppgui.b373
-rw-r--r--appl/cmd/ip/ppp/pppgui.m21
-rw-r--r--appl/cmd/ip/ppp/ppptest.b86
-rw-r--r--appl/cmd/ip/ppp/script.b168
-rw-r--r--appl/cmd/ip/ppp/script.m14
11 files changed, 1728 insertions, 0 deletions
diff --git a/appl/cmd/ip/ppp/mkfile b/appl/cmd/ip/ppp/mkfile
new file mode 100644
index 00000000..193b8faf
--- /dev/null
+++ b/appl/cmd/ip/ppp/mkfile
@@ -0,0 +1,27 @@
+<../../../../mkconfig
+
+TARG=\
+ pppclient.dis\
+ pppdial.dis\
+ pppgui.dis\
+ ppptest.dis\
+ modem.dis\
+ script.dis\
+
+MODULES=\
+ modem.m\
+ pppclient.m\
+ pppgui.m\
+ script.m\
+
+SYSMODULES=\
+ sys.m\
+ draw.m\
+ tk.m\
+ dict.m\
+ string.m\
+ lock.m\
+
+DISBIN=$ROOT/dis/ip/ppp
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/cmd/ip/ppp/modem.b b/appl/cmd/ip/ppp/modem.b
new file mode 100644
index 00000000..6085524a
--- /dev/null
+++ b/appl/cmd/ip/ppp/modem.b
@@ -0,0 +1,468 @@
+implement Modem;
+
+include "sys.m";
+ sys: Sys;
+
+include "lock.m";
+ lock: Lock;
+ Semaphore: import lock;
+
+include "draw.m";
+
+include "modem.m";
+
+hangupcmd := "ATH0"; # was ATZH0 but some modem versions on Umec hung on ATZ (BUG: should be in modeminfo)
+
+# modem return codes
+Ok, Success, Failure, Abort, Noise, Found: con iota;
+
+maxspeed: con 115200;
+
+#
+# modem return messages
+#
+Msg: adt {
+ text: string;
+ code: int;
+};
+
+msgs: array of Msg = array [] of {
+ ("OK", Ok),
+ ("NO CARRIER", Failure),
+ ("ERROR", Failure),
+ ("NO DIALTONE", Failure),
+ ("BUSY", Failure),
+ ("NO ANSWER", Failure),
+ ("CONNECT", Success),
+};
+
+kill(pid: int)
+{
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "kill") < 0)
+ sys->print("modem: can't kill %d: %r\n", pid);
+}
+
+#
+# prepare a modem port
+#
+openserial(d: ref Device)
+{
+ if (d==nil) {
+ raise "fail: device not initialized";
+ return;
+ }
+
+ d.data = nil;
+ d.ctl = nil;
+
+ d.data = sys->open(d.local, Sys->ORDWR);
+ if(d.data == nil) {
+ raise "fail: can't open "+d.local;
+ return;
+ }
+
+ d.ctl = sys->open(d.local+"ctl", Sys->ORDWR);
+ if(d.ctl == nil) {
+ raise "can't open "+d.local+"ctl";
+ return;
+ }
+
+ d.speed = maxspeed;
+ d.avail = nil;
+}
+
+#
+# shut down the monitor (if any) and return the connection
+#
+
+close(m: ref Device): ref Sys->Connection
+{
+ if(m == nil)
+ return nil;
+ if(m.pid != 0){
+ kill(m.pid);
+ m.pid = 0;
+ }
+ if(m.data == nil)
+ return nil;
+ mc := ref sys->Connection(m.data, m.ctl, nil);
+ m.ctl = nil;
+ m.data = nil;
+ return mc;
+}
+
+#
+# Send a string to the modem
+#
+
+send(d: ref Device, x: string): int
+{
+ if (d == nil)
+ return -1;
+
+ a := array of byte x;
+ f := sys->write(d.data, a, len a);
+ if (f < 0) {
+ # let's attempt to close & reopen the modem
+ close(d);
+ openserial(d);
+ f = sys->write(d.data,a, len a);
+ }
+ sys->print("->%s\n",x);
+ return f;
+}
+
+#
+# apply a string of commands to modem & look for a response
+#
+
+apply(d: ref Device, s: string, substr: string, secs: int): int
+{
+ m := Ok;
+ buf := "";
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ buf[len buf] = c; # assume no Unicode
+ if(c == '\r' || i == (len s -1)){
+ if(c != '\r')
+ buf[len buf] = '\r';
+ if(send(d, buf) < 0)
+ return Abort;
+ (m, nil) = readmsg(d, secs, substr);
+ buf = "";
+ }
+ }
+ return m;
+}
+
+#
+# get modem into command mode if it isn't already
+#
+GUARDTIME: con 1100; # usual default for S12=50 in units of 1/50 sec; allow 100ms fuzz
+
+attention(d: ref Device): int
+{
+ for(i := 0; i < 3; i++){
+ if(apply(d, hangupcmd, nil, 2) == Ok)
+ return Ok;
+ sys->sleep(GUARDTIME);
+ if(send(d, "+++") < 0)
+ return Abort;
+ sys->sleep(GUARDTIME);
+ (nil, msg) := readmsg(d, 0, nil);
+ if(msg != nil)
+ sys->print("status: %s\n", msg);
+ }
+ return Failure;
+}
+
+#
+# apply a command type
+#
+
+applyspecial(d: ref Device, cmd: string): int
+{
+ if(cmd == nil)
+ return Failure;
+ return apply(d, cmd, nil, 2);
+}
+
+#
+# hang up any connections in progress and close the device
+#
+onhook(d: ref Device)
+{
+ if(d == nil)
+ return;
+
+ # hang up the modem
+ monitoring(d);
+ if(attention(d) != Ok)
+ sys->print("modem: no attention\n");
+
+ # hangup the stream (eg, for ppp) and toggle the lines to the modem
+ if(d.ctl != nil) {
+ sys->fprint(d.ctl,"d0\n");
+ sys->fprint(d.ctl,"r0\n");
+ sys->fprint(d.ctl, "h\n"); # hangup on native serial
+ sys->sleep(250);
+ sys->fprint(d.ctl,"r1\n");
+ sys->fprint(d.ctl,"d1\n");
+ }
+
+ close(d);
+}
+
+#
+# does string s contain t anywhere?
+#
+
+contains(s, t: string): int
+{
+ if(t == nil)
+ return 1;
+ if(s == nil)
+ return 0;
+ n := len t;
+ for(i := 0; i+n <= len s; i++)
+ if(s[i:i+n] == t)
+ return 1;
+ return 0;
+}
+
+#
+# read till we see a message or we time out
+#
+readmsg(d: ref Device, secs: int, substr: string): (int, string)
+{
+ if (d == nil)
+ return (Abort, "device not initialized");
+ found := 0;
+ secs *= 1000;
+ limit := 1000; # pretty arbitrary
+ s := "";
+
+ for(start := sys->millisec(); sys->millisec() <= start+secs;){
+ a := getinput(d,1);
+ if(len a == 0){
+ if(limit){
+ sys->sleep(1);
+ continue;
+ }
+ break;
+ }
+ if(a[0] == byte '\n' || a[0] == byte '\r' || limit == 0){
+ if (len s) {
+ if (s[(len s)-1] == '\r')
+ s[(len s)-1] = '\n';
+ sys->print("<-%s\n",s);
+ }
+ if(substr != nil && contains(s, substr))
+ found = 1;
+ for(k := 0; k < len msgs; k++)
+ if(len s >= len msgs[k].text &&
+ s[0:len msgs[k].text] == msgs[k].text){
+ if(found)
+ return (Found, s);
+ return (msgs[k].code, s);
+ }
+ start = sys->millisec();
+ s = "";
+ continue;
+ }
+ s[len s] = int a[0];
+ limit--;
+ }
+ s = "No response from modem";
+ if(found)
+ return (Found, s);
+
+ return (Noise, s);
+}
+
+#
+# get baud rate from a connect message
+#
+
+getspeed(msg: string, speed: int): int
+{
+ p := msg[7:]; # skip "CONNECT"
+ while(p[0] == ' ' || p[0] == '\t')
+ p = p[1:];
+ s := int p;
+ if(s <= 0)
+ return speed;
+ else
+ return s;
+}
+
+#
+# set speed and RTS/CTS modem flow control
+#
+
+setspeed(d: ref Device, baud: int)
+{
+ if(d != nil && d.ctl != nil){
+ sys->fprint(d.ctl, "b%d", baud);
+ sys->fprint(d.ctl, "m1");
+ }
+}
+
+dumpa(a: array of byte): string
+{
+ s := "";
+ for(i:=0; i<len a; i++){
+ b := int a[i];
+ if(b >= ' ' && b < 16r7f)
+ s[len s] = b;
+ else
+ s += sys->sprint("\\%.2x", b);
+ }
+ return s;
+}
+
+monitoring(d: ref Device)
+{
+ # if no monitor then spawn one
+ if(d.pid == 0) {
+ pidc := chan of int;
+ spawn monitor(d, pidc);
+ d.pid = <-pidc;
+ }
+}
+
+#
+# a process to read input from a modem.
+#
+monitor(d: ref Device, pidc: chan of int)
+{
+ openserial(d);
+ pidc <-= sys->pctl(0, nil); # pidc can be written once only.
+ a := array[Sys->ATOMICIO] of byte;
+ for(;;) {
+ d.lock.obtain();
+ d.status = "Idle";
+ d.remote = "";
+ setspeed(d, d.speed);
+ d.lock.release();
+ # shuttle bytes
+ while((n := sys->read(d.data, a, len a)) > 0){
+ d.lock.obtain();
+ if (len d.avail < Sys->ATOMICIO) {
+ na := array[len d.avail + n] of byte;
+ na[0:] = d.avail[0:];
+ na[len d.avail:] = a[0:n];
+ d.avail = na;
+ }
+ d.lock.release();
+ }
+ # on an error, try reopening the device
+ d.data = nil;
+ d.ctl = nil;
+ openserial(d);
+ }
+}
+
+#
+# return up to n bytes read from the modem by monitor()
+#
+getinput(d: ref Device, n: int): array of byte
+{
+ if(d==nil || n <= 0)
+ return nil;
+ a: array of byte;
+ d.lock.obtain();
+ if(len d.avail != 0){
+ if(n > len d.avail)
+ n = len d.avail;
+ a = d.avail[0:n];
+ d.avail = d.avail[n:];
+ }
+ d.lock.release();
+ return a;
+}
+
+getc(m: ref Device, timo: int): int
+{
+ start := sys->millisec();
+ while((b := getinput(m, 1)) == nil) {
+ if (timo && sys->millisec() > start+timo)
+ return 0;
+ sys->sleep(1);
+ }
+ return int b[0];
+}
+
+init(modeminfo: ref ModemInfo): ref Device
+{
+ if (sys == nil) {
+ sys = load Sys Sys->PATH;
+ lock = load Lock Lock->PATH;
+ if (lock == nil) {
+ raise "fail: Couldn't load lock module";
+ return nil;
+ }
+ lock->init();
+ }
+
+ newdev := ref Device;
+ newdev.lock = Semaphore.new();
+ newdev.local = modeminfo.path;
+ newdev.pid = 0;
+ newdev.t = modeminfo;
+
+ return newdev;
+}
+
+
+#
+# dial a number
+#
+dial(d: ref Device, number: string)
+{
+ if (d==nil) {
+ raise "fail: Device not initialized";
+ return;
+ }
+
+ monitoring(d);
+
+ # modem type should already be established, but just in case
+ sys->print("Attention\n");
+ x := attention(d);
+ if (x != Ok)
+ sys->print("Attention failed\n");
+ #
+ # extended Hayes commands, meaning depends on modem (VGA all over again)
+ #
+ sys->print("Init\n");
+ if(d.t.country != nil)
+ applyspecial(d, d.t.country);
+
+ if(d.t.init != nil)
+ applyspecial(d, d.t.init);
+
+ if(d.t.other != nil)
+ applyspecial(d, d.t.other);
+
+ applyspecial(d, d.t.errorcorrection);
+
+ compress := Abort;
+ if(d.t.mnponly != nil)
+ compress = applyspecial(d, d.t.mnponly);
+ if(d.t.compression != nil)
+ compress = applyspecial(d, d.t.compression);
+
+ rateadjust := Abort;
+ if(compress != Ok)
+ rateadjust = applyspecial(d, d.t.rateadjust);
+ applyspecial(d, d.t.flowctl);
+
+ # finally, dialout
+ sys->print("Dialing\n");
+ if((dt := d.t.dialtype) == nil)
+ dt = "ATDT";
+ if(send(d, sys->sprint("%s%s\r", dt, number)) < 0) {
+ raise "can't dial "+number;
+ return;
+ }
+
+ (i, msg) := readmsg(d, 120, nil);
+ if(i != Success) {
+ raise "fail: "+msg;
+ return;
+ }
+
+ connectspeed := getspeed(msg, d.speed);
+
+ # change line rate if not compressing
+ if(rateadjust == Ok)
+ setspeed(d, connectspeed);
+
+ if(d.ctl != nil){
+ if(d != nil)
+ sys->fprint(d.ctl, "s%d", connectspeed); # set DCE speed (if device implements it)
+ sys->fprint(d.ctl, "c1"); # enable CD monitoring
+ }
+}
diff --git a/appl/cmd/ip/ppp/modem.m b/appl/cmd/ip/ppp/modem.m
new file mode 100644
index 00000000..9a99acf8
--- /dev/null
+++ b/appl/cmd/ip/ppp/modem.m
@@ -0,0 +1,41 @@
+Modem: module
+{
+ PATH: con "/dis/ip/ppp/modem.dis";
+
+ ModemInfo: adt {
+ path: string;
+ init: string;
+ country: string;
+ other: string;
+ errorcorrection:string;
+ compression: string;
+ flowctl: string;
+ rateadjust: string;
+ mnponly: string;
+ dialtype: string;
+ };
+
+ Device: adt {
+ lock: ref Lock->Semaphore;
+ # modem stuff
+ ctl: ref Sys->FD;
+ data: ref Sys->FD;
+
+ local: string;
+ remote: string;
+ status: string;
+ speed: int;
+ t: ref ModemInfo;
+ # input reader
+ avail: array of byte;
+ pid: int;
+ };
+
+ init: fn(i: ref ModemInfo): ref Device;
+ dial: fn( m: ref Device, number: string);
+ getc: fn(m: ref Device, timout: int): int;
+ getinput: fn(m: ref Device, n: int ): array of byte;
+ send: fn(m: ref Device, x: string): int;
+ close: fn(m: ref Device): ref Sys->Connection;
+ onhook: fn(m: ref Device);
+};
diff --git a/appl/cmd/ip/ppp/pppclient.b b/appl/cmd/ip/ppp/pppclient.b
new file mode 100644
index 00000000..be321b59
--- /dev/null
+++ b/appl/cmd/ip/ppp/pppclient.b
@@ -0,0 +1,216 @@
+implement PPPClient;
+
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+
+include "lock.m";
+include "modem.m";
+include "script.m";
+
+include "pppclient.m";
+
+include "translate.m";
+ translate : Translate;
+ Dict : import translate;
+ dict : ref Dict;
+
+#
+# Globals (these will have to be removed if we are going multithreaded)
+#
+
+pid := 0;
+modeminfo: ref Modem->ModemInfo;
+pppdir: string;
+
+ppplog(log: chan of int, errfile: string, pidc: chan of int)
+{
+ pidc <-= sys->pctl(0, nil); # set reset pid to our pid
+ src := sys->open(errfile, Sys->OREAD);
+ if (src == nil)
+ raise sys->sprint("fail: Couldn't open %s: %r", errfile);
+
+ LOGBUFMAX: con 1024;
+ buf := array[LOGBUFMAX] of byte;
+ connected := 0;
+
+ while ((count := sys->read(src, buf, LOGBUFMAX)) > 0) {
+ (n, toklist) := sys->tokenize(string buf[:count],"\n");
+ for (;toklist != nil;toklist = tl toklist) {
+ case hd toklist {
+ "no error" =>
+ log <-= s_SuccessPPP;
+ lasterror = nil;
+ connected = 1;
+ "permission denied" =>
+ lasterror = X("Username or Password Incorrect");
+ log <-= s_Error;
+ "write to hungup channel" =>
+ lasterror = X("Remote Host Hung Up");
+ log <-= s_Error;
+ * =>
+ lasterror = X(hd toklist);
+ log <-= s_Error;
+ }
+ }
+ }
+ if(count == 0 && connected && lasterror == nil){ # should change ip/pppmedium.c instead?
+ lasterror = X("Lost Connection");
+ log <-= s_Error;
+ }
+}
+
+startppp(logchan: chan of int, pppinfo: ref PPPInfo)
+{
+ ifd := sys->open("/net/ipifc/clone", Sys->ORDWR);
+ if (ifd == nil)
+ raise "fail: Couldn't open /net/ipifc/clone";
+
+ buf := array[32] of byte;
+ n := sys->read(ifd, buf, len buf);
+ if(n <= 0)
+ raise "fail: can't read from /net/ipifc/clone";
+
+ pppdir = "/net/ipifc/" + string buf[0:n];
+ pidc := chan of int;
+ spawn ppplog(logchan, pppdir + "/err", pidc);
+ pid = <-pidc;
+ logchan <-= s_StartPPP;
+
+ if (pppinfo.ipaddr == nil)
+ pppinfo.ipaddr = "-";
+# if (pppinfo.ipmask == nil)
+# pppinfo.ipmask = "255.255.255.255";
+ if (pppinfo.peeraddr == nil)
+ pppinfo.peeraddr = "-";
+ if (pppinfo.maxmtu == nil)
+ pppinfo.maxmtu = "512";
+ if (pppinfo.username == nil)
+ pppinfo.username = "-";
+ if (pppinfo.password == nil)
+ pppinfo.password = "-";
+ framing := "1";
+
+ ifc := "bind ppp "+modeminfo.path+" "+ pppinfo.ipaddr+" "+pppinfo.peeraddr+" "+pppinfo.maxmtu
+ +" "+framing+" "+pppinfo.username+" "+pppinfo.password;
+
+ # send the add command
+ if (sys->fprint(ifd, "%s", ifc) < 0) {
+ sys->print("pppclient: couldn't write %s/ctl: %r\n", pppdir);
+ raise "fail: Couldn't write /net/ipifc";
+ return;
+ }
+}
+
+connect(mi: ref Modem->ModemInfo, number: string,
+ scriptinfo: ref Script->ScriptInfo, pppinfo: ref PPPInfo, logchan: chan of int)
+{
+ sys = load Sys Sys->PATH;
+
+ translate = load Translate Translate->PATH;
+ if (translate != nil) {
+ translate->init();
+ dictname := translate->mkdictname("", "pppclient");
+ (dict, nil) = translate->opendict(dictname);
+ }
+ if (pid != 0) # yikes we are already running
+ reset();
+
+ # create a new process group
+ pid = sys->pctl( Sys->NEWPGRP, nil);
+
+ {
+ logchan <-= s_Initialized;
+
+ # open & init the modem
+ modeminfo = mi;
+ modem := load Modem Modem->PATH;
+ if (modem == nil) {
+ raise "fail: Couldn't load modem module";
+ return;
+ }
+
+ modemdev := modem->init(modeminfo);
+ logchan <-= s_StartModem;
+ modem->dial(modemdev, number);
+ logchan <-= s_SuccessModem;
+
+ # if script
+ if (scriptinfo != nil) {
+ script := load Script Script->PATH;
+ if (script == nil) {
+ raise "fail: Couldn't load script module";
+ return;
+ }
+ logchan <-= s_StartScript;
+ script->execute(modem, modemdev, scriptinfo);
+ logchan <-= s_SuccessScript;
+ }
+
+ mc := modem->close(modemdev); # keep connection open for ppp mode
+ modemdev = nil;
+ modem = nil; # unload modem module
+
+ # if ppp
+ if (pppinfo != nil)
+ startppp(logchan, pppinfo);
+ else
+ logchan <-= s_Done;
+ }
+ exception e{
+ "fail*" =>
+ lasterror = e;
+ sys->print("PPPclient: fatal exception: %s\n", e);
+ logchan <-= s_Error;
+ kill(pid, "killgrp");
+ exit;
+ }
+}
+
+reset()
+{
+ sys->print("reset...");
+ if(pid != 0){
+ kill(pid, "killgrp");
+ pid = 0;
+ }
+
+ if(pppdir != nil){ # shut down the PPP link
+ fd := sys->open(pppdir + "/ctl", Sys->OWRITE);
+ if(fd == nil || sys->fprint(fd, "unbind") < 0)
+ sys->print("pppclient: can't unbind: %r\n");
+ fd = nil;
+ pppdir = nil;
+ }
+
+ modem := load Modem Modem->PATH;
+ if (modem == nil) {
+ raise "fail: Couldn't load modem module";
+ return;
+ }
+ modemdev := modem->init(modeminfo);
+ if(modemdev != nil)
+ modem->onhook(modemdev);
+ modem = nil;
+
+ # clear error buffer
+ lasterror = nil;
+}
+
+kill(pid: int, msg: string)
+{
+ a := array of byte msg;
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil || sys->write(fd, a, len a) < 0)
+ sys->print("pppclient: can't %s %d: %r\n", msg, pid);
+}
+
+# Translate a string
+
+X(s : string) : string
+{
+ if (dict== nil) return s;
+ return dict.xlate(s);
+}
+
diff --git a/appl/cmd/ip/ppp/pppclient.m b/appl/cmd/ip/ppp/pppclient.m
new file mode 100644
index 00000000..23396af4
--- /dev/null
+++ b/appl/cmd/ip/ppp/pppclient.m
@@ -0,0 +1,31 @@
+
+PPPClient: module {
+ PATH: con "/dis/ip/ppp/pppclient.dis";
+
+ PPPInfo: adt {
+ ipaddr: string;
+ ipmask: string;
+ peeraddr: string;
+ maxmtu: string;
+ username: string;
+ password: string;
+ };
+
+ connect: fn( mi: ref Modem->ModemInfo, number: string,
+ scriptinfo: ref Script->ScriptInfo,
+ pppinfo: ref PPPInfo, logchan: chan of int);
+ reset: fn();
+
+ lasterror :string;
+
+ s_Error: con -666;
+ s_Initialized, # Module Initialized
+ s_StartModem, # Modem Initialized
+ s_SuccessModem, # Modem Connected
+ s_StartScript, # Script Executing
+ s_SuccessScript, # Script Executed Sucessfully
+ s_StartPPP, # PPP Started
+ s_LoginPPP, # CHAP/PAP Authentication
+ s_SuccessPPP, # PPP Session Established
+ s_Done: con iota; # PPPClient Cleaningup & Exiting
+};
diff --git a/appl/cmd/ip/ppp/pppdial.b b/appl/cmd/ip/ppp/pppdial.b
new file mode 100644
index 00000000..ec689dc1
--- /dev/null
+++ b/appl/cmd/ip/ppp/pppdial.b
@@ -0,0 +1,283 @@
+implement PPPdial;
+
+#
+# Module: ispservice
+# Purpose: Simple PPP Dial-on-Demand
+# Author: Eric Van Hensbergen (ericvh@lucent.com)
+#
+# Copyright © 1998-1999 Lucent Technologies Inc. All rights reserved.
+# Revisions copyright © 2000-2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+
+include "cfgfile.m";
+ cfg: CfgFile;
+ ConfigFile: import cfg;
+
+include "lock.m";
+include "modem.m";
+include "script.m";
+include "pppclient.m";
+ ppp: PPPClient;
+include "pppgui.m";
+
+PPPdial: module
+{
+ init: fn(nil: ref Draw->Context): string;
+ connect: fn(): string;
+};
+
+context: ref Draw->Context;
+modeminfo: ref Modem->ModemInfo;
+pppinfo: ref PPPClient->PPPInfo;
+scriptinfo: ref Script->ScriptInfo;
+isp_number: string; # should be part of pppinfo
+lastCdir: ref Sys->Dir; # state of file when last read
+
+DEFAULT_ISP_DB_PATH: con "/services/ppp/isp.cfg"; # contains pppinfo & scriptinfo
+DEFAULT_MODEM_DB_PATH: con "/services/ppp/modem.cfg"; # contains modeminfo
+MODEM_DB_PATH: con "/usr/inferno/config/modem.cfg"; # contains modeminfo
+ISP_DB_PATH: con "/usr/inferno/config/isp.cfg"; # contains pppinfo & scriptinfo
+ISP_RETRIES: con 5;
+
+getcfgstring(c: ref ConfigFile, key: string) :string
+{
+ l := c.getcfg(key);
+ if (l == nil)
+ return nil;
+ for(ret := ""; l != nil; l = tl l)
+ ret+= " " + hd l;
+
+ return ret[1:]; # trim the first space
+}
+
+configinit()
+{
+ mi: Modem->ModemInfo;
+ pppi: PPPClient->PPPInfo;
+ info: list of string;
+
+ cfg = load CfgFile CfgFile->PATH;
+ if (cfg == nil)
+ raise "fail: load CfgFile";
+
+ # Modem Configuration
+
+ cfg->verify(DEFAULT_MODEM_DB_PATH, MODEM_DB_PATH);
+ modemcfg := cfg->init(MODEM_DB_PATH);
+ if (modemcfg == nil)
+ raise "fail: read: "+MODEM_DB_PATH;
+ modeminfo = ref mi;
+
+ modeminfo.path = getcfgstring(modemcfg, "PATH");
+ modeminfo.init = getcfgstring(modemcfg, "INIT");
+ modeminfo.country = getcfgstring(modemcfg, "COUNTRY");
+ modeminfo.other = getcfgstring(modemcfg, "OTHER");
+ modeminfo.errorcorrection = getcfgstring(modemcfg,"CORRECT");
+ modeminfo.compression = getcfgstring(modemcfg,"COMPRESS");
+ modeminfo.flowctl = getcfgstring(modemcfg,"FLOWCTL");
+ modeminfo.rateadjust = getcfgstring(modemcfg,"RATEADJ");
+ modeminfo.mnponly = getcfgstring(modemcfg,"MNPONLY");
+ modeminfo.dialtype = getcfgstring(modemcfg,"DIALING");
+ if(modeminfo.dialtype!="ATDP")
+ modeminfo.dialtype="ATDT";
+
+ cfg->verify(DEFAULT_ISP_DB_PATH, ISP_DB_PATH);
+ (ok, stat) := sys->stat(ISP_DB_PATH);
+ if(ok >= 0)
+ lastCdir = ref stat;
+ sys->print("cfg->init(%s)\n", ISP_DB_PATH);
+
+ # ISP Configuration
+ pppcfg := cfg->init(ISP_DB_PATH);
+ if (pppcfg == nil)
+ raise "fail: Couldn't load ISP configuration file: "+ISP_DB_PATH;
+ pppinfo = ref pppi;
+ isp_number = getcfgstring(pppcfg, "NUMBER");
+ pppinfo.ipaddr = getcfgstring(pppcfg,"IPADDR");
+ pppinfo.ipmask = getcfgstring(pppcfg,"IPMASK");
+ pppinfo.peeraddr = getcfgstring(pppcfg,"PEERADDR");
+ pppinfo.maxmtu = getcfgstring(pppcfg,"MAXMTU");
+ pppinfo.username = getcfgstring(pppcfg,"USERNAME");
+ pppinfo.password = getcfgstring(pppcfg,"PASSWORD");
+
+ info = pppcfg.getcfg("SCRIPT");
+ if (info != nil) {
+ scriptinfo = ref Script->ScriptInfo;
+ scriptinfo.path = hd info;
+ scriptinfo.username = pppinfo.username;
+ scriptinfo.password = pppinfo.password;
+ } else
+ scriptinfo = nil;
+
+ info = pppcfg.getcfg("TIMEOUT");
+ if (info != nil)
+ scriptinfo.timeout = int (hd info);
+
+ cfg = nil; # might as well unload it
+}
+
+#
+# Parts of the following two functions could be generalized
+#
+
+isipaddr(a: string): int
+{
+ i, c, ac, np: int = 0;
+
+ for(i = 0; i < len a; i++) {
+ c = a[i];
+ if(c >= '0' && c <= '9') {
+ np = 10*np + c - '0';
+ continue;
+ }
+ if (c == '.' && np) {
+ ac++;
+ if (np > 255)
+ return 0;
+ np = 0;
+ continue;
+ }
+ return 0;
+ }
+ return np && np < 256 && ac == 3;
+}
+
+# check if there is an existing PPP connection
+connected(): int
+{
+ ifd := sys->open("/net/ipifc", Sys->OREAD);
+ if(ifd == nil)
+ return 0;
+
+ buf := array[1024] of byte;
+
+ for(;;) {
+ (n, d) := sys->dirread(ifd);
+ if (n <= 0)
+ return 0;
+ for(i := 0; i < n; i++)
+ if(d[i].name[0] <= '9') {
+ sfd := sys->open("/net/ipifc/"+d[i].name+"/status", Sys->OREAD);
+ if (sfd == nil)
+ continue;
+ ns := sys->read(sfd, buf, len buf);
+ if (ns <= 0)
+ continue;
+ (nflds, flds) := sys->tokenize(string buf[0:ns], " \t\r\n");
+ if(nflds < 4)
+ continue;
+ if (isipaddr(hd tl tl flds))
+ return 1;
+ }
+ }
+}
+
+#
+# called once when loaded
+#
+init(ctxt: ref Draw->Context): string
+{
+ sys = load Sys Sys->PATH;
+ {
+ ppp = load PPPClient PPPClient->PATH;
+ if (ppp == nil)
+ raise "fail: Couldn't load ppp module";
+
+ # Contruct Config Tables During Init - may want to change later
+ # for multiple configs (Software Download Server versus ISP)
+ configinit();
+ context = ctxt;
+ }exception e {
+ "fail:*" =>
+ return e;
+ }
+ return nil;
+}
+
+dialup_cancelled := 0;
+connecting := 0;
+
+#
+# called each time a translation is needed, to check that we're on line(!)
+# eventually this will be replaced by a packet interface that does dial-on-demand
+#
+connect(): string
+{
+ {
+ dialup_cancelled = 0;
+ (ok, stat) := sys->stat(ISP_DB_PATH);
+ if (ok < 0 || lastCdir == nil || !samefile(*lastCdir, stat))
+ configinit();
+ errc := chan of string;
+ while(!connected()){
+ if(!connecting) {
+ connecting = 1;
+ sync := chan of int;
+ spawn pppconnect(errc, sync);
+ <- sync;
+ return <-errc;
+ }else{
+ sys->sleep(2500);
+ if (dialup_cancelled)
+ return "fail: dialup cancelled";
+ }
+ }
+ }exception e{
+ "fail:*" =>
+ return e;
+ "*" =>
+ sys->print("pppdial: caught exception: %s\n", e);
+ return "fail: internal error: "+e;
+ }
+ return nil;
+}
+
+pppconnect(errc: chan of string, sync: chan of int)
+{
+ connecting = 1;
+ sys->pctl(Sys->NEWPGRP, nil);
+ sync <-= 0;
+ resp_chan: chan of int;
+ logger := chan of int;
+ pppgui := load PPPGUI PPPGUI->PATH;
+ for (count :=0; count < ISP_RETRIES; count++) {
+ resp_chan = pppgui->init(context, logger, ppp, nil);
+ spawn ppp->connect(modeminfo, isp_number, scriptinfo, pppinfo, logger);
+ x := <-resp_chan;
+ if (x > 0) {
+ if (x == 1) {
+ # alt needed in case calling process has been killed
+ alt {
+ errc <-= nil => ;
+ * => ;
+ }
+ } else { # user cancelled dial-in
+ dialup_cancelled = 1;
+ alt {
+ errc <-= "fail: dialup cancelled" => ;
+ * => ;
+ }
+ }
+ connecting = 0;
+ return;
+ }
+ # else connect failed, go around loop to try again
+ }
+ alt {
+ errc <-= "fail: dialup failed" => ;
+ * => ;
+ }
+ connecting = 0;
+}
+
+samefile(d1, d2: Sys->Dir): int
+{
+ return d1.dev==d2.dev && d1.dtype==d2.dtype &&
+ d1.qid.path==d2.qid.path && d1.qid.vers==d2.qid.vers &&
+ d1.mtime==d2.mtime;
+}
diff --git a/appl/cmd/ip/ppp/pppgui.b b/appl/cmd/ip/ppp/pppgui.b
new file mode 100644
index 00000000..40e7e3b4
--- /dev/null
+++ b/appl/cmd/ip/ppp/pppgui.b
@@ -0,0 +1,373 @@
+#
+# Copyright © 1998 Lucent Technologies Inc. All rights reserved.
+# Revisions copyright © 2000,2001 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Originally Written by N. W. Knauft
+# Adapted by E. V. Hensbergen (ericvh@lucent.com)
+# Further adapted by Vita Nuova
+#
+
+implement PPPGUI;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+
+include "tk.m";
+ tk: Tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "translate.m";
+ translate: Translate;
+ Dict: import translate;
+ dict: ref Dict;
+
+include "lock.m";
+include "modem.m";
+include "script.m";
+include "pppclient.m";
+ ppp: PPPClient;
+
+include "pppgui.m";
+
+#Screen constants
+BBG: con "#C0C0C0"; # Background color for button
+PBG: con "#808080"; # Background color for progress bar
+LTGRN: con "#00FF80"; # Color for progress bar
+BARW: con 216; # Progress bar width
+BARH: con " 9"; # Progress bar height
+INCR: con 30; # Progress bar increment size
+N_INCR: con 7; # Number of increments in progress bar width
+BSIZE: con 25; # Icon button size
+ISIZE: con BSIZE + 4; # Icon window size
+DIALQUANTA : con 1000;
+ICONQUANTA : con 5000;
+
+#Globals
+pppquanta := DIALQUANTA;
+
+#Font
+FONT: con "/fonts/lucidasans/unicode.6.font";
+
+#Messages
+stat_msgs := array[] of {
+ "Initializing Modem",
+ "Dialling Service Provider",
+ "Logging Into Network",
+ "Executing Login Script",
+ "Script Execution Complete",
+ "Logging Into Network",
+ "Verifying Password",
+ "Connected",
+ "",
+};
+
+config_icon := array[] of {
+ "button .btn -text X -width "+string BSIZE+" -height "+string BSIZE+" -command {send tsk open} -bg "+BBG,
+ "pack .btn",
+
+ "pack propagate . no",
+ ". configure -bd 0",
+ ". unmap",
+ "update",
+};
+
+
+# Create internet connect window, spawn event handler
+init(ctxt: ref Draw->Context, stat: chan of int, pppmod: PPPClient, args: list of string): chan of int
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+
+ if (draw == nil || tk == nil || tkclient == nil) {
+ sys->fprint(sys->fildes(2), "pppgui: can't load Draw or Tk: %r\n");
+ return nil;
+ }
+
+ translate = load Translate Translate->PATH;
+ if(translate != nil) {
+ translate->init();
+ dictname := translate->mkdictname("", "pppgui");
+ dicterr: string;
+ (dict, dicterr) = translate->opendict(dictname);
+ if(dicterr != nil)
+ sys->fprint(sys->fildes(2), "pppgui: can't open %s: %s\n", dictname, dicterr);
+ }else
+ sys->fprint(sys->fildes(2), "pppgui: can't load %s: %r\n", Translate->PATH);
+ ppp = pppmod; # set the global
+
+ tkargs := "";
+
+ if (args != nil) {
+ tkargs = hd args;
+ args = tl args;
+ } else
+ tkargs="-x 340 -y 4";
+
+ tkclient->init();
+
+ (t, wmctl) := tkclient->toplevel(ctxt, tkargs, "PPP", Tkclient->Plain);
+
+ config_win := array[] of {
+ "frame .f",
+ "frame .fprog",
+
+ "canvas .cprog -bg "+PBG+" -bd 2 -width "+string BARW+" -height "+BARH+" -relief ridge",
+ "pack .cprog -in .fprog -pady 6",
+
+ "label .stat -text {"+X("Initializing connection...")+"} -width 164 -font "+FONT,
+ "pack .stat -in .f -side left -fill y -anchor w",
+
+ "button .done -text {"+X("Cancel")+"} -width 60 -command {send cmd cancel} -bg "+BBG+" -font "+FONT,
+ "pack .fprog -side bottom -expand 1 -fill x",
+ "pack .done -side right -padx 1 -pady 1 -fill y -anchor e",
+ "pack .f -side left -expand 1 -padx 5 -pady 3 -fill both -anchor w",
+
+ "pack propagate . no",
+ ". configure -bd 2 -relief raised -width "+string WIDTH,
+ "update",
+ };
+
+ for(i := 0; i < len config_win; i++)
+ tk->cmd(t, config_win[i]);
+
+ itkargs := "";
+ if (args != nil) {
+ itkargs = hd args;
+ args = tl args;
+ }
+ tkclient->onscreen(t, nil);
+ tkclient->startinput(t, "ptr" :: nil);
+
+ if (itkargs == "") {
+ x := int tk->cmd(t, ". cget x");
+ y := int tk->cmd(t, ". cget y");
+ x += WIDTH - ISIZE;
+ itkargs = "-x "+string x+" -y "+string y;
+ }
+
+ (ticon, iconctl) := tkclient->toplevel(ctxt, itkargs, "PPP", Tkclient->Plain);
+
+ for( i = 0; i < len config_icon; i++)
+ tk->cmd(ticon, config_icon[i]);
+
+ tk->cmd(ticon, "image create bitmap Network -file network.bit -maskfile network.bit");
+ tk->cmd(ticon, ".btn configure -image Network");
+ tkclient->startinput(ticon, "ptr"::nil);
+
+ chn := chan of int;
+ spawn handle_events(t, wmctl, ticon, iconctl, stat, chn);
+ return chn;
+}
+
+ppp_timer(sync: chan of int, stat: chan of int)
+{
+ for(;;) {
+ sys->sleep(pppquanta);
+ alt {
+ <-sync =>
+ return;
+ stat <-= -1 =>
+ ;
+ }
+ }
+}
+
+send(cmd: chan of string, msg: string)
+{
+ cmd <-= msg;
+}
+
+# Process events and pass disconnect cmd to calling app
+handle_events(t: ref Tk->Toplevel, wmctl: chan of string, ticon: ref Tk->Toplevel, iconctl: chan of string, stat, chn: chan of int)
+{
+ sys->pctl(Sys->NEWPGRP, nil);
+ cmd := chan of string;
+ tk->namechan(t, cmd, "cmd");
+
+ tsk := chan of string;
+ tk->namechan(ticon, tsk, "tsk");
+
+ connected := 0;
+ winmapped := 1;
+ timecount := 0;
+ xmin := 0;
+ x := 0;
+
+ iocmd := sys->file2chan("/chan", "pppgui");
+ if (iocmd == nil) {
+ sys->print("fail: pppgui: file2chan: /chan/pppgui: %r\n");
+ return;
+ }
+
+ pppquanta = DIALQUANTA;
+ sync_chan := chan of int;
+ spawn ppp_timer(sync_chan, stat);
+
+Work:
+ for(;;) alt {
+ s := <-t.ctxt.kbd =>
+ tk->keyboard(t, s);
+
+ s := <-t.ctxt.ptr =>
+ tk->pointer(t, *s);
+
+ s := <-t.ctxt.ctl or
+ s = <-t.wreq or
+ s = <-wmctl =>
+ tkclient->wmctl(t, s);
+
+ s := <-ticon.ctxt.kbd =>
+ tk->keyboard(ticon, s);
+ s := <-ticon.ctxt.ptr =>
+ tk->pointer(ticon, *s);
+ s := <-ticon.ctxt.ctl or
+ s = <-ticon.wreq or
+ s = <-iconctl =>
+ tkclient->wmctl(ticon, s);
+
+ (off, data, fid, wc) := <-iocmd.write => # remote io control
+ if (wc == nil)
+ break;
+ spawn send(cmd, string data[0:len data]);
+ wc <-= (len data, nil);
+
+ (nil, nbytes, fid, rc) := <-iocmd.read =>
+ if (rc != nil)
+ rc <-= (nil, "not readable");
+
+ press := <-cmd =>
+ case press {
+ "cancel" or "disconnect" =>
+ tk->cmd(t, ".stat configure -text 'Disconnecting...");
+ tk->cmd(t, "update");
+ ppp->reset();
+ if (!connected) {
+ # other end may have gone away
+ alt {
+ chn <-= 666 => ;
+ * => ;
+ }
+ }
+ break Work;
+ * => ;
+ }
+
+ prs := <-tsk =>
+ case prs {
+ "open" =>
+ tk->cmd(ticon, ". unmap; update");
+ tk->cmd(t, ". map; raise .; update");
+ winmapped = 1;
+ timecount = 0;
+ * => ;
+ }
+
+ s := <-stat =>
+ if (s == -1) { # just an update event
+ if(winmapped){
+ if(!connected) { # increment status bar
+ if (x < xmin+INCR) {
+ x++;
+ tk->cmd(t, ".cprog create rectangle 0 0 "+string x + BARH+" -fill "+LTGRN);
+ }
+ }else{
+ timecount++;
+ if(timecount > 1){
+ winmapped = 0;
+ timecount = 0;
+ tk->cmd(t, ". unmap; update");
+ tk->cmd(ticon, ". map; raise .; update");
+ continue;
+ }
+ }
+ tk->cmd(t, "raise .; update");
+ } else {
+ tk->cmd(ticon, "raise .; update");
+ timecount = 0;
+ }
+ continue;
+ }
+ if (s == ppp->s_Error) {
+ tk->cmd(t, ".stat configure -text '"+ppp->lasterror);
+ if (!winmapped) {
+ tk->cmd(ticon, ". unmap; update");
+ tk->cmd(t, ". map; raise .");
+ }
+ tk->cmd(t, "update");
+ sys->sleep(3000);
+ ppp->reset();
+ if (!connected)
+ chn <-= 0; # Failure
+ break Work;
+ }
+
+ if (s == ppp->s_Initialized)
+ tk->cmd(t,".cprog create rectangle 0 0 "+string BARW + BARH+" -fill "+PBG);
+
+ x = xmin = s * INCR;
+ if (xmin > BARW)
+ xmin = BARW;
+ tk->cmd(t, ".cprog create rectangle 0 0 "+string xmin + BARH+" -fill "+LTGRN);
+ tk->cmd(t, "raise .; update");
+ tk->cmd(t, ".stat configure -text '"+X(stat_msgs[s]));
+
+ if (s == ppp->s_SuccessPPP || s == ppp->s_Done) {
+ if(!connected){
+ chn <-= 1;
+ connected = 1;
+ }
+ pppquanta = ICONQUANTA;
+
+ # find and display connection speed
+ speed := findrate("/dev/modemstat", "rcvrate" :: "baud" :: nil);
+ if(speed != nil)
+ tk->cmd(t, ".stat configure -text {"+X(stat_msgs[s])+" "+speed+" bps}");
+ else
+ tk->cmd(t, ".stat configure -text {"+X(stat_msgs[s])+"}");
+ tk->cmd(t, ".done configure -text Disconnect -command 'send cmd disconnect");
+ tk->cmd(t, "update");
+ sys->sleep(2000);
+ tk->cmd(t, ". unmap; pack forget .fprog; update");
+ winmapped = 0;
+ tk->cmd(ticon, ". map; raise .; update");
+ }
+
+ tk->cmd(t, "update");
+ }
+ sync_chan <-= 1; # stop ppp_timer
+}
+
+findrate(file: string, opt: list of string): string
+{
+ fd := sys->open(file, sys->OREAD);
+ if(fd == nil)
+ return nil;
+ buf := array [1024] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 1)
+ return nil;
+ (nil, flds) := sys->tokenize(string buf[0:n], " \t\r\n");
+ for(; flds != nil; flds = tl flds)
+ for(l := opt; l != nil; l = tl l)
+ if (hd flds == hd l)
+ return hd tl flds;
+ return nil;
+}
+
+
+
+# Translate a string
+
+X(s : string) : string
+{
+ if (dict== nil) return s;
+ return dict.xlate(s);
+}
+
diff --git a/appl/cmd/ip/ppp/pppgui.m b/appl/cmd/ip/ppp/pppgui.m
new file mode 100644
index 00000000..af9ec574
--- /dev/null
+++ b/appl/cmd/ip/ppp/pppgui.m
@@ -0,0 +1,21 @@
+#
+# Copyright © 1998 Lucent Technologies Inc. All rights reserved.
+# Revisions copyright © 2000,2001 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Originally Written by N. W. Knauft
+# Adapted by E. V. Hensbergen (ericvh@lucent.com)
+# Further adapted by Vita Nuova
+#
+
+PPPGUI: module
+{
+ PATH: con "/dis/ip/ppp/pppgui.dis";
+
+ # Dimension constant for ISP Connect window
+ WIDTH: con 300;
+ HEIGHT: con 58;
+
+ init: fn(ctxt: ref Draw->Context, stat: chan of int,
+ ppp: PPPClient, args: list of string): chan of int;
+};
+
diff --git a/appl/cmd/ip/ppp/ppptest.b b/appl/cmd/ip/ppp/ppptest.b
new file mode 100644
index 00000000..e5dfced0
--- /dev/null
+++ b/appl/cmd/ip/ppp/ppptest.b
@@ -0,0 +1,86 @@
+# Last change: R 24 May 2001 11:05 am
+implement PPPTest;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+
+include "lock.m";
+include "modem.m";
+include "script.m";
+include "pppclient.m";
+include "pppgui.m";
+
+PPPTest: module {
+ init: fn(nil: ref Draw->Context, args: list of string);
+};
+usage()
+{
+ sys->print("ppptest device modem_init tel user password \n");
+ sys->print("Example: ppptest /dev/modem atw2 4125678 rome xxxxxxxx\n");
+ exit;
+
+}
+init( ctxt: ref Draw->Context, argv: list of string )
+{
+ sys = load Sys Sys->PATH;
+
+ mi: Modem->ModemInfo;
+ pi: PPPClient->PPPInfo;
+ tel : string;
+# si: Script->ScriptInfo;
+ argv = tl argv;
+ if(argv == nil)
+ usage();
+ else
+ mi.path = hd argv;
+
+ argv = tl argv;
+ if(argv == nil)
+ usage();
+ else
+ mi.init = hd argv;
+ argv = tl argv;
+ if(argv == nil)
+ usage();
+ else
+ tel = hd argv;
+ argv = tl argv;
+ if(argv == nil)
+ usage();
+ else
+ pi.username = hd argv;
+ argv = tl argv;
+ if(argv==nil)
+ usage();
+ else
+ pi.password = hd argv;
+
+
+ #si.path = "rdid.script";
+ #si.username = "ericvh";
+ #si.password = "foobar";
+ #si.timeout = 60;
+
+
+ ppp := load PPPClient PPPClient->PATH;
+
+ logger := chan of int;
+
+ spawn ppp->connect( ref mi, tel, nil, ref pi, logger );
+
+ pppgui := load PPPGUI PPPGUI->PATH;
+ respchan := pppgui->init( ctxt, logger,ppp, nil);
+
+ event := 0;
+ while (1) {
+ event =<- respchan;
+ sys->print("GUI event received: %d\n",event);
+ if (event) {
+ sys->print("success");
+ exit;
+ } else {
+ raise "fail: Couldn't connect to ISP";
+ }
+ }
+}
diff --git a/appl/cmd/ip/ppp/script.b b/appl/cmd/ip/ppp/script.b
new file mode 100644
index 00000000..8be184a4
--- /dev/null
+++ b/appl/cmd/ip/ppp/script.b
@@ -0,0 +1,168 @@
+implement Script;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "string.m";
+ str: String;
+
+include "lock.m";
+include "modem.m";
+ modem: Modem;
+
+include "script.m";
+
+delim: con "-"; # expect-send delimiter
+BUFSIZE: con (1024 * 32);
+
+execute( modmod: Modem, m: ref Modem->Device, scriptinfo: ref ScriptInfo )
+{
+ sys= load Sys Sys->PATH;
+ str= load String String->PATH;
+ if (str == nil) {
+ raise "fail: couldn't load string module";
+ return;
+ }
+ modem = modmod;
+
+ if (scriptinfo.path != nil) {
+ sys->print("Executing Script %s\n",scriptinfo.path);
+ # load the script
+ scriptinfo.content = scriptload(scriptinfo.path);
+ } else {
+ sys->print("Executing Inline Script\n");
+ }
+
+ # Check for timeout variable
+
+ if (scriptinfo.timeout == 0)
+ scriptinfo.timeout = 20;
+
+ tend := sys->millisec() + 1000*scriptinfo.timeout;
+
+ conv := scriptinfo.content;
+
+ while (conv != nil) {
+ e, s: string = nil;
+ p := hd conv;
+ conv = tl conv;
+ if (len p == 0)
+ continue;
+ sys->print("script: %s\n",p);
+ if (p[0] == '-') { # just send
+ if (len p == 1)
+ continue;
+ s = p[1:];
+ } else {
+ (n, esl) := sys->tokenize(p, delim);
+ if (n > 0) {
+ e = hd esl;
+ esl = tl esl;
+ if (n > 1)
+ s = hd esl;
+ }
+ }
+ if (e != nil) {
+ if (match(m, special(e,scriptinfo), tend-sys->millisec()) == 0) {
+ sys->print("script: match failed\n");
+ raise "fail: Script Failed";
+ return;
+ }
+ }
+ if (s != nil)
+ modem->send(m, special(s, scriptinfo));
+ }
+
+ sys->print("script: done!\n");
+}
+
+match(m: ref Modem->Device, s: string, timo: int): int
+{
+ for(;;) {
+ c := modem->getc(m, timo);
+ if (c == '\r')
+ c = '\n';
+ sys->print("%c",c);
+ if (c == 0)
+ return 0;
+ head:
+ while(c == s[0]) {
+ i := 1;
+ while(i < len s) {
+ c = modem->getc(m, timo);
+ if (c == '\r')
+ c = '\n';
+ sys->print("%c",c);
+ if(c == 0)
+ return 0;
+ if(c != s[i])
+ continue head;
+ i++;
+ }
+ return 1;
+ }
+ if(c == '~')
+ return 1; # assume PPP for now
+ }
+}
+
+#
+# Expand special script sequences
+#
+special(s: string, scriptinfo: ref ScriptInfo ): string
+{
+ if (s == "$username") # special variable
+ s = scriptinfo.username;
+ else if (s == "$password")
+ s = scriptinfo.password;
+
+ return deparse(s);
+}
+
+deparse(s : string) : string
+{
+ r: string = "";
+ for(i:=0; i < len s; i++) {
+ c := s[i];
+ if (c == '\\' && i+1 < len s) {
+ c = s[++i];
+ case c {
+ 't' => c = '\t';
+ 'n' => c = '\n';
+ 'r' => c = '\r';
+ 'b' => c = '\b';
+ 'a' => c = '\a';
+ 'v' => c = '\v';
+ '0' => c = '\0';
+ '$' => c = '$';
+ 'u' =>
+ if (i+4 < len s) {
+ i++;
+ (c, nil) = str->toint(s[i:i+4], 16);
+ i+=3;
+ }
+ }
+ }
+ r[len r] = c;
+ }
+ return r;
+}
+
+scriptload( path: string) :list of string
+{
+ dfd := sys->open(path, Sys->OREAD);
+ if (dfd == nil) {
+ raise "fail: Script file ("+path+") not found";
+ return nil;
+ }
+
+ scriptbuf := array[BUFSIZE] of byte;
+ scriptlen := sys->read(dfd, scriptbuf, len scriptbuf);
+ if(scriptlen < 0)
+ raise "fail: can't read script: "+sys->sprint("%r");
+
+ (nil, scriptlist) := sys->tokenize(string scriptbuf[0:scriptlen], "\n");
+ return scriptlist;
+}
diff --git a/appl/cmd/ip/ppp/script.m b/appl/cmd/ip/ppp/script.m
new file mode 100644
index 00000000..342d4d79
--- /dev/null
+++ b/appl/cmd/ip/ppp/script.m
@@ -0,0 +1,14 @@
+Script: module {
+ PATH: con "/dis/ip/ppp/script.dis";
+
+ ScriptInfo: adt {
+ path: string;
+ content: list of string;
+ timeout: int;
+ username: string;
+ password: string;
+ };
+
+ execute: fn( modem: Modem, m: ref Modem->Device,
+ scriptinfo: ref ScriptInfo );
+};