/* * GETTY.C * * $Header: Beta:src/uucp/src/getty/RCS/getty.c,v 1.1 90/02/02 12:13:30 dillon Exp Locker: dillon $ * * (C)Copyright 1989, Matthew Dillon, All Rights Reserved * * Uses: * GETTY:PASSWD * GETTY:LOGFILE * GETTY:Getty-Header * * GETTY * * -Sdevicenam Tells GETTY which serial.device to use, default * serial.device * * -Uunitnum Tells GETTY which unit number to use, default 0 * * -A Always talk to the modem at the first baud * rate in the -B list and ignore the baud rate * in the CONNECT message. * * -7 use SERF_7WIRE while online. * * -Bn Set baud rate. If specified multiple times the * first one is the default and remaining ones * switched to when a BREAK is received after a * connect. Also determines how CONNECT messages * are interpreted. A CONNECT with no number uses * the first -B entry while CONNECTs with numbers * use those numbers regardless of any -B entries. * * -Mc Set modem type: * c = m = multimodem * h = hays * d = dumb (no AT or +++ cmds are ever sent), * normally used with only one specified * baud rate. * * -m1 Turn on the modem speaker during dialing/connect * (default is -m0) * * -h0 Ignore CD (carrier detect), default is to use * carrier detect. * * -c0 Ignore CONNECT message (i.e. you are not connected * to a modem, usually combined with -h0). Default is * to expect a CONNECT message. * * -d0 do not use DTR to drop connection. Default is * to drop DTR to drop a connection. If this option * is chosen the +++ and ATH0 sequence will be used * to drop a connection. * * -xn Set debug level. Also causes log output to go * to stdout instead of GETTY:LOGFILE * * -0 QUIT - Kills any running getty for the specified * port. * * Any fields specified without a '-' are considered modem commands * used to initialize/reinitialize the modem. Up to 16 fields may * be specified (each is sent to the modem in 1 second intervals) */ #include #include #include #include #include #include #include #include #include #include #include #include "protos.h" #include #include "version.h" #include "log.h" IDENT(".06"); #ifndef IO_STATF_READBREAK #define IO_STATF_READBREAK (IOSTF_READBREAK<<8) #endif #define arysize(ary) (sizeof(ary)/sizeof((ary)[0])) #define ST_WAITCD 0 #define ST_CONNECT 1 #define ST_LOGIN 2 #define ST_PASSWD 3 #define ST_RUN 4 typedef struct IORequest IOR; typedef struct timerequest IOT; typedef struct IOExtSer IOSER; typedef struct MsgPort PORT; typedef struct List LIST; typedef struct Node NODE; typedef struct Message MSG; typedef void (*FPTR)(); typedef struct GMsg { struct Message Msg; short Cmd; long Data1; void *Data2; } GMsg; extern struct ProcID *RunPasswdEntry(); char *CopyRight = "(c)Copyright 1989, Matthew Dillon, All Rights Reserved\r\n"; char *ComPortName; char *DeviceName = "serial.device"; long DeviceUnit = 0; long RefCnt = 0; /* take into account ourselves */ long NullFH; char SpeakerLevel = 0; char AnswerRing = 2; /* default, answer on second ring */ char SpeakerOpt = 0; char IgnoreCD = 0; char IgnoreConnect = 0; char IgnoreDTR = 0; char BaudAdjust = 0; char DropOnExit = 1; char ModemType = 'h'; char ZeroOption = 0; /*char NoBugs = 0;*/ char Wire7 = 0; /* use 7 wire while online */ long Bauds[16] = { 9600 }; /* up 16 baud rates */ char *AtFields[16]; void *GettyTask; APTR UnitPtr; PORT *ComPort; PORT *IoSink; /* Sink for IO reqs. */ long IoSinkMask; long ComPortMask; IOT Iot; /* check-carrier */ IOSER Ios; /* parameters req */ IOSER Iosr; /* serial read-req */ IOSER Ioss; /* synchronous req */ char IotIP; /* Iot0 in progress */ char IosrIP; char ScrBuf[256]; char ConnectBuf[64]; char LoginBuf[32]; char PasswdBuf[32]; char RxBuf[32]; char HaveConnectMsg; char HaveLogin; char HavePasswd; short State; short Index; short BIndex; short CountDown; short GotOffPort; long GetStatus(); void SerPuts(); void RxStop(); void RxStart(); void xexit(); void Disconnect(); void ClearRecv(); void ResetSP(); void InitModem(); void SetParams(); void DoOptions(); void InterceptDeviceVector(); void RestoreDeviceVector(); brk() { return(0); } main(ac, av) char *av[]; { extern int IAmGetty; char termCr = 1; char termLf = 1; IAmGetty = 1; /* for LockSerialPort()/UnLockSerialPort() */ puts(CopyRight); fflush(stdout); onbreak(brk); LogProgram = "Getty"; LogWho = LoginBuf; LogFile = "Getty:LOGFILE"; PasswdFile = "Getty:Passwd"; DoOptions(ac, av); IoSink = CreatePort(NULL, 0); IoSinkMask = 1 << IoSink->mp_SigBit; ComPortName = malloc(strlen(DeviceName) + 20); sprintf(ComPortName, "Getty.%s.%ld", DeviceName, DeviceUnit); Forbid(); if (ComPort = FindPort(ComPortName)) { GMsg msg; msg.Cmd = 'O'; msg.Data1 = ac; msg.Data2 = (void *)av; msg.Msg.mn_ReplyPort = IoSink; PutMsg(ComPort, &msg.Msg); WaitPort(IoSink); Remove(&msg.Msg.mn_Node); Permit(); puts("Options updated"); xexit(0); } ComPort = CreatePort(ComPortName, 0L); Permit(); ComPortMask = 1L << ComPort->mp_SigBit; NullFH = Open("NULL:", 1006); if (NullFH == NULL) { ulog(-1, "GETTY REQUIRES NULL: HANDLER!"); puts("Requires NULL: handler!"); xexit(1); } if (LogToStdout == 0) { freopen("NULL:", "r", stdin); freopen("NULL:", "w", stdout); freopen("NULL:", "w", stderr); } /* * Timer Device */ Iot.tr_node.io_Message.mn_ReplyPort = IoSink; if (OpenDevice("timer.device", UNIT_VBLANK, &Iot, 0L)) { Iot.tr_node.io_Device = NULL; xexit(20); } Iot.tr_node.io_Command = TR_ADDREQUEST; /* * SERIAL.DEVICE */ Ios.IOSer.io_Message.mn_ReplyPort = IoSink; Ios.io_SerFlags = SERF_XDISABLED | SERF_SHARED; if (Wire7) Ios.io_SerFlags |= SERF_7WIRE; if (OpenDevice(DeviceName, DeviceUnit, &Ios, 0L)) { Ios.IOSer.io_Device = NULL; xexit(21); } InterceptDeviceVector(Ios.IOSer.io_Device); Iosr = Ios; Ioss = Ios; Iosr.IOSer.io_Command = CMD_READ; Ios.IOSer.io_Command = SDCMD_QUERY; DoIO(&Ios); Ios.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE | SERF_SHARED; Ios.io_ExtFlags = 0; Ios.io_Baud = Bauds[0]; Ios.io_ReadLen = 8; Ios.io_WriteLen = 8; Ios.io_StopBits = 1; SetParams(0); /* * Run Operation */ State = ST_WAITCD; Iot.tr_time.tv_secs = 1; Iot.tr_time.tv_micro= 0; IotIP = 1; SendIO(&Iot); InitModem(); GettyTask = FindTask(NULL); Signal(GettyTask, IoSinkMask); for (;;) { long mask; IOR *ior; GMsg *msg; mask = Wait(SIGBREAKF_CTRL_C | IoSinkMask | ComPortMask); ulog(9, "State = %d %d %08lx %08lx", State, RefCnt, UnitPtr, Ios.IOSer.io_Unit); if (mask & SIGBREAKF_CTRL_C) break; if (msg = (GMsg *)GetMsg(ComPort)) { do { switch(msg->Cmd) { case 'O': DoOptions(msg->Data1, msg->Data2); break; } ReplyMsg((MSG *)msg); if (ZeroOption) xexit(0); } while (msg = (GMsg *)GetMsg(ComPort)); if (State == ST_WAITCD && !GotOffPort) Disconnect(0); } while (ior = (IOR *)GetMsg(IoSink)) { if (ior == (IOR *)&Iot) { short diu; long status; IotIP = 0; Iot.tr_time.tv_secs = 1; Iot.tr_time.tv_micro= 0; /* * If # of references for unit is > 1 then device * is in use. When refs fall <= 1 then reset the * serial port. */ diu = DeviceInUse(); if (diu) { if (GotOffPort == 0) { ulog(1, "Device in use"); RxStop(); State = ST_WAITCD; } GotOffPort = 2; SendIO(&Iot); IotIP = 1; continue; } if (GotOffPort) { /* * delay 5 seconds before getting back on port */ if (--GotOffPort) { Iot.tr_time.tv_secs = 5; SendIO(&Iot); IotIP = 1; } else { ulog(1, "Getty resetting"); SendIO(&Iot); IotIP = 1; Disconnect(1); } continue; } if (State == ST_WAITCD) RxStop(); if (State != ST_WAITCD && IosrIP == 0) { RxStart(); ulog(2, "Carrier, Getty getting on port"); } status = GetStatus(); /* * If state connected and we loose carrier, disconnect. * * If state connected and timeout occurs disconnect. */ if (State != ST_WAITCD) { if ((status & CIAF_COMCD) != 0) { ulog(2, "Getty, Carrier lost"); Disconnect(1); } else { if (!IgnoreCD && --CountDown == 0) { ulog(2, "Getty, Timeout, Disconnecting"); Disconnect(1); status |= CIAF_COMCD; /* set lost carrier flag */ } } } switch(State) { case ST_WAITCD: if ((status & CIAF_COMCD) == 0) { State = ST_CONNECT; CountDown = 60; /* allow 60 seconds */ ulog(2, "Carrier Detect"); } else { Iot.tr_time.tv_secs = 2; } break; case ST_CONNECT: /* * Wait for CONNECT message, then send Login: */ if (IgnoreConnect && HaveConnectMsg == 0) { SetParams(1); HaveConnectMsg = 1; ulog(2, "Connect"); } if (HaveConnectMsg) { FILE *fi; Delay(50); ClearRecv(); if (fi = fopen("Getty:Getty-Header", "r")) { while (fgets(ScrBuf, sizeof(ScrBuf), fi)) { SerPuts(ScrBuf); SerPuts("\r"); } fclose(fi); } termCr = termLf = 1; ClearRecv(); SerPuts("Login: "); ulog(1, "Getty, Connect, Login"); State = ST_LOGIN; Index = 0; HaveLogin = 0; LoginBuf[0] = 0; } break; case ST_LOGIN: /* wait Login: response */ if (HaveLogin) { if (LoginBuf[0] == 0) { State = ST_CONNECT; break; } ClearRecv(); PasswdBuf[0] = 0; /* * If no password required, else request * password. */ if (CheckLoginAndPassword()) { HavePasswd = 1; Index = 0; State = ST_PASSWD; } else { SerPuts("Password: "); ulog(2, "Getty, Passwd"); State = ST_PASSWD; HavePasswd = 0; Index = 0; } } break; case ST_PASSWD: /* wait Password: response */ if (HavePasswd) { if (CheckLoginAndPassword()) { ulog(-1, "login %s", LoginBuf); /* * Disable read requests but leave serial * port locked. */ RxStop(); /* * If run successful, leave read req and * timer disabled. */ RunPasswdEntry(); if (DropOnExit) Disconnect(1); else State = ST_CONNECT; ulog(-1, "disconnect"); } else { SerPuts("Login Failed.\r\n\n"); State = ST_CONNECT; ulog(0, "LoginFailed user=%s pass=%s", LoginBuf, PasswdBuf); } HaveLogin = 0; HavePasswd= 0; LoginBuf[0] = 0; } break; } /* * Make no read requests while running 3rd party * program, else resend read request. */ if (IotIP == 0) { IotIP = 1; SendIO(&Iot); } } /* * RECEIVED SERIAL READ DATA */ if (ior == (IOR *)&Iosr) { long status; IosrIP = 0; status = GetStatus(); /* * BREAK used to switch baud rates between allowed * values */ if (status & IO_STATF_READBREAK) { if (BaudAdjust == 0 && (State == ST_LOGIN || State == ST_PASSWD)) { if (++BIndex == arysize(Bauds)) BIndex = 0; if (Bauds[BIndex] == 0) BIndex = 0; Ios.io_Baud = Bauds[BIndex]; SetParams(1); ulog(1, " to %d baud", Ios.io_Baud); Delay(50); ClearRecv(); Index = 0; State = ST_CONNECT; } } else if (Iosr.IOSer.io_Actual == 1) { char *ptr; UBYTE c = (UBYTE)RxBuf[0]; ulog(9, "Rx %02x %c", c, (c < 32) ? ' ' : c); c &= 0x7F; switch(State) { case ST_WAITCD: /* looking for CONNECT msg */ case ST_CONNECT: /* looking for CONNECT msg */ ptr = ConnectBuf; break; case ST_LOGIN: /* looking for login name */ ptr = LoginBuf; break; case ST_PASSWD: /* looking for password */ ptr = PasswdBuf; break; } if (State == ST_LOGIN && HaveLogin) c = 0; if (State == ST_PASSWD && HavePasswd) c = 0; switch(c) { case 0: break; case 8: if (State == ST_LOGIN && HaveLogin) break; if (Index) { if (State == ST_LOGIN) SerPuts("\010 \010"); --Index; } break; case 10: if (termLf == 0) break; termCr = 0; case 13: if (c == 13) { if (termCr == 0) break; else termLf = 0; } ptr[Index] = 0; Index = 0; switch(State) { case ST_WAITCD: case ST_CONNECT: if (strncmp(ptr, "CONNECT", 7)) { ulog(9, "Looking for CONNECT, got '%s'", ptr); break; } Delay(50); HaveConnectMsg = 1; if (BaudAdjust) { ulog(9, "Connect Auto-Baud %d", Ios.io_Baud); } else { char *str = ptr + 7; while (*str && (*str == 9 || *str == ' ')) ++str; if (*str >= '0' && *str <= '9') Ios.io_Baud = atoi(str); else Ios.io_Baud = Bauds[0]; ulog(9, "Connect at %d baud", Ios.io_Baud); } SetParams(1); break; case ST_LOGIN: HaveLogin = 1; SerPuts("\r\n"); ulog(1, "Login: %s", ptr); break; case ST_PASSWD: HavePasswd = 1; SerPuts("\r\n"); ulog(2, "Password: %s", ptr); break; } break; default: if (Index == 31) break; if (State == ST_LOGIN) { char cc[2]; cc[0] = c; cc[1] = 0; SerPuts(cc); } ptr[Index++] = c; break; } } if (IosrIP == 0) RxStart(); } } } xexit(0); } void RxStart() { Iosr.IOSer.io_Data = (APTR)RxBuf; Iosr.IOSer.io_Length = 1; Iosr.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE; IosrIP = 1; SendIO(&Iosr); } void RxStop() { if (IosrIP) { AbortIO(&Iosr); WaitIO(&Iosr); IosrIP = 0; } } long GetStatus() { int error; long status = CIAF_COMCD; /* no carrier detect */ if (!DeviceInUse()) { Ioss.IOSer.io_Command = SDCMD_QUERY; Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE; if (error = DoIO(&Ioss)) ulog(0, "Query Error %d", error); status = Ioss.io_Status; if (IgnoreCD) status &= ~CIAF_COMCD; } return(status); } void xexit(code) { if (ComPortMask) { GMsg *msg; Forbid(); while (msg = (GMsg *)GetMsg(ComPort)) ReplyMsg((MSG *)msg); DeletePort(ComPort); Permit(); } if (IotIP) { AbortIO(&Iot); WaitIO(&Iot); } if (Iot.tr_node.io_Device) CloseDevice(&Iot); RxStop(); if (Ios.IOSer.io_Device) { RestoreDeviceVector(Ios.IOSer.io_Device); CloseDevice(&Ios); } if (IoSink) DeletePort(IoSink); if (NullFH) Close(NullFH); if (code) ulog(-1, "Getty Exiting with code %d", code); exit(code); } void SerPuts(str) char *str; { int error; if (!DeviceInUse()) { Ioss.IOSer.io_Command = CMD_WRITE; Ioss.IOSer.io_Data = (APTR)str; Ioss.IOSer.io_Length = strlen(str); Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE; if (error = DoIO(&Ioss)) ulog(0, "Write Error %d", error); } } static short RxDisableIP; void RxDisable() { RxDisableIP = IosrIP; RxStop(); } void RxEnable() { if (RxDisableIP && IosrIP == 0) RxStart(); } void ClearRecv() { int error; if (!DeviceInUse()) { Ioss.IOSer.io_Command = CMD_CLEAR; Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE; if (error = DoIO(&Ioss)) ulog(0, "Clear Error %d", error); if (IosrIP) { AbortIO(&Iosr); WaitIO(&Iosr); IosrIP = 0; RxStart(); } } } void ResetSP() { int error; if (!DeviceInUse()) { Ioss.IOSer.io_Command = CMD_RESET; Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE; if (error = DoIO(&Ioss)) ulog(0, "Reset Error %d", error); } } void SetParams(wire7) { int error; /*if (NoBugs == 0) wire7 = Wire7;*/ { RxStop(); if (!DeviceInUse()) { if (wire7 && Wire7) Ios.io_SerFlags |= SERF_7WIRE; else Ios.io_SerFlags &= ~SERF_7WIRE; Ios.IOSer.io_Command = SDCMD_SETPARAMS; Ios.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE; if (error = DoIO(&Ios)) ulog(0, "SetParams Error %d", error); } } } void Disconnect(dropdtr) { short retry = (IgnoreDTR) ? 2 : 10; ulog(2, "Disconnect drop=%d", dropdtr); HaveConnectMsg = 0; HaveLogin = 0; HavePasswd = 0; LoginBuf[0] = 0; PasswdBuf[0] = 0; Index = 0; State = ST_WAITCD; if (IgnoreCD == 0) { while (dropdtr && DeviceInUse() == 0) { short i; long status = GetStatus(); if (status & CIAF_COMCD) /* no carrier */ break; RxDisable(); SetParams(0); if (IgnoreDTR) { if (ModemType != 'd') { Delay(70); if (DeviceInUse()) break; SerPuts("+++"); Delay(70); if (DeviceInUse()) break; SerPuts("\010\010\r"); Delay(10); if (DeviceInUse()) break; SerPuts("ATH0\r"); Delay(120); } } else { RefCnt += 2; CloseDevice(&Ios); Ios.IOSer.io_Device = NULL; /* so xexit doesn't reclose */ for (i = 0; i < 5; ++i) { /* 5 seconds */ Delay(50); if (SetSignal(SIGBREAKF_CTRL_C, 0) & SIGBREAKF_CTRL_C) xexit(23); } /* * Use Iosr to re-open serial device so we don't loose * our config. */ RefCnt -= 2; if (Wire7) Ios.io_SerFlags |= SERF_7WIRE; else Ios.io_SerFlags &= ~SERF_7WIRE; if (OpenDevice(DeviceName, DeviceUnit, &Ios, 0)) { Ios.IOSer.io_Device = NULL; xexit(22); } Iosr.IOSer.io_Device = Ios.IOSer.io_Device; Ioss.IOSer.io_Device = Ios.IOSer.io_Device; Iosr.IOSer.io_Unit = Ios.IOSer.io_Unit; Ioss.IOSer.io_Unit = Ios.IOSer.io_Unit; SetParams(1); } /* * Loop until carrier lost */ RxEnable(); if (--retry == 0) { if (IgnoreDTR == 0) puts("Getty: unable to disconnect!"); break; } } } if (!DeviceInUse()) { ResetSP(); InitModem(); } } void InitModem() { char buf[64]; short i; RxDisable(); ulog(2, "Init Modem"); Ios.io_Baud = Bauds[0]; /* reset baud rate */ BIndex = 0; SetParams(0); RxEnable(); switch(ModemType) { case 'm': /* Multi Modem */ SerPuts("\010\010\r"); Delay(10); if (DeviceInUse()) break; SerPuts("AT\r"); Delay(50); if (DeviceInUse()) break; sprintf(buf, "ATM%dS0=%dX4$BA%d&E%d\r", SpeakerLevel, AnswerRing, !BaudAdjust, (Wire7) ? 4 : 3 ); SerPuts(buf); break; case 'h': SerPuts("\010\010\r"); Delay(10); if (DeviceInUse()) break; SerPuts("ATZ\r"); Delay(120); if (DeviceInUse()) break; strcpy(buf, "AT"); if (SpeakerOpt) sprintf(buf + strlen(buf), "M%d", SpeakerLevel); sprintf(buf + strlen(buf), "S0=%d", AnswerRing); strcat(buf, "\r"); SerPuts(buf); break; case 'd': SerPuts("\010\010\r"); break; } for (i = 0; i < arysize(AtFields) && AtFields[i]; ++i) { Delay(50); if (DeviceInUse()) break; SerPuts(AtFields[i]); SerPuts("\r"); } Delay(20); ClearRecv(); Index = 0; } void DoOptions(ac, av) char *av[]; { short i; short bi = 0; short fi = 0; long v; LogLevel = 1; for (i = 1; i < ac; ++i) { char *ptr = av[i]; if (*ptr != '-') { if (fi != arysize(AtFields)) AtFields[fi++] = ptr; else puts("AT field overflow"); continue; } if (*++ptr) /* skip - */ ++ptr; /* and option */ v = atoi(ptr); switch(ptr[-1]) { case '0': ZeroOption = 1; break; case '7': Wire7 = 1; break; case 'S': DeviceName = ptr; break; case 'U': DeviceUnit = v; break; case 'M': ModemType = *ptr; break; case 'A': BaudAdjust = 1; break; case 'B': if (bi != arysize(Bauds)) Bauds[bi++] = v; else puts("-B field overflow"); break; case 'm': SpeakerOpt = 1; SpeakerLevel = v; break; case 'r': AnswerRing = v; break; case 'h': IgnoreCD = !v; break; case 'c': IgnoreConnect = !v; break; case 'd': IgnoreDTR = !v; break; case 'x': LogLevel = v; LogToStdout = (v >= 9); break; case 'n': /*NoBugs = 1;*/ break; default: printf("Warning, Bad option: -%s\n", ptr); break; } } if (fi && fi != arysize(AtFields)) AtFields[fi] = NULL; if (bi && bi != arysize(Bauds)) Bauds[bi] = 0; } DeviceInUse() { return(RefCnt > 0); } /* * Device Vector Intercept, used to force SERF_SHARED on device open. */ extern void AsmOpenIntercept(); extern void AsmCloseIntercept(); /*extern void A2232BeginIOFix();*/ FPTR OldOpenVector; FPTR OldCloseVector; /*FPTR OldBeginIOVector;*/ void InterceptDeviceVector(dev) struct Library *dev; { Forbid(); OldOpenVector = SetFunction((struct Library *)dev, LIB_OPEN, AsmOpenIntercept); OldCloseVector = SetFunction((struct Library *)dev, LIB_CLOSE, AsmCloseIntercept); /*OldBeginIOVector = SetFunction((struct Library *)dev, DEV_BEGINIO, A2232BeginIOFix);*/ Permit(); } void RestoreDeviceVector(dev) struct Library *dev; { FPTR oldFunc; int error = 0; /* * must restore in same order as had openned */ for (;;) { Forbid(); oldFunc = (FPTR)SetFunction((struct Library *)dev, LIB_OPEN, OldOpenVector); if (oldFunc != (FPTR)AsmOpenIntercept) { SetFunction((struct Library *)dev, LIB_OPEN, oldFunc); Permit(); if (error == 0) { error = 1; ulog(-1, "UNABLE TO REVERSE SETVECTOR, YOU MUST DEINSTALL"); ulog(-1, "GETTYs ON THE SAME DEVICE IN REVERSE ORDER!"); } Delay(50 * 5); continue; } break; } SetFunction((struct Library *)dev, LIB_CLOSE, OldCloseVector); /*SetFunction((struct Library *)dev, DEV_BEGINIO, OldBeginIOVector);*/ Permit(); }