Subject: v15i010: UUCP/CU access on one modem Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET Submitted-by: Dave Settle Posting-number: Volume 15, Issue 10 Archive-name: ultrix-modem [ Seems oriented toward System V-oid machines... --r$ ] I'm submitting the new version of 'modem', a program to allow a line connected to a modem to be used for bi-directional uucp/cu accesses. It functionally replaces 'uugetty', but provides a more sophisticated method of talking to intelligent modems, which can screw up uugetty by talking too much. The new version now has support for hardware DCD detection, which allows the shell to be hungup when the line is dropped. Cheers, Dave. #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # modem.1 # Makefile # modem.h # modem.c # uchange.c # sendex.c # io.c # line.c # statelook.c export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'README'" '(2276 characters)' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' Modem: A bi-directional uugetty replacement. "Modem" works just like "uugetty", and allows incoming and outgoing calls through the same port. In addition, it allows provides additional facilities: 1/. Option to insert a uucp-style send-expect conversation to initialise the modem. 2/. Code to spawn getty at the correct speed, if you have a modem that can sense the speed of incoming calls. 3/. Autologout facility, if there is no activity on the modem for a specified period of time. 4/. Log of all connected calls. 5/. Option to run "who" and "ps" on the port, when connected, if you're really security-concious. 6/. Correct handling of DCD (carrier detect) signals from the modem. 7/. Works happily with "uucp", "cu", and "kermit", which are not aware that "modem" is running. [NB Slight change necessary to uucp dialer script required] 8/. State-changing code, which allows you to set the modem in a state related to the time of day. Useful if you want it only to auto-answer when you're not there, and want to use it as a telephone otherwise. [Thanks to jack@swlabs.uucp for this code.] It works fine on my modem, which talks too much for uugetty to be able to handle it! For your local site, you have to do the following: Put this line in /etc/inittab XX:2:respawn:/usr/lib/modem ttyXX where ttyXX is your modem line Make sure that your dialer script is prepared to: a) sleep for > 1 sec BEFORE reading the first reply from the modem b) accept that the first character sent by the modem will NOT get there e.g. My Hayes dialer script is NORMALLY ... AT OK ATDP\T CONNECT but MUST be modified to ... AT\r\D OK ATDP\T CONNECT #( \D is a 2-sec pause ) Modify the included dialer script to talk to YOUR modem, if you have a non- supported modem type. Please let me have your scripts, so that I can include them in future releases. Modify the speed-detection routine, if your modem is not a hayes-compatible Modify "locked()" to read YOUR lock directory - but see the makefile options for various lockfile protocols. If necessary, modify the wiring to the modem so that DCD is asserted when the modem is online, but not asserted at other times. This is necessary for the modem to generate a hangup signal when a remote site disconnects. SHAR_EOF if test 2276 -ne "`wc -c < 'README'`" then echo shar: "error transmitting 'README'" '(should have been 2276 characters)' fi fi echo shar: "extracting 'modem.1'" '(2065 characters)' if test -f 'modem.1' then echo shar: "will not over-write existing file 'modem.1'" else cat << \SHAR_EOF > 'modem.1' .TH modem 1 local .SH NAME modem .SH SYNOPSIS .B "modem [speed] tty" .PP .SH DESCRIPTION .I modem is a functional replacement for \fIuugetty\fR, and allows a line connected to a modem to be used for both incoming and outgoing uucp/cu calls. .PP It has an initial conversation with the modem, so that it can be set in the correct mode, and then waits for the modem to detect an incoming call. It then spawns \fIgetty(8)\fR to login the user, after making an alteration in the file /etc/utmp, so that getty thinks it was spawned by \fIinit(8)\fR. .PP While the user remains logged on, \fImodem\rB will periodically run \fIps\fR on the line, so that it can record what the user is doing. This facility can be turned off (see WATCHIT) option, if not required. .PP If, at any point prior to the incoming call, \fImodem\fR detects an \fBoutgoing\fR call, then it exits. Just before the exit, it attempts to set the line so that it can be used by outgoing callers. .PP If \fImodem\fR detects a LOCK file when it is invoked, then it will wait until this file is removed before proceeding. .PP \fImodem\fR also enforces an idle timeout, so that it the line appears to be idle for too long, the user is logged out. This may cause problems with kermit, which doesn't update the times on the device, but uses \fI/dev/tty\fR instead. .PP The optional \fBspeed\fR argument can be used to force \fImodem\fR to talk to the modem at the specified speed, otherwise it will talk at 1200 baud. .PP The tty name should be of the form \fBtty44\fR. .SH EXAMPLES .PP 44:2:respawn:/usr/lib/modem tty44 .PP 1a:234:respawn:/usr/lib/modem 2400 tty1a .PP .SH FILES /dev/\fI\fB .PP /usr/spool/uucp/modem.log .SH SEE ALSO getty(8), uugetty(8), sh(1), ps(1), who(1), utmp(4) .SH BUGS The code for hardware DCD detection support is new. .PP There are still some unexplained diagnostics in my log file, mainly about write(1) calls failing to write characters. .PP You will have to modify some source code if your modem and/or system is different from mine (speed configuration and lock files). SHAR_EOF if test 2065 -ne "`wc -c < 'modem.1'`" then echo shar: "error transmitting 'modem.1'" '(should have been 2065 characters)' fi fi echo shar: "extracting 'Makefile'" '(1328 characters)' if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' # # new makefile for modem. # # if your lockfile is: # /usr/spool/uucp/LCK.. No defines needed. # /usr/spool/locks/LCK.. -DOL3B2 # /usr/spool/locks/LCK.L. -DHDB # # New definitions: # HDB: If you have Honey-Danber UUCP (Thanks Rick) # OL3B2: If you have an Olivetti 3B2 # SPEEDCONFIG: If you have a modem that tells you the speed of incoming # calls (In ASCII - 300(or \r), 1200, 2400, 1275, 7512) # If your modem says different things, hack the code # WATCHIT: If you want "who" and "ps" run periodically to check # what's going on. # HAYES If you have a Hayes-type modem. # JSET: If you have an AT&T Aztec modem (Thanks Rick). # DEBUG: See what's going on. # SLOW: Wait 1 sec between each character written to the modem. # STATES If you want to use Jack's state-changing stuff. # # check "modem.h" for additional options on default modem speed and timezone. # CFLAGS = -DOL3B2 -DSPEEDCONFIG -DWATCHIT -DHAYES PARTS = modem.o uchange.o sendex.o io.o line.o statelook.o # LIBS = -lg modem: ${PARTS} rm -f modem cc ${PARTS} -o modem ${LIBS} install: modem mv /etc/modem /etc/modem.old cp modem /etc lint: lint ${CFLAGS} ${PARTS:.o=.c} ${PARTS}: modem.h Makefile shar: shar -cv README modem.1 Makefile modem.h ${PARTS:.o=.c} > modem.shar man: nroff -man modem.1 > modem.man SHAR_EOF if test 1328 -ne "`wc -c < 'Makefile'`" then echo shar: "error transmitting 'Makefile'" '(should have been 1328 characters)' fi fi echo shar: "extracting 'modem.h'" '(2898 characters)' if test -f 'modem.h' then echo shar: "will not over-write existing file 'modem.h'" else cat << \SHAR_EOF > 'modem.h' /* * some constants for your modem */ #define TIMEZONE "TZ=GMT0BST" /* time zone you operate in */ #define DEFSPEED "1200" /* speed your modem likes most */ #define WTIME 5 /* default wait time */ #define LOGFILE "/usr/spool/uucp/modem.log" /* log file */ #define max(a,b) ((a) > (b) ? (a) : (b)) #define min(a,b) ((a) < (b) ? (a) : (b)) #define MINUTES 60 #define IDLETIME 5*MINUTES /* autologout time */ #ifdef WATCHIT #define SPYTIME 1*MINUTES #else #define SPYTIME IDLETIME #endif #define GRACETIME 5 /* time to wait for signal to take effect */ extern int errno; #ifdef MAINDEF #define EXTERN #else #define EXTERN extern #endif long time(); int dread(), dwrite(), myread(); void mywrite(); EXTERN char lockf[50]; /* argument lock file */ EXTERN char dname[30]; /* device name in full - /dev/.... */ EXTERN int shell, status; /* pid of shell, and exit status */ EXTERN int dev; /* device used (open fildes) */ /* * The 'reset' sequence, if the modem doesn't behave as expected. * I still get problems with the modem not responding to 'AT'. */ #if defined(HAYES) #define RESET "\n\rAT\r\rAT\r\rAT\r\r" #else #define RESET "" #endif /* * The 'conversation' necessary to get your modem into a 'listening' state. * The converasation should complete iff an incoming call is connected. * If using Jack's states, set up your states below. Each modem line * must have flags set to indicate in which state(s) it can be executed. * If you're not using the STATES stuff, leave them at zero. */ #define SPK 0x0001 /* Speaker control */ #define AA 0x0002 /* Auto Answer Control */ struct conv { char *c_send; char *c_expect; int c_wait; /* time to wait for c_expect */ int c_flags; /* flags for matching with the state. */ }; #ifdef MAINDEF struct conv ring[] = { #if defined(HAYES) {"AT\r", "OK", WTIME, AA | SPK }, /* anyone there? */ {"ATM0\r", "OK", WTIME, AA | SPK }, /* turn OFF speaker */ {"ATS0=0\r", "OK", WTIME, AA | SPK}, /* DISABLE auto-answer */ {"ATS0=1\r", "OK", WTIME, AA}, /* maybe enable auto answer */ {"ATM1\r", "OK", WTIME, SPK}, /* maybe turn speaker on */ {"ATX1V1\r", "OK", WTIME, AA | SPK }, /* turn on responses */ {"ATS24=3\r", "OK", WTIME, AA | SPK}, /* enable speed-seeking */ {"", "RING", 0, AA | SPK}, /* wait (indefinately) for RING in */ {"", "CONNECT", 20, AA | SPK}, /* wait for call to be connected */ {NULL, NULL, 0, 0} /* finished */ #else /* defined (HAYES) */ #if defined(JSET) /* AT&T Aztec Protocol */ {"\\d\r\\d", "MODEM: ", WTIME, 0 }, /* anyone there? */ {"", "DATA", 3600, 0}, /* wait (1 hour) for RING in */ {NULL, NULL, 0, 0} /* Should be indefinite, but prevent problems if modem gets wedged */ #else /* defined (JSET) */ {NULL, NULL, 0, 0} /* no init conversation */ #endif /* defined (JSET) */ #endif /* defined (HAYES) */ }; #endif /* defined (MAINDEF) */ SHAR_EOF if test 2898 -ne "`wc -c < 'modem.h'`" then echo shar: "error transmitting 'modem.h'" '(should have been 2898 characters)' fi fi echo shar: "extracting 'modem.c'" '(10194 characters)' if test -f 'modem.c' then echo shar: "will not over-write existing file 'modem.c'" else cat << \SHAR_EOF > 'modem.c' /* * Copyright (c) Dave Settle 1987 * All rights reserved. * Permission is granted to do anything with this program, except remove * this copyright notice, or sell it for profit. * * Problems, suggestions, bug fixes etc, to: * * Dave Settle, SMB Business Software, Thorn EMI Datasolve * * UUCP: * dave@smb.co.uk * ...!mcvax!ukc!nott-cs!smb!dave * * SMAIL: Voice: * SMB Business Software +44 623 651651 * High Street * Mansfield Telex: * Nottingham NG18 1ES 37392 TECSMG * England * Fax: * +44 623 659947 * * modem.c: a bi-directional "getty" to allow incoming calls AND outgoing * uucico's * * Modified by Rick Richardson for HDB uucp and AT&T Aztec protocol * * Modified by Jack Bonn for state logic (allows autoanswer on/off at * a given time for each day of the week) * * Modified by Dave Settle: * * Code fixes: * hangup routine added, to make sure phone is down before script, and * after user disconnects. * Parameterised send-expect routines. * Minor bug fix to dread() * Re-enable SIGALRM in wakeup() * Catch all signals, to enable crashes to be detected. * Chmod device back to 666, so that we can use it afterwards. * Oct 87: fixup modem control with CLOCAL, so that remote disconnect * can be detected and dealt with. * Oct 87: fix autologout to send series of signals, not just SIGHUP * Oct 87: Clean up code, and distribute to other source files. * Nov 87: Fix lurking bug in 'expect'. Clean code for 'lint'. * Nov 87: Ignore SIHGUP's generated by 'hangup'. * Dec 87: Force getty NOT to hangup the line before login. * Jan 87: Open stdio file descriptors. * * */ #include #include #include #include #include #include #include #include #ifdef WATCHIT #include struct passwd *getpwuid(); #endif #define MAINDEF #include "modem.h" /* variable definitions */ /* * wakeup gets called when the alarm goes off */ wakeup(){ signal(SIGALRM, wakeup); } fault(sig){ printf("Crashed with signal %d\n", sig); closedown(sig); } closedown(sig){ time_t now; now = time((long *) 0); /* * [Nov 87]: Since we're about to hang up the phone, * ignore the signal that this is going to generate. */ signal(SIGHUP, SIG_IGN); if(sig) printf("Caught signal %d\n", sig); printf("Closedown at %s", asctime(localtime(&now))); /* * Oct 87: new addition for SIGHUP. We receive this signal when the line * is disconnected by a remote caller. Since the line is dead, so should * the shell be (it got SIGHUP same as us); log it out anyway, even if it's * trying to ignore it. */ if((sig == SIGHUP) && shell) autologout(shell); /* * change the value in utmp back to our pid. */ if(shell) uchange(shell, getpid()); /* back to normal */ /* * clear modem line, which should also hangup the phone (if it wasn't already). */ hangup(dev); close(dev); /* * chmod the device back to 666, so that uucico and cu can access it. * NOTE: I prefer this to chown(uucp), which does not allow "cu" access. */ chmod(dname,0666); /* uucp access */ unlink(lockf); if(status) printf("Logout status 0x%x\n", status); fclose(stdout); exit(0); } main(argc, argv, envp) char **argv, **envp; { long t; char *arg[5], *speed; int baud, i; struct conv *p; struct stat sbuf; time_t now, mtime; #ifdef WATCHIT FILE *procs; struct passwd *pw; char psbuff[128]; #endif #ifdef SPEEDCONFIG char c, buff[8]; #endif #ifdef STATES time_t delta, suicide; int state; #endif FILE *lock; /* * catch and report all signals, but apply special treatment to legal signals * Any program bugs get reported this way. You can get a core dump by sending * it SIGFPE. */ for(i=1;i []\n", argv[0]); sleep(5); exit(5); } sprintf(dname, "/dev/%s", argv[1]); #if defined(OL3B2) sprintf(lockf, "/usr/spool/locks/LCK..%s", argv[1]); #else #if defined(HDB) sprintf(lockf, "/usr/spool/locks/LCK.L.%s", argv[1]); #else sprintf(lockf, "/usr/spool/uucp/LCK..%s", argv[1]); #endif #endif /* * do not start while device is locked */ while(locked()) sleep(30); if((dev = open(dname, O_RDWR | O_NDELAY)) == -1) { perror(dname); sleep(5); /* don't go crazy with respawns */ exit(4); } /* * setup correct time zone */ putenv(TIMEZONE); /* * set terminal parameters * Additional argument (if present) can be used to force an initial speed * (Thanks rick) */ if(argc > 2) speed = argv[2]; else speed = DEFSPEED; baud = findspeed(speed); init_term(dev, baud); /* * send-expect strings - at the end, someone has connected to the modem */ #ifdef STATES /* * find out when the next state change is due to occur. At this point, we exit. * The next initiation will set the modem up differently. */ delta = duration(&state); suicide = time((long *) 0) + delta; #endif for(p = ring; p->c_send; p++) { #ifdef STATES if(!applicable(p, state)) continue; #endif send(p->c_send, mywrite); #ifdef STATES if(expect(p->c_expect, (int)(p->c_wait ? min(p->c_wait, delta) : delta), myread)) { #else if(expect(p->c_expect, p->c_wait, myread)) { #endif t = time((long *) 0); #ifdef STATES if(t >= suicide) { printf("Exit due to state change\n"); exit(0); } #endif printf("Incoming call failed to connect on %s", asctime(localtime(&t))); /* * we don't have to do anything special here, since no locks have been setup. * However, since the main problems appear to be non-responding modems, send * it some sort of 'un-wedging' sequence */ send(RESET, mywrite); sleep(3); hangup(dev); sleep(10); ioctl(dev, TCFLSH, 2); /* Flush both queues */ exit(3); /*NOTREACHED*/ } } signal(SIGALRM, wakeup); /* * OK - incoming call connected. Find speed (if possible), create lock file, * then spawn getty. * At this point, we also trap hangups, so that we can clean up after * any incoming calls. (Hangups are now detected by the hardware). */ signal(SIGHUP, closedown); #ifdef SPEEDCONFIG dread(dev, &c, 1); if(c == '\r') speed = "300"; else { dread(dev, buff, 4); if(!strcmp(buff, "1200")) speed = "1200"; if(!strcmp(buff, "2400")) speed = "2400"; if(!strcmp(buff, "1275")) speed = "1200"; if(!strcmp(buff, "7512")) speed = "1200"; } baud = findspeed(speed); #endif t = time((long *)0); printf("Call connected at %s on %s", speed, asctime(localtime(&t))); /* * Someone has rung in - lock the device. */ lock = fopen(lockf, "w"); /* * hand over to "getty" */ arg[0] = "getty"; arg[1] = "-h"; arg[2] = argv[1]; arg[3] = speed; arg[4] = NULL; if(shell = fork()) { /* * change the utmp pid to the "getty" process, so that it can login */ uchange(getpid(), shell); /* change utmp entry */ #if defined(OL3B2) fprintf(lock," %d\n", shell); #else #if defined(HDB) fprintf(lock,"%d\n", shell); #else fwrite((char *) &shell, sizeof shell, 1, lock); #endif #endif fclose(lock); /* * wait for logout from shell. * if terminal is idle for IDLETIME, force a logout anyway */ alarm(SPYTIME); while((wait(&status) == -1) && (errno == EINTR)) { if(stat(dname, &sbuf) == -1) { perror(dname); continue; } now = time((long *)0); mtime = max(sbuf.st_atime, sbuf.st_mtime); if((now - mtime) >= IDLETIME) { write(dev, "autologout\r\n", 12); printf("Device idle - autologout at %s\n", asctime(localtime(&now))); uchange(shell, getpid()); autologout(shell); break; } #ifdef WATCHIT t = time((long *) 0); pw = getpwuid((int) sbuf.st_uid); printf("Logon user is %s on %s", pw->pw_name, asctime(localtime(&t))); procs = popen("ps -ef", "r"); while(fgets(psbuff, sizeof psbuff, procs)) if(partof(psbuff, argv[1]) && partof(psbuff, pw->pw_name)) printf("%s", psbuff); pclose(procs); #endif alarm(SPYTIME); } t = time((long *) 0); printf("Call disconnected on %s", asctime(localtime(&t))); closedown(0); /* remove locks etc */ /*NOTREACHED*/ } else { sleep(1); /* allow changes to utmp */ /* * Re-do the termio settings, so that we can login. * CLOCAL is removed at this point, so that a hangup will generate SIGHUP. * Close all the files which we have open - getty expects none. */ login_term(dev, baud); for(i=0;i<20;i++) close(i); #ifdef DEBUG printf("%d: /etc/getty %s %s\n", getpid(), arg[1], arg[2]); #endif execve("/etc/getty", arg, envp); printf("can't exec getty\n"); exit(1); /* NOTREACHED */ } } /* * check for presence of lock file * return 1 if locked. */ locked(){ struct stat sb; if(stat(lockf, &sb) == -1) return(0); #ifdef DEBUG printf("%s locked\n", lockf); #endif return(1); } #ifdef WATCHIT /* * partof: look for str in text. Has a bug if you look for ...xxx... */ partof(text, str) char *text, *str; { char *needle, *p; for(p = text, needle = str; *p ; p++) { if(*p != *needle) needle = str; if(*p == *needle) needle++; if(*needle == '\0') return(1); } return(0); } #endif /* * autologout: log out the child shell. Sends SIGHUP, SIGTERM, SIGKILL * until the child exits. */ autologout(child) { kill(child, SIGHUP); alarm(GRACETIME); if(wait(&status) == -1) { kill(child, SIGTERM); alarm(GRACETIME); if(wait(&status) == -1) { kill(child, SIGKILL); alarm(GRACETIME); if(wait(&status) == -1) { printf("Cannot kill child pid %d\n", child); status = 0; } } } alarm(0); } SHAR_EOF if test 10194 -ne "`wc -c < 'modem.c'`" then echo shar: "error transmitting 'modem.c'" '(should have been 10194 characters)' fi fi echo shar: "extracting 'uchange.c'" '(711 characters)' if test -f 'uchange.c' then echo shar: "will not over-write existing file 'uchange.c'" else cat << \SHAR_EOF > 'uchange.c' /* * Copyright (c) Dave Settle, Mar 1987 * Permission is granted to do anything with this program, except remove * this copyright notice, or sell it for profit. * * * change the ut_pid value from "old" to "new". * Attempts to do all the things that "getty" appears to do. */ #include #include struct utmp *utmp, *getutent(); uchange(old, new) { setutent(); while(utmp = getutent()) { if(utmp->ut_pid == old) { utmp->ut_pid = new; if(strcmp(utmp->ut_user, "getty")) strcpy(utmp->ut_user, "modem"); else strcpy(utmp->ut_user, "getty"); pututline(utmp); endutent(); return(1); } } printf("Can't find utmp entry\n"); endutent(); return(1); } SHAR_EOF if test 711 -ne "`wc -c < 'uchange.c'`" then echo shar: "error transmitting 'uchange.c'" '(should have been 711 characters)' fi fi echo shar: "extracting 'sendex.c'" '(1797 characters)' if test -f 'sendex.c' then echo shar: "will not over-write existing file 'sendex.c'" else cat << \SHAR_EOF > 'sendex.c' /* * Copyright (c) Dave Settle, Mar 1987 * Permission is granted to do anything with this program, except remove * this copyright notice, or sell it for profit. * * * sendex.c: contains the send-expect routines, beefed-up a little to * make them more useful. * Both "send" and "expect" now have a function, rather than * a file descriptor. This function gets called as follows: * * (int) (*rdfunc)(); [send] * (*wtfunc)((char *)s); [expect] * */ #include #include #include /* * send: write the string out. Show the chars written if necessary. */ send(s, func) char *s; void (*func)(); { #ifdef DEBUG printf("send("); sshow(s); printf(")\n"); #endif (*func)(s); sleep(1); } /* * expect: expect a string. Return 0 on success, 1 on timeout. * chars are threaded on the needle as they match the expect string * if one fails to match, all chars are unthreaded. * expect succeeds if all chars on expect string are threaded. */ jmp_buf env; timeout(){ longjmp(env, 1); } expect(s, t, func) char *s; int (*func)(); { char *needle = s, c; int (*handler)(); alarm(0); handler = signal(SIGALRM, timeout); if(setjmp(env)) { printf("Timeout expecting %s\n", s); return(1); /* fail - timeout */ } alarm(t); /* if zero, no timeout */ #ifdef DEBUG printf("\nexpect(%s)\n", s); #endif while(*needle) { c = (*func)(); #ifdef DEBUG show(c); #endif if(*needle != c) needle = s; if(*needle == c) needle++; } #ifdef DEBUG printf("got it\n"); #endif alarm(0); signal(SIGALRM, handler); return(0); } #ifdef DEBUG /* * sshow: show string */ sshow(s) char *s; { while(*s) show(*s++); } show(c) char c; { if((c < 31)) { printf("^%c", c + '@'); if(c == '\n') putchar('\n'); } else putchar(c); fflush(stdout); } #endif SHAR_EOF if test 1797 -ne "`wc -c < 'sendex.c'`" then echo shar: "error transmitting 'sendex.c'" '(should have been 1797 characters)' fi fi echo shar: "extracting 'io.c'" '(4690 characters)' if test -f 'io.c' then echo shar: "will not over-write existing file 'io.c'" else cat << \SHAR_EOF > 'io.c' /* * Copyright (c) Dave Settle, March 1987 * Permission is granted to do anything with this program, except remove * this copyright notice, or sell it for profit. * * * io.c: routines to talk to the device directly. * * dread and dwrite act like read(2) and write(2), execpt that they always * either succeed or exit, so the caller doesn't have to check. * * hangup drops the DTR line to the modem, so that it will (hopefully) hang up * the phone line. It keeps it this way for 5 seconds. * * findspeed takes an ascii speed, and returns the corresponing baud rate. * * uuexit is a dodgy routine to make UUCP happy. */ #include #include #include #include "modem.h" char *sys_errlist[]; /* error list */ #define RETRY 5 /* retry failed writes */ /* * dwrite: write some characters to the modem. * If your modem likes characters to be written s-l-o-w-l-y, define 'SLOW' */ dwrite(f, s, n) int f; unsigned n; char *s; { struct termio termio; int r, retry = 0, i = 0; static int error = 0; /* error occurred on last call */ while((i < n) && (retry < RETRY)) { if(locked()) uuexit(0); errno = 0; if(r = write(f, s + i, 1) == 1) i++; /* Success! */ else { retry++; fixline(); } #ifdef SLOW sleep(1); /* Let the modem deal with it */ #endif } if(i != n) { ioctl(dev, TCGETA, &termio); printf("Tried %d times, still got %d/%d written. [error = %s]\n", retry, i, n, sys_errlist[errno]); printf("FAIL: dev %d, iflag %x, oflag %x, cflag %x, lflag %x, line %d\n", dev, termio.c_iflag,termio.c_oflag,termio.c_cflag, termio.c_lflag, termio.c_line); error = 1; } else if(retry > 1) printf("Write problem: fixed on retry %d\n", retry - 1); if(error && i == n) { ioctl(dev, TCGETA, &termio); printf("OK: iflag %x, oflag %x, cflag %x, lflag %x, line %d\n", termio.c_iflag,termio.c_oflag,termio.c_cflag, termio.c_lflag, termio.c_line); } if(i == n) error = 0; return(r); } /* * read one character at a time, checking before (and after) each read for * a lock file. If one exists, then exit. */ dread(f, s, n) int f, n; char *s; { int i, w; for(i=0;i 'line.c' /* * line.c: set up the various termio params for the line at various stages. * [What I would like for Christmas: System V tty driver code, so I could * find out why it behaves so strangely ... ] */ #include "modem.h" #include #include static struct termio term; static int speed; init_term(device, baud) int device, baud; { speed = baud; if(ioctl(device, TCGETA, &term) == -1) perror("TCGETA"); term.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK); term.c_iflag &= ~(ISTRIP | INPCK); term.c_cflag &= ~(CBAUD | CSIZE); term.c_cflag |= baud | CS8 | CLOCAL; term.c_cc[VMIN] = 1; term.c_cc[VTIME] = 0; if(ioctl(device, TCSETA, &term) == -1) perror("TCSETA"); /* * Since we opened the line with O_NDELAY, reads will return immediately if * no chars are ready. We don't really want this, 'cos "dread" will then * eat CPU time, so we turn it off. * It only seems to be really effective when the device is opened again - * otherwise we get strange diagnostics about "wrote 0 of 4". Thanks to * the C-KERMIT crew for this tip. * * The open() here seems to hang sometimes, so a timeout is implemented. * [since CLOCAL has been set above, this should never happen. We can't * just open with O_NDELAY, because uucp won't work anyway.] */ fcntl(device, F_SETFL, 0); alarm(5); if(close(open(dname, O_RDONLY)) == -1 ) { printf("** Error: Couldn't reopen device after TCSETA\n"); exit(1); } alarm(0); } /* * Strange things have been known to happen during "login" with incorrect * settings - if it forgets to ask you for a password, check them carefully. * Oct 87: Disable CLOCAL, since DCD is now asserted, and we want to be told * about hangups. */ login_term(device, baud) int device, baud; { ioctl(device, TCGETA, &term); term.c_cc[VEOF] = 04; /* cntrl-D */ term.c_iflag = BRKINT | IGNPAR | ISTRIP | ICRNL; term.c_oflag = OPOST | ONLCR | TAB3; term.c_cflag &= ~(CSIZE | CLOCAL | CBAUD); term.c_cflag |= CS7 | HUPCL | CREAD | baud; term.c_lflag = ISIG | ICANON; ioctl(device, TCSETAW, &term); } /* * fixline: write failed - redo settings on line. */ fixline(){ ioctl(dev, TCFLSH, 2); /* flush queues */ ioctl(dev, TCXONC, 1); /* restart XON/XOFF */ fcntl(dev, F_SETFL, 0); /* poke the flag */ close(open(dname, O_RDONLY | O_NDELAY)); init_term(dev, speed); } SHAR_EOF if test 2338 -ne "`wc -c < 'line.c'`" then echo shar: "error transmitting 'line.c'" '(should have been 2338 characters)' fi fi echo shar: "extracting 'statelook.c'" '(3302 characters)' if test -f 'statelook.c' then echo shar: "will not over-write existing file 'statelook.c'" else cat << \SHAR_EOF > 'statelook.c' /* * Stuff to work out when to next change state (causes exit) * and whether to apply a particular bit of the modem conversation. */ #include #include "modem.h" #ifdef STATES #define SECS_IN_WEEK (60L * 60L * 24L * 7L) #define WEEK_TIME(d,h,m) ((((long)d*24L+(long)h)*60L+(long)m)*60L) #define EVENT_LEN (sizeof(week_event)/sizeof(struct event)) #define IND_LEN (sizeof(ind_tab)/sizeof(struct ind)) /* This is a table of when to turn on and off the autoanswer mode of the modem. */ struct event { long s_time; int s_state; } week_event[] = { { WEEK_TIME (0, 7, 35), AA | SPK }, /* AA on, speaker on 7:35 Sun */ { WEEK_TIME (0, 22, 00), AA }, /* AA on, speaker off 22:00 Sun */ { WEEK_TIME (1, 7, 35), SPK }, /* AA off, speaker on 7:35 Mon */ { WEEK_TIME (1, 17, 25), AA | SPK }, /* AA on, speaker on 17:25 Mon */ { WEEK_TIME (1, 22, 00), AA }, /* AA on, speaker off 22:00 Mon */ { WEEK_TIME (2, 7, 35), SPK }, /* AA off, speaker on 7:35 Tue */ { WEEK_TIME (2, 17, 25), AA | SPK }, /* AA on, speaker on 17:25 Tue */ { WEEK_TIME (2, 22, 00), AA }, /* AA on, speaker off 22:00 Tue */ { WEEK_TIME (3, 7, 35), SPK }, /* AA off, speaker on 7:35 Wed */ { WEEK_TIME (3, 17, 25), AA | SPK }, /* AA on, speaker on 17:25 Wed */ { WEEK_TIME (3, 22, 00), AA }, /* AA on, speaker off 22:00 Wed */ { WEEK_TIME (4, 7, 35), SPK }, /* AA off, speaker on 7:35 Thu */ { WEEK_TIME (4, 17, 25), AA | SPK }, /* AA on, speaker on 17:25 Thu */ { WEEK_TIME (4, 22, 00), AA }, /* AA on, speaker off 22:00 Thu */ { WEEK_TIME (5, 7, 35), SPK }, /* AA off, speaker on 7:35 Fri */ { WEEK_TIME (5, 17, 25), AA | SPK }, /* AA on, speaker on 17:25 Fri */ { WEEK_TIME (5, 22, 00), AA }, /* AA on, speaker off 22:00 Fri */ { WEEK_TIME (6, 7, 35), AA | SPK }, /* AA on, speaker on 7:35 Sat */ { WEEK_TIME (6, 22, 00), AA } /* AA on, speaker off 22:00 Sat */ }; long duration(state) int *state; /* 0 if new Auto Answer state is OFF, 1 if ON */ { long secs_into_week; /* Seconds since midnight Sunday morning */ long t; struct tm *cur_time; int i, index; long delta, tst_delta; t = time((long *) 0); cur_time = localtime (&t); secs_into_week = ((((long)(cur_time->tm_wday)) * 24L + (long)(cur_time->tm_hour)) * 60L + (long)(cur_time->tm_min)) * 60L + (long)(cur_time->tm_sec); delta = SECS_IN_WEEK + 1; /* SOMETHING has to be closer than this! */ /* Loop looking for an entry with a better delta (closer to now) */ for (i=0; i < EVENT_LEN; i++) { tst_delta = week_event[i].s_time - secs_into_week; if (tst_delta < 0L) tst_delta += SECS_IN_WEEK; /* Adjust for wrap around */ if (tst_delta < delta) { /* We found a closer event to now */ delta = tst_delta; index = i; } } /* * Decrement index by one circularly (note table MUST be in order) -- */ if (index-- == 0) index = EVENT_LEN-1; *state = week_event[index].s_state; #ifdef DEBUG printf("Currently in state %d. Will change in %ld seconds\n", *state, delta); #endif return(delta); } /* This routine decides whether a particular init line should be executed. */ applicable(p, state) struct conv *p; int state; { return(p->c_flags & state); } #endif SHAR_EOF if test 3302 -ne "`wc -c < 'statelook.c'`" then echo shar: "error transmitting 'statelook.c'" '(should have been 3302 characters)' fi fi exit 0 # End of shell archive -- Dave Settle, SMB Business Software, Thorn EMI Datasolve, High St, Mansfield, UK UUCP: dave@smb.co.uk ...!mcvax!ukc!nott-cs!smb!dave <--- This way to point of view --->