/* * usage: term [-s speed] [-l line] [-e escape-char] [-c callscript] * Robert de Bath, rd103925@mayday.cix.co.uk * [ small change by M.Andreoli ] * defaults: * speed: Higest of 115200, 38400 and 2400 * line : /dev/ttyS1 * escape: ~ * escape char may be specified as three octal digits * * term escapes: * escape-. or escape-CTRLD terminate * escape-! escape to shell * escape-# send break * escape-escape send escape char */ #include #include #include #include #include #include #include #include #ifdef V7 # include #else # if defined(__linux__) || defined(HAVE_TERMIOS) # include # ifndef HAVE_TERMIOS # define HAVE_TERMIOS # endif # else # include # endif # ifndef SYSV # define SYSV # endif #endif #define XSCRIPT #define XLOGFILE #define XCHARXLATE #ifdef B115200 #define SPEED B115200 /* default speed */ #else #ifdef B38400 #define SPEED B38400 /* default speed */ #else #define SPEED B2400 /* default speed */ #endif #endif #define LINE "/dev/ttyS1" /* the default line */ #define ESC '~' /* default esc char */ #define EOT '\004' /* ^D */ #ifndef R_OK /* access() modes */ #define R_OK 4 #define W_OK 2 #endif char esc[2] = { ESC, '\0' }; extern void s_line(); /* set & save line characteristics */ extern void r_line(); /* reset saved line characteristics */ int wr_pid = 0; /* writer process id */ int line_fd; /* comm line file desc */ int tty_fd; /* console file desc */ char line_device[64]; #ifdef __STDC__ #define sigtype void #else #define sigtype int #endif sigtype cleanup(); sigtype open_timeout(); #ifdef CHARXLATE int Termtype = 2; #else int Termtype = 0; #endif int Bit_mask = 0xFF; /* Strip high bit from line */ int Parity = 0; /* Add parity to line */ #ifdef LOGFILE FILE * lfile = 0; char logfname[64] = "tty/ttylog%d"; char ltbuf[10240]; char * ltptr = 0; #endif #ifdef SCRIPT int in_script = 0; char script_name[128]; char script_args[128]; struct script_sv { struct script_sv * next; FILE * scrfd; char script_args[128]; } * saved_sc = 0; int verbose = 0; #endif sigtype cleanup() { int status; r_line(); if( wr_pid ) kill(wr_pid, SIGTERM); wait(&status); #ifdef LOGFILE if( lfile ) fclose(lfile); lfile = 0; #endif if( wr_pid ) fprintf(stderr, "Disconnected\r\n"); exit(0); } void main(argc, argv) int argc; char **argv; { int done, status; int speed = SPEED; extern char *optarg; extern int optind; extern int getopt(), conv_speed(), conv_esc(), access(); strcpy(line_device, LINE); while((status = getopt(argc, argv,"s:l:e:rdm7pv")) != EOF) { switch(status) { case 's': if((speed = conv_speed(optarg)) == EOF) { fprintf(stderr,"%s: invalid speed\n", optarg); exit(1); } break; case 'l': strncpy(line_device, optarg, sizeof(line_device)); if(access(line_device, R_OK|W_OK) == -1) { perror(optarg); exit(2); } break; case 'e': esc[0] = conv_esc(optarg); break; case 'p': Parity = 1; /* FALL */ case '7': Bit_mask = 0x7F; break; case 'd': Termtype = 1; break; #ifdef CHARXLATE case 'r': Termtype = 0; break; #endif #ifdef SCRIPT case 'v': verbose++; break; #endif default: fprintf(stderr, "usage: %s [-s speed] [-l line] [-e escape-char]\n", *argv); exit(3); } } if( optind < argc ) { fprintf(stderr, "usage: %s [-s speed] [-l line] [-e escape-char]\n", *argv); exit(3); } signal(SIGALRM, open_timeout); alarm(5); #ifdef O_NDELAY if((line_fd = open(line_device, O_RDWR|O_NDELAY)) < 0) #else if((line_fd = open(line_device, O_RDWR)) < 0) #endif { perror(line_device); exit(4); } #ifdef O_NDELAY s_line(speed); { int fd = line_fd; if((line_fd = open(line_device, O_RDWR)) < 0) { perror(line_device); exit(4); } close(fd); } #endif alarm(0); signal(SIGALRM, SIG_DFL); tty_fd = fileno(stdin); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, SIG_IGN); s_line(speed); signal(SIGTERM, cleanup); #ifdef SCRIPT if( in_script ) fprintf(stderr, "Running script\r\n"); else #endif fprintf(stderr, "Connected to modem!\r\n"); fprintf(stderr, "reset with ATZ, volume with ATL1, ATL2,...\r\n"); fprintf(stderr, "call with ATDTnumber, exit with ~.\r\n"); fprintf(stderr, "\r\n"); signal(SIGCHLD, cleanup); fork_writer(); /* launch writer process */ read_tty(); cleanup(); } sigtype open_timeout() { fprintf(stderr, "Cannot open port -- No carrier\n"); cleanup(); } typedef struct { char *str; int spd; } vspeeds; static vspeeds valid_speeds[] = { { "300", B300 }, { "1200", B1200 }, { "2400", B2400 }, { "4800", B4800 }, { "9600", B9600 }, #ifdef B19200 { "19200", B19200 }, #endif #ifdef B38400 { "38400", B38400 }, #endif #ifdef B57600 { "57600", B57600 }, #endif #ifdef B115200 { "115200", B115200 }, #endif { (char *)0, 0 } }; /* * given a desired speed string, if valid return its Bspeed value * else EOF */ int conv_speed(s) register char *s; { register vspeeds *p = &valid_speeds[0]; if(*s == 0) return EOF; for(; p->str != (char *)0; p++) if(strncmp(s, p->str, strlen(s)) == 0) return p->spd; return EOF; } /* * convert given string to char * string maybe a char, or octal digits */ int conv_esc(s) register char *s; { register int val = 0; if(!isdigit(*s)) return *s; /* ! octal */ /* convert octal digs */ do { if(*s > '7' || *s < '0') { fprintf(stderr,"escape char must be character/octal digits\n"); exit(4); } val = (val << 3) + (*s++ - '0'); } while(isdigit(*s)); return val; } /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ #ifdef V7 /* This is Version 7 unix code it should work on any unix machine */ /* If you believe that you'll believe anything :-) */ struct sgttyb tty_save, line_save, tty; int tty_mode = -1; int got_line = 0; void s_line(speed) { set_mode(2); if(!got_line) { got_line = 1; ioctl(line_fd, TIOCGETP, &line_save); } tty = line_save; tty.sg_ospeed = speed; /* Set new speed */ tty.sg_ispeed = speed; /* Set new speed */ tty.sg_flags |= RAW; tty.sg_flags &= ~ECHO; /* Parity doesn't appear to work with Xenix tty.sg_flags |= EVENP; */ /* So xmit parity is done in software */ ioctl(line_fd, TIOCSETP, &tty); } void r_line() { set_mode(0); if(!got_line) return; ioctl(line_fd, TIOCSETP, &line_save); } set_mode(which) int which; { if( tty_mode == -1 ) { if(ioctl(tty_fd, TIOCGETP, &tty)) { perror("Unable to sense terminal"); exit(1); } tty_save = tty; tty_mode = 0; } tty = tty_save; switch(which) { default: /* Normal cooked */ which = 0; break; case 1: /* CBREAK */ tty.sg_flags &= ~ECHO; tty.sg_flags |= CBREAK; /* Allow break & cr/nl mapping */ break; case 2: /* RAW */ tty.sg_flags &= ~ECHO; tty.sg_flags |= RAW; break; } if(ioctl(tty_fd, TIOCSETP, &tty)) { perror("Unable to change terminal mode"); } else tty_mode = which; } #endif /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ #ifdef SYSV /* This is System V unix code */ #ifdef HAVE_TERMIOS struct termios tty_save, line_save, tty; #else struct termio tty_save, line_save, tty; #endif static int tty_mode = -1; int got_line = 0; void s_line(speed) int speed; { set_mode(2); if(!got_line) { got_line = 1; #ifndef HAVE_TERMIOS ioctl(line_fd, TCGETA, &line_save); #else tcgetattr(line_fd, &line_save); #endif } tty = line_save; tty.c_iflag &= ~(IXON|IXANY|IXOFF|ISTRIP|INLCR|ICRNL|IGNCR|IUCLC); tty.c_oflag &= ~(OLCUC|ONLCR|OCRNL|ONOCR|ONLRET); tty.c_cflag &= ~(CBAUD|PARENB); tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHONL|ISIG); #ifdef CBAUDEX tty.c_cflag &= ~(CBAUDEX); #endif tty.c_cflag &= ~(CBAUD|PARENB); tty.c_iflag |= IXOFF; /* X/Y modem will clear this */ tty.c_cflag |= CLOCAL|(speed); tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; #ifndef HAVE_TERMIOS ioctl(line_fd, TCSETA, &tty); #else tcsetattr(line_fd, TCSANOW, &tty); #endif } void r_line() { set_mode(0); if(!got_line) return; #ifndef HAVE_TERMIOS ioctl(line_fd, TCSETA, &line_save); #else tcsetattr(line_fd, TCSANOW, &line_save); #endif } set_mode(which) int which; { if( tty_mode == -1 ) { #ifndef HAVE_TERMIOS if(ioctl(tty_fd, TCGETA, &tty)) #else if(tcgetattr(tty_fd, &tty)) #endif { perror("Unable to sense terminal"); exit(1); } tty_save = tty; tty_mode = 0; } tty = tty_save; switch(which) { case 0: /* Normal cooked */ break; case 1: /* CBREAK */ tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHONL); tty.c_lflag |= ICRNL|ISIG; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; break; case 2: /* RAW */ tty.c_oflag &= ~OPOST; tty.c_iflag &= ~(ICRNL|IGNCR|INLCR); tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHONL|ICRNL|ISIG); tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; break; } #ifndef HAVE_TERMIOS if(ioctl(tty_fd, TCSETA, &tty)) #else if(tcsetattr(tty_fd, TCSANOW, &tty)) #endif { perror("Unable to change terminal mode"); } else tty_mode = which; } #endif /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ fork_writer() { #ifdef LOGFILE if( lfile ) fflush(lfile); #endif if (wr_pid = fork()) { if (wr_pid == -1) { r_line(); perror("Cannot fork writer"); exit(0); } return; } #ifdef LOGFILE if(lfile) fclose(lfile); lfile = 0; #endif signal(SIGCHLD, SIG_DFL); write_tty(); cleanup(); } write_tty() { char ch; char was_newline = 1; signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, cleanup); while (1) { read(0, &ch, 1); ch &= (char)0177; if (was_newline) { if (ch == esc[0]) { read(0, &ch, 1); ch &= (char)0177; switch (ch) { case EOT: case '.': case '>': read(0, &ch, 1); exit(0); case '!': /* tell reader to pause */ kill(getppid(), SIGUSR1); /*do_shell(); */ /* wake him up again */ if( kill(getppid(), SIGUSR2) < 0 ) exit(0); ch = '\377'; break; default: if(ch != esc[0]) { fprintf(stderr, "invalid command--use\ \"~~\" to start a line with \"~\"\r\n"); } } } } if (ch == '\r' || ch == '\004' || ch == '\003' || ch == '\177') was_newline = 1; else was_newline = 0; if( ch != '\377' ) { int i; if( Parity ) { ch &= 0x7F; for(i=0; i<7; i++) if( ch & (1< 0 ) { for(i=0;i last_ok + timeout ) { printf("\r\nTimeout ... Script aborted\r\n"); cleanup(); } return ok; } #define VALIDREC 1 /* This record is available for matching */ #define ALTMATCH 2 /* Is an extra match record for prev line */ #define SCCOM 4 /* sendstr is a script command */ #define UXCOM 8 /* sendstr is a unix command */ static struct matchrec { char match[MATCHLEN]; int matchlen; int maxcount; char * sendstr; int flags; } matchtab[MAXMATCH]; static int matcount; static check_match() { struct matchrec * sptr; struct matchrec * mptr; extern int line_fd; int i,j; int checked_one; Restart: sptr = matchtab; mptr = matchtab; checked_one = 0; for(i=0; iflags & ALTMATCH) == 0 ) sptr = mptr; if( (sptr->flags & VALIDREC) == 0 ) continue; checked_one = 1; if( (mptr->matchlen == 0 || ( buffer[offset-mptr->matchlen] == mptr->match[0] && strcmp(buffer+offset-mptr->matchlen, mptr->match) == 0 ) ) ) { time(&last_ok); offset = sizeof(buffer)/2; memset(buffer, 0, sizeof(buffer)); if( mptr->matchlen ) vdisp(0, "\r\n"); if( sptr->flags & SCCOM ) switch(script_com(sptr->sendstr)) { case 0: break; case 1: close_script(); return 0; case 2: goto Restart; } #if 0 else if( sptr->flags & UXCOM ) #endif else { vdisp(0, " '"); if( line_fd ) { /* Yes we want this ssslllooowww incase */ /* the other end is stupid */ /* Of course this isn't really that slow */ char ch[2]; ch[1] = '\0'; for(i=0; sptr->sendstr[i]; i++) { ch[0] = sptr->sendstr[i]; vdisp(1, ch); if(*ch == '\377') sleep(2); else { write(line_fd, ch, 1); #ifdef M_XENIX nap(20); #else usleep(20000L); #endif } } } vdisp(0, "'\r\n"); } time(&last_ok); if( sptr->maxcount ) { if( --(sptr->maxcount) == 0 ) { sptr->flags &= ~VALIDREC; } } else { read_matchset(); goto Restart; /* Incase this is a '- "Send"' line */ } break; } } return checked_one; } static clear_matchtab() { int i; for(i=0; iscrfd; strcpy(script_args, p->script_args); saved_sc = p->next; free(p); continue; } p = lbuf; while( *p == ' ' || *p == '\t' ) p++; if( *p == '#' || *p == '\n' || *p == '\0' ) continue; #ifdef VARS if( *p == '%' ) if( *p == '$' ) #endif memset(&new, 0, sizeof(new)); if( *p == '*' || *p == '?' ) { if( *p == '?' ) new.maxcount = 1; else new.maxcount = starcount; p++; while( *p == ' ' || *p == '\t' ) p++; } else keepgoing = 0; saveptr = matcount; do { if( *p == ',' ) p++; p = get_item(p, new.match, sizeof(new.match), 0); if( p == 0 ) { fprintf(stderr, "Syntax error:%s", lbuf); goto next_line; } new.matchlen = strlen(new.match); if( new.matchlen ) { if( wflg == 0 ) { vdisp(0, ""); wflg = 1; } vdisp(0, " '"); vdisp(1, new.match); vdisp(0, "'"); } while( *p == ' ' || *p == '\t' ) p++; if( matcount >= MAXMATCH ) fprintf(stderr, "Too many match strings\r\n"); else { matchtab[matcount] = new; matcount++; new.flags |= ALTMATCH; } } while( *p == ','); p = get_item(p, sbuf, sizeof(sbuf), 1); if( p == 0 ) { fprintf(stderr, "Syntax error:%s", lbuf); goto next_line; } if( *sbuf != '"' ) { matchtab[saveptr].sendstr = malloc(strlen(sbuf)+1); strcpy(matchtab[saveptr].sendstr, sbuf); matchtab[saveptr].flags |= SCCOM; while( (p = get_item(p, sbuf, sizeof(sbuf), 1)) != 0 ) { matchtab[saveptr].sendstr = realloc(matchtab[saveptr].sendstr, strlen(matchtab[saveptr].sendstr) + strlen(sbuf)+2); strcat(matchtab[saveptr].sendstr, " "); strcat(matchtab[saveptr].sendstr, sbuf); } } else { matchtab[saveptr].sendstr = malloc(strlen(sbuf)); strcpy(matchtab[saveptr].sendstr, sbuf+1); } matchtab[saveptr].flags |= VALIDREC; next_line: ; } if( wflg ) vdisp(0, "\r\n"); } static char * get_item(p, buf, szbuf, itype) char * p; char * buf; int szbuf; int itype; { char stc; while( *p == ' ' || *p == '\t' ) p++; if( *p == '-' ) { if( itype == 0 ) buf[0] = '\0'; else buf[0] = '"'; buf[1] = '\0'; p++; return p; } else if( ( itype == 0 && *p != '"' ) || *p == '\0' ) { buf[0] = '\0'; return 0; } if( itype == 0 ) stc = *p++; else { *buf++ = stc = *p++; szbuf--; } while( *p ) { if( stc == '"' && *p == '"' ) break; if( stc != '"' && (*p == ' ' || *p == '\t' || *p == '\n') ) break; if( szbuf < 2+(itype&1) ) { p++; continue; ; } szbuf--; if( *p == '\\' && p[1] ) { switch(p[1]) { case 'b': *buf++ = '\b'; break; case 'c': itype &= ~1; break; case 'd': *buf++ = '\377'; break; case 'f': *buf++ = '\f'; break; case 'n': *buf++ = '\n'; break; case 'r': *buf++ = '\r'; break; case 't': *buf++ = '\t'; break; case 'a': { char * q = script_args; while(*q) *buf++ = *q++; } break; default: *buf++ = p[1]; break; } p+=2; } else *buf++ = *p++; } if( itype&1 && stc == '"' ) *buf++ = '\r'; *buf = 0; if( *p ) p++; return p; } static vdisp(flg, str) int flg; char * str; { if( verbose != 1 ) return; fflush(stdout); if( !flg ) fprintf(stderr, "%s", str); else while( *str ) { int ch = *str++; ch &= 0xFF; if( ch >= ' ' && ch != 0x9B && ch != '\\' && ch != 0xFF) fputc(ch, stderr); else if( ch < ' ' ) fprintf(stderr, "^%c", ch+'@'); else if( ch == 0xFF ) fprintf(stderr, "\\d"); else fprintf(stderr, "\\0x%02x", ch); } } #endif