static char *sccsid = "@(#)cu.c 4.7 (Berkeley) 82/10/21"; #include #include #include /* * defs that come from uucp.h */ #define NAMESIZE 15 #define FAIL -1 #define SAME 0 #define SLCKTIME 5400 /* system/device timeout (LCK.. files) in seconds */ #define ASSERT(e, f, v) if (!(e)) {\ fprintf(stderr, "AERROR - (%s) ", "e");\ fprintf(stderr, f, v);\ cleanup(FAIL);\ } /* * cu telno [-t] [-s speed] [-l line] [-a acu] [-p] * * -t is for dial-out to terminal. * speeds are: 110, 134, 150, 300, 1200, 2400. 300 is default. * * -p says strip parity of characters transmitted. (to compensate * for c100's) * * Escape with `~' at beginning of line. * Ordinary diversions are ~<, ~> and ~>>. * Silent output diversions are ~>: and ~>>:. * Terminate output diversion with ~> alone. * Quit is ~. and ~! gives local command or shell. * Also ~$ for canned procedure pumping remote. * ~%put from [to] and ~%take from [to] invoke builtins */ #define CRLF "\r\n" #define wrc(ds) write(ds,&c,1) char *devcul = "/dev/cul0"; char *devcua = "/dev/cua0"; char *lspeed = "300"; int ln; /* fd for comm line */ char tkill, terase; /* current input kill & erase */ int efk; /* process of id of listener */ char c; char oc; char *connmsg[] = { "", "line busy", "call dropped", "no carrier", "can't fork", "acu access", "tty access", "tty hung", "usage: cu telno [-t] [-s speed] [-l line] [-a acu]", "lock failed: line busy" }; rdc(ds) { ds=read(ds,&c,1); oc = c; c &= 0177; return (ds); } int intr; sig2() { signal(SIGINT, SIG_IGN); intr = 1; } int set14; xsleep(n) { xalarm(n); pause(); xalarm(0); } xalarm(n) { set14=n; alarm(n); } sig14() { signal(SIGALRM, sig14); if (set14) alarm(1); } int dout; int nhup; int dbflag; int pflag; /* strip parity on chars sent to remote */ int nullbrk; /* turn breaks (nulls) into dels */ int pipes[2] = { -1, -1 }; /* * main: get connection, set speed for line. * spawn child to invoke rd to read from line, output to fd 1 * main line invokes wr to read tty, write to line */ main(ac,av) char *av[]; { int fk; int speed; char *telno; struct sgttyb stbuf; int cleanup(); signal(SIGALRM, sig14); signal(SIGINT, cleanup); signal(SIGHUP, cleanup); signal(SIGQUIT, cleanup); if (ac < 2) { prf(connmsg[8]); exit(8); } for (; ac > 1; av++,ac--) { if (av[1][0] != '-') telno = av[1]; else switch(av[1][1]) { case 't': dout = 1; --ac; continue; case 'b': nullbrk++; continue; case 'd': dbflag++; continue; case 'p': pflag++; continue; case 's': lspeed = av[2]; ++av; --ac; break; case 'l': devcul = av[2]; ++av; --ac; break; case 'a': devcua = av[2]; ++av; --ac; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': devcua[strlen(devcua)-1] = av[1][1]; devcul[strlen(devcul)-1] = av[1][1]; break; default: prf("Bad flag %s", av[1]); break; } } if (!exists(devcua) || !exists(devcul)) exit(9); ln = conn(devcul, devcua, telno); if (ln < 0) { prf("Connect failed: %s",connmsg[-ln]); cleanup(-ln); } switch(atoi(lspeed)) { case 110: speed = B110;break; case 150: speed = B150;break; default: case 300: speed = B300;break; case 1200: speed = B1200;break; case 2400: speed = B2400;break; } stbuf.sg_ispeed = speed; stbuf.sg_ospeed = speed; stbuf.sg_flags = EVENP|ODDP; if (!dout) { stbuf.sg_flags |= RAW; stbuf.sg_flags &= ~ECHO; } ioctl(ln, TIOCSETP, &stbuf); ioctl(ln, TIOCEXCL, (struct sgttyb *)NULL); ioctl(ln, TIOCHPCL, (struct sgttyb *)NULL); prf("Connected"); pipe(pipes); if (dout) fk = -1; else fk = fork(); nhup = (int)signal(SIGINT, SIG_IGN); if (fk == 0) { chwrsig(); rd(); prf("\007Lost carrier"); cleanup(3); } mode(1); efk = fk; wr(); mode(0); if (fk != -1) kill(fk, SIGKILL); wait((int *)NULL); stbuf.sg_ispeed = 0; stbuf.sg_ospeed = 0; ioctl(ln, TIOCSETP, &stbuf); prf("Disconnected"); cleanup(0); } /* * conn: establish dial-out connection. * Example: fd = conn("/dev/ttyh","/dev/dn1","4500"); * Returns descriptor open to tty for reading and writing. * Negative values (-1...-7) denote errors in connmsg. * Uses alarm and fork/wait; requires sig14 handler. * Be sure to disconnect tty when done, via HUPCL or stty 0. */ conn(dev,acu,telno) char *dev, *acu, *telno; { struct sgttyb stbuf; extern errno; char *p, *q, b[30]; char *ltail, *atail; char *rindex(); int er, fk, dn, dh, t; er=0; fk=(-1); atail = rindex(acu, '/')+1; if (mlock(atail) == FAIL) { er = 9; goto X; } ltail = rindex(dev, '/')+1; if (mlock(ltail) == FAIL) { er = 9; delock(atail); goto X; } if ((dn=open(acu,1))<0) { er=(errno == 6? 1:5); goto X; } if ((fk=fork()) == (-1)) { er=4; goto X; } if (fk == 0) { open(dev,2); for (;;) pause(); } xsleep(2); /* * copy phone #, assure EON */ p=b; q=telno; while (*p++=(*q++)) ; p--; if (*(p-1)!='<') { /*if (*(p-1)!='-') *p++='-';*/ *p++='<'; } t=p-b; xalarm(5*t); t=write(dn,b,t); xalarm(0); if (t<0) { er=2; goto X; } /* close(dn) */ xalarm(40); /* was 5; sometimes missed carrier */ dh = open(dev,2); xalarm(0); if (dh<0) { er=(errno == 4? 3:6); goto X; } ioctl(dh, TIOCGETP, &stbuf); stbuf.sg_flags &= ~ECHO; xalarm(10); ioctl(dh, TIOCSETP, &stbuf); ioctl(dh, TIOCHPCL, (struct sgttyb *)NULL); xalarm(0); X: if (er) close(dn); delock(atail); if (fk!=(-1)) { kill(fk, SIGKILL); xalarm(10); while ((t=wait((int *)NULL))!=(-1) && t!=fk); xalarm(0); } return (er? -er:dh); } /* * wr: write to remote: 0 -> line. * ~. terminate * ~': case ':': { register char *q; if(pipes[1]==-1) { prf("Can't tell other demon to divert"); break; } q = b+1; if(*q=='>') q++; write(pipes[1],q,strlen(q)+1); if(dbflag) prf("msg to be delivered:"),prf(q); if (efk != -1) kill(efk,SIGEMT); } break; #ifdef SIGTSTP #define CTRLZ 26 case CTRLZ: mode(0); kill(getpid(), SIGTSTP); mode(1); break; #endif case '%': dopercen(&b[2]); break; default: prf("Use `~~' to start line with `~'"); } continue; } } dopercen(line) register char *line; { char *args[10]; register narg, f; int rcount; for (narg = 0; narg < 10;) { while(*line == ' ' || *line == '\t') line++; if (*line == '\0') break; args[narg++] = line; while(*line != '\0' && *line != ' ' && *line != '\t') line++; if (*line == '\0') break; *line++ = '\0'; } if (equal(args[0], "take")) { if (narg < 2) { prf("usage: ~%%take from [to]"); return; } if (narg < 3) args[2] = args[1]; write(pipes[1], ">:/dev/null",sizeof(">:/dev/null")); if(dbflag) prf("sending take message"); if (efk != -1) kill(efk,SIGEMT); xsleep(5); wrln("echo '~>"); wrln(args[2]); wrln("'; tee /dev/null <"); wrln(args[1]); wrln(";echo '~>'\n"); return; } else if (equal(args[0], "put")) { if (narg < 2) { prf("usage: ~%%put from [to]"); return; } if (narg < 3) args[2] = args[1]; if ((f = open(args[1], 0)) < 0) { prf("cannot open: %s", args[1]); return; } wrln("stty -echo;cat >"); wrln(args[2]); wrln(";stty echo\n"); xsleep(5); intr = 0; if (!nhup) signal(SIGINT, sig2); mode(2); rcount = 0; while(!intr && rdc(f) == 1) { rcount++; if (c == tkill || c == terase) wrln("\\"); if (wrc(ln) != 1) { xsleep(2); if (wrc(ln) != 1) { prf("character missed"); intr = 1; break; } } } signal(SIGINT, SIG_IGN); close(f); if (intr) { wrln("\n"); prf("stopped after %d bytes", rcount); } wrln("\004"); xsleep(5); mode(1); return; } prf("~%%%s unknown\n", args[0]); } equal(s1, s2) register char *s1, *s2; { while (*s1++ == *s2) if (*s2++ == '\0') return(1); return(0); } wrln(s) register char *s; { while (*s) write(ln, s++, 1); } /* chwrsig: Catch orders from wr process * to instigate diversion */ int whoami; chwrsig(){ int readmsg(); whoami = getpid(); signal(SIGEMT,readmsg); } int ds,slnt,taking; int justrung; readmsg(){ static char dobuff[128], morejunk[256]; int n; justrung = 1; signal(SIGEMT,readmsg); if(dbflag) { prf("About to read from pipe"); } n = read(pipes[0],morejunk,256); if(dbflag) { prf("diversion mesg recieved is"); prf(morejunk); prf(CRLF); } dodiver(morejunk); } dodiver(msg) char *msg; { register char *cp = msg; if (*cp=='>') cp++; if (*cp==':') { cp++; if(*cp==0) { slnt ^= 1; return; } else { slnt = 1; } } if (ds >= 0) close(ds); if (*cp==0) { slnt = 0; ds = -1; return; } if (*msg!='>' || (ds=open(cp,1))<0) ds=creat(cp,0644); lseek(ds, (long)0, 2); if(ds < 0) prf("Creat failed:"), prf(cp); if (ds<0) prf("Can't divert %s",cp+1); } /* * rd: read from remote: line -> 1 * catch: diversion caught by interrupt routine */ #define ORDIN 0 #define SAWCR 1 #define EOL 2 #define SAWTL 3 #define DIVER 4 rd() { extern int ds,slnt; char rb[600], lb[600], *rlim, *llim, c; register char *p,*q; int cnt, state = 0, mustecho, oldslnt; ds=(-1); p = lb; llim = lb+600; agin: while((cnt = read(ln,rb,600)) > 0) { if(!slnt) write(1,rb,cnt); if(ds < 0) continue; oldslnt = slnt; for( q=rb, rlim = rb + cnt - 1; q <= rlim; ) { c = *q++ & 0177; if(p < llim) *p++ = c; switch(state) { case ORDIN: if(c=='\r') state = SAWCR; break; case SAWCR: if(c=='\n') { state = EOL; p--; p[-1] = '\n'; } else state = ORDIN; break; case EOL: state = (c=='~' ? SAWTL : (c=='\r' ? SAWCR : ORDIN)); break; case SAWTL: state = (c=='>' ? DIVER : (c=='\r' ? SAWCR : ORDIN)); break; case DIVER: if(c=='\r') { p--; } else if (c=='\n') { state = ORDIN; p[-1] = 0; dodiver(lb+2); c = 0; p = lb; } } if(slnt==0 && oldslnt) { if(c=='\n') { write(ln,lb,p-lb-1); write(ln,CRLF,sizeof(CRLF)); } else if(q==rlim) { write(ln,lb,p-lb); c = '\n'; /*force flush to file*/ } } if(c=='\n') { if(ds >= 0) write(ds,lb,p-lb); p = lb; } } } if(justrung) { justrung = 0; goto agin; } } struct {char lobyte; char hibyte;}; mode(f) { struct sgttyb stbuf; if (dout) return; ioctl(0, TIOCGETP, &stbuf); tkill = stbuf.sg_kill; terase = stbuf.sg_erase; if (f == 0) { stbuf.sg_flags &= ~RAW; stbuf.sg_flags |= ECHO|CRMOD; } if (f == 1) { stbuf.sg_flags |= RAW; stbuf.sg_flags &= ~(ECHO|CRMOD); } if (f == 2) { stbuf.sg_flags &= ~RAW; stbuf.sg_flags &= ~(ECHO|CRMOD); } ioctl(0, TIOCSETP, &stbuf); } echo(s) char *s; { char *p; for (p=s;*p;p++); if (p>s) write(0,s,p-s); write(0,CRLF, sizeof(CRLF)); } prf(f, s) char *f; char *s; { fprintf(stderr, f, s); fprintf(stderr, CRLF); } exists(devname) char *devname; { if (access(devname, 0)==0) return(1); prf("%s does not exist", devname); return(0); } cleanup(code) { rmlock(NULL); exit(code); } /* * This code is taken directly from uucp and follows the same * conventions. This is important since uucp and cu should * respect each others locks. */ /* ulockf 3.2 10/26/79 11:40:29 */ /* #include "uucp.h" */ #include #include /******* * ulockf(file, atime) * char *file; * time_t atime; * * ulockf - this routine will create a lock file (file). * If one already exists, the create time is checked for * older than the age time (atime). * If it is older, an attempt will be made to unlink it * and create a new one. * * return codes: 0 | FAIL */ ulockf(file, atime) char *file; time_t atime; { struct stat stbuf; time_t ptime; int ret; static int pid = -1; static char tempfile[NAMESIZE]; if (pid < 0) { pid = getpid(); sprintf(tempfile, "/usr/spool/uucp/LTMP.%d", pid); } if (onelock(pid, tempfile, file) == -1) { /* lock file exists */ /* get status to check age of the lock file */ ret = stat(file, &stbuf); if (ret != -1) { time(&ptime); if ((ptime - stbuf.st_ctime) < atime) { /* file not old enough to delete */ return(FAIL); } } ret = unlink(file); ret = onelock(pid, tempfile, file); if (ret != 0) return(FAIL); } stlock(file); return(0); } #define MAXLOCKS 10 /* maximum number of lock files */ char *Lockfile[MAXLOCKS]; int Nlocks = 0; /*** * stlock(name) put name in list of lock files * char *name; * * return codes: none */ stlock(name) char *name; { char *p; extern char *calloc(); int i; for (i = 0; i < Nlocks; i++) { if (Lockfile[i] == NULL) break; } ASSERT(i < MAXLOCKS, "TOO MANY LOCKS %d", i); if (i >= Nlocks) i = Nlocks++; p = calloc(strlen(name) + 1, sizeof (char)); ASSERT(p != NULL, "CAN NOT ALLOCATE FOR %s", name); strcpy(p, name); Lockfile[i] = p; return; } /*** * rmlock(name) remove all lock files in list * char *name; or name * * return codes: none */ rmlock(name) char *name; { int i; for (i = 0; i < Nlocks; i++) { if (Lockfile[i] == NULL) continue; if (name == NULL || strcmp(name, Lockfile[i]) == SAME) { unlink(Lockfile[i]); free(Lockfile[i]); Lockfile[i] = NULL; } } return; } /* this stuff from pjw */ /* /usr/pjw/bin/recover - check pids to remove unnecessary locks */ /* isalock(name) returns 0 if the name is a lock */ /* unlock(name) unlocks name if it is a lock*/ /* onelock(pid,tempfile,name) makes lock a name on behalf of pid. Tempfile must be in the same file system as name. */ /* lock(pid,tempfile,names) either locks all the names or none of them */ isalock(name) char *name; { struct stat xstat; if(stat(name,&xstat)<0) return(0); if(xstat.st_size!=sizeof(int)) return(0); return(1); } unlock(name) char *name; { if(isalock(name)) return(unlink(name)); else return(-1); } onelock(pid,tempfile,name) char *tempfile,*name; { int fd; fd=creat(tempfile,0444); if(fd<0) return(-1); write(fd,(char *) &pid,sizeof(int)); close(fd); if(link(tempfile,name)<0) { unlink(tempfile); return(-1); } unlink(tempfile); return(0); } lock(pid,tempfile,names) char *tempfile,**names; { int i,j; for(i=0;names[i]!=0;i++) { if(onelock(pid,tempfile,names[i])==0) continue; for(j=0;j