Newsgroups: comp.sources.unix From: Tom_Limoncelli@Warren.MENTORG.COM Subject: v26i059: tpage2 - tom's utilities for alphanumeric pagers (V2.0) Sender: unix-sources-moderator@pa.dec.com Approved: vixie@pa.dec.com Submitted-By: Tom_Limoncelli@Warren.MENTORG.COM Posting-Number: Volume 26, Issue 59 Archive-Name: tpage2 [ I don't have an alphanumeric pager so I couldn't test this. --vix ] Own an alpha-numeric pager? This will let your computer speak the protocol so you can send messages to it. With the help of a mail filter like "procmail" (available via ftp from your local comp.sources.unix archive or from berg@messua.informatik.rwth-aachen.de) you can have messages containing certain keywords automatically sent to your pager. Tom_Limoncelli@Warren.MENTORG.COM #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'README' <<'END_OF_FILE' READDME -- General information about "tpage" X by Tom Limoncelli, tal@warren.mentorg.com X Copyright (c) 1992, Tom Limoncelli X The sources can be freely copied for non-commercial use only X and only if the source is unmodified. X X"tpage" or "Tom's Pager System" is a set of programs that let you send messages to alpha-numeric pagers using the "IXO" protocol. It supports a dialing directory, a "who's on duty now" schedule, and can do special tricks with RFC822-format email. X The system has the following features: X X...sends pages to any pager system that supports the IXO protocol. X X...additional protocols can be added. (I'll write the touch-tone X protocol soon). X X...can parse email messages and extract the interesting info from X them resulting in shorter messages. X X...can copy it's input to stdout and therefore can be used as a "tee". X X...maintains a directory of people's phone numbers/PINs. X X...can page "the person on duty" (searches a schedule). X X...schedule can have slots that are empty, but find someone anyway if X the message is marked "urgent". X X...with programs like procmail, permits you to send certain email X messages to your pager. X X...a list of modems can be given to the daemon. X How it works (and how all the programs fit together): X o beep2.pl takes command-line and stdin, reads the schedule, looks X up people's paging info in the directory, and queues the message. o tpaged.pl should always be running. Every 30 seconds it wakes up X to check the queue for messages. o tpaged.pl then sorts and batches the messages. o tpaged.pl then calls the appropriate program to do the protocol X (currently that's ixocico but others can be written) and watches X the output for messages: X "#MESOK x" which means that message "x" was successful and can be X deleted from the queue. X "#MESREJECT x" which means that message "x" was rejected and should X be deleted from the queue. Email will be sent to the person X that sent the page. X XFor installation instructions, read INSTALL. XFor program history, read HISTORY. X If you aren't using "procmail", you're working too hard. Check X"archie" for a location near you! END_OF_FILE if test 2173 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'HISTORY' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'HISTORY'\" else echo shar: Extracting \"'HISTORY'\" \(994 characters\) sed "s/^X//" >'HISTORY' <<'END_OF_FILE' HISTORY X by Tom Limoncelli, tal@warren.mentorg.com X Copyright (c) 1992, Tom Limoncelli X The sources can be freely copied for non-commercial use only X and only if the source is unmodified. X XFor installation instructions, read INSTALL. XFor program history, read HISTORY. XFor general program information, read README. X X X(( This program is the successor to "fbeep". X(( X(( fbeep history: X(( X(( 1.0 Oct 29, 1991 -- First working version. X(( X(( 1.1 Oct 30, 1991 -- Debugging levels implemented (-s -v -vv). X(( X(( 1.2 Oct 30, 1991 -- Cleaned the output, error message on failure. X(( X(( 1.3 Nov 20, 1991 -- Added -m, -M and "null person" options. X(( X(( Made -a (abort) replace -w (wait). X(( X(( 1.4 Jan 2, 1992 -- Destination can now be a list of people. X(( X(( (project ended, replaced by tpage, tpaged, and ixocico) X X beep2.pl: X 1.0 Jan 31, 1992 -- First release. X tpaged.pl history: X 1.0 Jan 31, 1992 -- First release. X ixocico.c X 1.0 Jan 31, 1992 -- First release. X END_OF_FILE if test 994 -ne `wc -c <'HISTORY'`; then echo shar: \"'HISTORY'\" unpacked with wrong size! fi # end of 'HISTORY' fi if test -f 'INSTALL' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'INSTALL'\" else echo shar: Extracting \"'INSTALL'\" \(4323 characters\) sed "s/^X//" >'INSTALL' <<'END_OF_FILE' INSTALL -- How to install the Tom Page set of programs. X by Tom Limoncelli, tal@warren.mentorg.com X Copyright (c) 1992, Tom Limoncelli X The sources can be freely copied for non-commercial use only X and only if the source is unmodified. X XFor program history, read HISTORY. XFor general program information, read README. X X THEORY OF OPERATION: (this assumes you've read README already) X-------------------- X tpage.pl (which is often renamed to "beep" or "page") takes a number of options, figures out who and how to send to a person, figures out the message, and stuffs the info into a file for the daemon to read. X tpage.pl refers to /home/adm/lib/tpage/table to find out the phone number and other information that must be given to the daemon. You can also specify a different file name with the "-T" option. X tpaged.pl wakes up every couple minutes to see if there is anything new in the queue. If there is, it takes the information, dials out to your paging company, and transmits the message. X Rather than page a particular person, tpage.pl can be given a schedule and be told, "page whoever is on duty right now". This is done by giving it "-" as the "who" parameter. The schedule is stored in /home/adm/lib/tpage/schedule, or by specifying a file name with the "-S" option. X tpage can take the message from the command line, or from stdin. If it is coming from stdin, it can parse it as email and do various things with it (see the man page). X X INSTALLATION: X------------- X X1. Install the Perl programming language. The front-end to tpaged is written in Perl. Any version should do, but it was developed with version 4.019. Hopefully, this program already exists on your system. If not, I highly recommend that you get it from ftp.uu.net:/pub/gnu and install it. X XFor the rest of this document, $TPAGE will refer to the directory that you wish to keep the files associated with the tpage system. X ALL FILES SHOULD BE COPIED to $TPAGE except tpage (belongs in X/usr/local/bin/tpage). The queue directory should be $TPAGE/pqueue but nothing will break if you put it someplace else. X X2. Edit tpage.pl and copy it to "/usr/local/bin/tpage". The first section explains what variables need to be edited and why. You might want to call it "page" or "beep" instead. X XEdit tpaged.pl and copy it to "$TPAGE/tpage". The first section of each file explains what variables need to be edited and why. X XEdit tpage.l and copy it to "/usr/local/man/manl" or wherever local manpages are kept. The first section of each file explains what variables need to be edited and why. X If you run SunOS you won't have to edit ixocico.c. If you don't run SunOS you're going to have the fun of porting this program. I put in a couple of #defines to get you started. Anyway, compile it with a simple X"make ixocico" and you're done with it. ixocico doesn't require many edits because tpaged gives it everything it needs to know. ixocico also belongs in $TPAGE/ixocico X Copy the sample "table" file and copy it to $DEFAULT_T (which should be $TPAGE/table if you're smart). Read it. Then edit it to your requirements. X Copy the sample "schedule" file and copy it to $DEFAULT_S (which should be $TPAGE/schedule if you're smart). Read it. Then edit it to your requirements. You can edit this later or even skip this step if you don't care about the schedule feature. X X3. Read the man page ("nroff -man tpage.l | more"). X X4. Create the $QUEUE_DIR directory (as defined in $TPAGE/tpage.pl and X$TPAGE/tpaged.pl) and do a chmod 0777 on it. Make sure that users on any machine that should be able to run "tpage" can create files there and delete at least their own files. If you use NFS you'll have to export that directory. If you don't use NFS, you're smart. X X5. Set up a cron job that runs "startdaemon" every 20 minutes on the machine with the modems. You might want to run it as root, you might want to run it as any user that can access those modems. X X6. Read the man page ("nroff -man tpage.l | more"). X Test it and you're done. Try: X tpage -d 1xxxyyyzzzz -p 123456 "this is a test" X(substitute your phone number and pin) X tpage alias "this is a second test" X(replace "alias" with a name from the "table" file) X tpage - - this is a test using the schedule and stdin. X^D X X(this should page the "person on duty") X X END_OF_FILE if test 4323 -ne `wc -c <'INSTALL'`; then echo shar: \"'INSTALL'\" unpacked with wrong size! fi # end of 'INSTALL' fi if test -f 'depend' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'depend'\" else echo shar: Extracting \"'depend'\" \(390 characters\) sed "s/^X//" >'depend' <<'END_OF_FILE' What routines use on what routines X---------------------------------- X safeprint: X checksum: safeprint X grabmodem: X send: safeprint X match: safeprint X hangup_modem: send X unlockmodem: X bail_out: hangup_modem unlockmodem X lockmodem: bail_out X getline: bailout safe_print X getpacket: bail_out safeprint X main: lockmodem grabmodem bail_out send match hangup_modem getpacket getline checksum X END_OF_FILE if test 390 -ne `wc -c <'depend'`; then echo shar: \"'depend'\" unpacked with wrong size! fi # end of 'depend' fi if test -f 'ixocico.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ixocico.c'\" else echo shar: Extracting \"'ixocico.c'\" \(12182 characters\) sed "s/^X//" >'ixocico.c' <<'END_OF_FILE' X/* ixocico -- IXO protocol call-in call-out. X** by Tom Limoncelli, tal@warren.mentorg.com X** Copyright (c) 1992, Tom Limoncelli X** The sources can be freely copied for non-commercial use only X** and only if they are unmodified. X** X** Version 1.0 -- See file HISTORY for details. X*/ X X/****************************************************************/ X/* USER CONFIGURABLE OPTIONS: */ X X/* this should be "#define" if you use SunOS, or "#undef" if you X** use HPUX. This controls the name of LOCKDIR and if getpriority() X** is used. I'm sure more needs to be done, but that's a start. X*/ X#define REAL_OS X X#ifdef REAL_OS X#define LOCKDIR "/var/spool/uucp" X#else X#define LOCKDIR "/usr/spool/locks" X/* That may not be correct */ X#endif X X/* not talking to the modem correctly? Try mucking with the grabmodem() routine. */ X X/* END OF USER CONFIGURABLE OPTIONS */ X/****************************************************************/ X X#include X#include X#include X/* #include */ X#include X#include X#include X X#ifdef REAL_OS X#include /* required for */ X#include /* required for getpriority() */ X#endif X X/* ASCII constants */ X#define STX (2) X#define EOT (4) X#define ACK (6) X#define LF (10) X#define CR (13) X#define NAK (21) X#define ESC (27) X#define RS (30) X X#define MAX_PACKET (10000) /* we'll never get a packet this big */ X#define MAXLINE (1000) X X/* only two little global variables, how's that? */ X int modem = 0; char *lockname = NULL; X X/* print a string without worrying about unprintable charactors */ void safeprint(str) char *str; X{ X while (*str) { X if (isgraph(*str)) X (void) fprintf(stdout, "%c", *str); X else { X switch (*str) { X case LF: X (void) fprintf(stdout, "\\n"); X break; X case CR: X (void) fprintf(stdout, "\\r"); X break; X case 32: X (void) fprintf(stdout, "\\s"); X break; X default: X (void) fprintf(stdout, "\\%d", *str); X break; X } X } X str++; X } X fflush(stdout); X} X X/* calculate checksum of a packet */ char *checksum(pk) char *pk; X{ X static char check[10]; X int sum = 0; X X for (;*pk; pk++) sum += *pk; X check[2] = '0' + (sum & 15); sum = sum >> 4; X check[1] = '0' + (sum & 15); sum = sum >> 4; X check[0] = '0' + (sum & 15); X check[3] = 0; X printf("CHECKSUM=:"); safeprint(check); printf(":\n"); X return check; X} X X/* open the modem. You should have done a lockmodem() first */ int grabmodem(dev) char *dev; X{ X struct termios ti; X int modem; X X errno = 0; X modem = open(dev, O_RDWR, 0); X if (errno) { X printf("#MODOPEN modem can't be opened\n"); X return 0; X } X X /* set tty params to 300bps, even parity, 7-1-e */ X errno = 0; X ioctl(modem, TCGETS, &ti); X if (errno) { X close(modem); X return 0; X } X ti.c_iflag |= IGNBRK; /* ignore breaks */ X /* ti.c_iflag |= IGNPAR; *//* ignore parity */ X ti.c_iflag &= ~INPCK; /* ignore parity errors */ X ti.c_iflag |= ISTRIP; /* strip 8th bit */ X ti.c_iflag &= ~INLCR; /* don't cr->nl */ X ti.c_iflag &= ~ICRNL; /* don't cr->nl */ X ti.c_iflag &= ~IGNCR; /* don't ignore cr */ X /* ti.c_iflag &= ~IXON; *//* don't do xon */ X /* ti.c_iflag &= ~IXOFF; *//* don't do xoff */ X X ti.c_oflag &= ~OPOST; /* don't post-process */ X X ti.c_cflag &= ~CBAUD; /* baud=300 */ X ti.c_cflag |= B300; X ti.c_cflag &= ~CSIZE; /* 8-bit bytes */ X ti.c_cflag |= CS8; X ti.c_cflag &= ~CSTOPB; /* one stop bit */ X ti.c_cflag &= ~PARENB; /* parity */ X ti.c_cflag &= ~PARODD; /* even parity */ X ti.c_cflag |= HUPCL; /* hang up on last close */ X /* ti.c_cflag |= CRTSCTS; *//* hardware handshaking */ X ti.c_cc[VMIN] = 0; /* read() can get as few as 0 bytes */ X ti.c_cc[VTIME] = 50; /* time out at 5 seconds no matter what */ X X ti.c_lflag &= ~ISIG; /* disable signals */ X ti.c_lflag &= ~ICANON; /* disable signals */ X ti.c_lflag &= ~ECHO; /* don't echo */ X X errno = 0; X ioctl(modem, TCSETS, &ti); X if (errno) { X close(modem); X return 0; X } X return modem; X} X X/* send data to the modem */ void send(fd, str) int fd; char *str; X{ X printf("Sending: :"); safeprint(str); printf(":\n", str); X write(fd, str, strlen(str)); X} X X/* wait for a particular string from the modem (err = # of times to re-try) */ int match(mod, str, err) X int mod; X char *str; X int err; X{ X int len; X char c; X printf("MATCHING on :"); safeprint(str); printf(":\n", str); X X while (1) { X c = 0; X/* printf("waiting for :%c:\n", *str); */ X len = read(mod, &c, 1); X if (len) { X/* printf("got=%d:%c\n", c,c); */ X if (c == *str) str++; X if (!(*str)) break; /* matched all? Exit loop */ X } X if (!err--) { X printf("NOT MATCHED\n"); X return 1; X } X } printf("MATCHED\n"); X return 0; X} X X/* hang up the modem */ void hangup_modem(fd) int fd; X{ X sleep(3); X send(fd, "+++"); X sleep(3); X send(fd, "ATH\r"); X sleep(1); X} X X/* unlock the modem */ void unlockmodem(name) char *name; X{ X printf("Unlocking modem.\n"); X (void)unlink(name); X return; X} X X/* clean up and leave this program */ void bail_out() X{ X if (modem) { X hangup_modem(modem); X close(modem); X } X if (lockname) unlockmodem(lockname); X exit(0); X} X X/* lock the modem OR DIE*/ char *lockmodem(dev) char *dev; X{ X int lock, pid; X int failcnt = 3; X char waitcnt = 0; X char *lkn, lname[200]; X X strcpy(lname, LOCKDIR); X strcat(lname, "/LCK.."); X strcat(lname, 1 + rindex(dev, '/')); X printf("Lockfile = %s\n", lname); X lkn = strdup(lname); X while (failcnt--) { X errno = 0; X lock = open(lname, O_CREAT | O_WRONLY | O_EXCL, 0777); X if (lock == -1) { X#ifdef REAL_OS X printf("Modem is locked, attempting to steal.\n"); X /* locked, let's read the cookie in the lock */ X pid = 0; X if ((lock = open(lname, O_RDONLY)) != -1) { X (void)read(lock, &pid, sizeof(int) ); X printf("Device is locked by process %d\n", pid); X close(lock); X } X printf("Lock = %d\n", lock); X if (pid < 3) { X printf("#MODOPEN device is locked by pid < 3\n"); X bail_out(); X } X /* see if the process still is alive */ X errno = 0; X (void) getpriority(PRIO_PROCESS, pid); X if (errno == ESRCH) { /* lock process dead, let's go! */ X if (unlink(lname)) { X printf("#MODOPEN Can't steal lock.\n"); X bail_out(); X } else { X printf("Lock is stale, stealing!\n"); X } X } else { X printf("#MODOPEN Valid lock in the way.\n"); X bail_out(); X } X#else X printf("#MODOPEN it's locked, I'm out of here!\n"); X bail_out(); X#endif X } else { X /* lock opened, stuff and go */ X pid = getpid(); X write(lock, &pid, sizeof(int)); X close(lock); X break; X } X } X if (failcnt==-1) { X printf("#MODOPEN Couldn't lock modem after many tries.\n"); X bail_out(); X } X return lkn; X} X X/* get a line from stdin OR DIE */ char *getline(line) char *line; X{ X int len; X char *r; X X /* get a line, if EOF return 0 */ X if (!(r = fgets(line, MAXLINE, stdin))) return 0; X X printf("Data in queue=:"); safeprint(line); printf(":\n", line); X X if (!(len = strlen(line))) { X printf("#BADQUEUE Blank line in queued data\n"); X bail_out(); X } X X if (line[len-1] == '\n') { X line[len-1] = 0; X } else { X /* if fgets didn't return a string ending in \n */ X printf("#BADQUEUE Data in queue has line too long\n"); X bail_out(); X } X return r; X} X X/* Loop until you get a valid packet. If you get a "message sequence" then display it. If you get an invalid pack DIE */ X void getpacket(fd, str) int fd; char *str; X{ X int max; X char c; X int len; X char *buf = str; X int err; X X *str = 0; X err=50; /* permit up to 500 message sequences or bad packets */ X while (err--) { X printf("Getting packet\n"); X max = MAX_PACKET; X while (read(fd, &c, 1) == 1) { X if (c == LF) continue; /* skip LF's */ X if (c == CR) { X /* don't actually put CR in the string */ X break; X } X *buf++ = c; X max--; X if (!max) { X /* packet was too long */ X printf("#PACKLEN packet was too long\n"); X bail_out(); X } X } X *buf = 0; X len = buf - str; X if (len) { X printf("Got packet--length=%d\n", len); X if (isgraph(*str)) { X printf("#GOTMESSEQ message sequece=:"); X safeprint(str); X printf(":\n"); X } else { X /* print packet contents */ X printf("packet is data=:"); safeprint(str); printf(":013:\n"); X return; X } X } X } X printf("#LONELY It's too quiet in here. Disconnecting.\n"); X bail_out(); X} X int main(argc, argv) int argc; char *argv[]; X{ X char dialstring[MAX_PACKET]; X char *pack = dialstring; /* use same workspace */ X char line[MAXLINE+1]; X char pin[MAXLINE+1]; X char mesg[MAXLINE+1]; X int mesnum, failcnt, len; X char c; X X /* check arguments */ X if (argc != 3) { X printf("#WRONGARGS wrong number of arguments\n"); X bail_out(); X } X X /* lock modem or die */ X lockname = lockmodem( argv[1] ); X /* open modem or die */ X printf("opening modem\n"); X modem = grabmodem( argv[1] ); X if (!modem) bail_out(); X X /* see if modem is awake or hangup; after 3 tries die */ X failcnt = 3; X while (failcnt--) { X send(modem, "AT\r"); X if (match(modem, "OK", 9)) { X printf("No response. Hang up and try again.\n"); X hangup_modem(modem); X while (read(modem, &c, 1)) { /*DONOTHING*/ }; X } else break; X } X if (failcnt==-1) bail_out(); X printf("dialing\n"); X X /* send the "A" of "ATDT" */ X do { X send(modem, "A"); X } while (match(modem, "A", 2)); X /* send the rest of the dial string */ X sprintf( dialstring, "TDT%s\r", argv[2] ); X send(modem, dialstring); X (void) match(modem, argv[2], 10); X X /* wait for the modem to connect */ X printf("waiting for CONNECT\n"); X if (match(modem, "CONNECT", 400)) { X printf("#NOCONN no connect\n"); X bail_out(); X } X printf("Waiting for ID=\n"); X X /* send a CR ever second until "ID=" comes back */ X while (1) { X if (match(modem, "ID=", 100)) send(modem, "\r"); X else break; X } X printf("Logging in\n"); X X failcnt = 3; X while (failcnt--) { X /* switch to "automatic protocol" */ X printf("Sending ESC\n"); X write(modem, "\033", 1); X send(modem, "PG1000000\r"); X X printf("Waiting for acceptance (aren't we all)\n"); X getpacket(modem, pack); X len = strlen(pack); X if ((len==1) && (*pack == ACK)) { X printf("login successful\n"); X break; X } X if ((len==1) && (*pack == NAK)) { X printf("retrying to log in.\n"); X continue; X } X if ((len==2) && (*pack == ESC) && (pack[1] == EOT)) { X printf("forced disconnect\n"); X bail_out(); X } X printf("#UNKNOWNPROTO not the response we're looking for. Disconnecting.\n"); X bail_out(); X } X if (failcnt==-1) bail_out(); X printf("waiting for message go ahead\n"); X failcnt=40; X while (failcnt--) { X getpacket(modem, pack); X if (!strcmp(pack, "\033[p")) break; X } X if (failcnt==-1) bail_out(); X printf("got message go-ahead\n"); X X for (mesnum=0; getline(pin); ) { X getline(mesg); X failcnt=100; X while (failcnt--) { X /* build the packet to be sent */ X pack[0] = STX; X pack[1] = 0; X strcat(pack, pin); X strcat(pack, "\r"); X strcat(pack, mesg); X strcat(pack, "\r\3"); /* CR then ETX */ X strcat(pack, checksum(pack)); X strcat(pack, "\r"); X send(modem, pack); X X /* wait for response and deal */ X printf("waiting for validation\n"); X getpacket(modem, pack); X len = strlen(pack); X if ((len==1)&&(*pack==ACK)) { X printf("#MESOK %d message xmitted fine\n", mesnum++); X break; X } else if ((len==1)&&(*pack==NAK)) { X printf("re-retrying message %d\n", mesnum); X } else if ((len==1)&&(*pack==RS)) { X printf("#MESREJECT %d message rejected\n", mesnum++); X break; X } else if ((len==2)&&(*pack==ESC)&&(*pack==EOT)) { X printf("#FORDIS forced disconnect\n"); X bail_out(); X } X } X if (failcnt==-1) { X printf("#PROTERR couldn't send packets\n"); X bail_out(); X } X } X printf("#DONE we're done. Logging out.\n"); X send(modem, "\4\r"); /* EOT then CR */ X X failcnt=3; X while (failcnt--) { X printf("waiting for system logout\n"); X getpacket(modem, pack); X len=strlen(pack); X if ((len==2) && (pack[0] == ESC) && (pack[1] == EOT)) { X printf("#BYE asked to hangup\n"); X bail_out(); X } X if ((len==1)&&(*pack==RS)) { X printf("#WRONGANY something went wrong. (useless msg)\n"); X bail_out(); X } X/* if (!stricmp(line, "NO CARRIER")) break; */ X } X printf("#BYE we're leaving.\n"); X bail_out(); X} X X END_OF_FILE if test 12182 -ne `wc -c <'ixocico.c'`; then echo shar: \"'ixocico.c'\" unpacked with wrong size! fi # end of 'ixocico.c' fi if test -f 'schedule' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'schedule'\" else echo shar: Extracting \"'schedule'\" \(2140 characters\) sed "s/^X//" >'schedule' <<'END_OF_FILE' X# beepersched -- the schedule of who's on-call at what times. X# [For use with tpage.pl -- By Tom Limoncelli (tal@warren.mentorg.com)] X# X# This file is only used if "who" is "-" X# X# The lines that begin with 0-6 refer to the days Sun-Sat. The 48 X# bytes after refer to who's on duty each half-hour. X# X# This schedule shows tal, ingo, and greg as X# having different hours. On Monday night, all three are paged. X# X# A "-" in this list (not to be confused with "-" on the command X# line) means "no body is on duty". If the "-U" (urgent) is used, X# beep2.pl keeps looking for another schedule to see if maybe it X# can find someone else scheduled for that time. The way I manage X# things is to have lower-case letters mean individual people and X# capitol letters meaning the fall-back "urgent" people (usually a list X# of the person and their fallback). For example, "g" is for greg, X# and "G" is for greg and the person that backs him up. X# Of course, you can use any system you wish. X# X# N X# O X# 1 1 O 1 1 X#0 1 2 3 4 5 6 7 8 9 0 1 N 1 2 3 4 5 6 7 8 9 0 1 X0--------------------ttttttttttttttttAAAAAAAAAAAA X1------------ttttgggttttttggggiiiiiiitt---------- X2------------ttttgggttttttggggiiiiiiitttttttttttt X3------------ttttgggttttttggggiiiiiiitt---------- X4------------ttttgggttttttggggiiiiiiitttttttttttt X5------------ttttgggttttttggggiiiiiiitt---------- X6-------------------tttttttttttttttttttttttttt--- X# X# these will only be accessed if -U is used. X# A "-" in this list means "don't call anyone" X# X# 1 1 O 1 1 X#0 1 2 3 4 5 6 7 8 9 0 1 N 1 2 3 4 5 6 7 8 9 0 1 X0TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X1TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X2TTTTTTTTTTTTTTTTTTT--------------TTTTT---------- X3TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT X4TTTTTTTTTTTTTTTTTTT--------------TTTTT---------- X5TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X6TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X# X# g=greg i=ingo t=tal X# X# a person and their backup G=greg,tal I=ingo,greg T=tal,greg X# A=greg,tal,ingo END_OF_FILE if test 2140 -ne `wc -c <'schedule'`; then echo shar: \"'schedule'\" unpacked with wrong size! fi # end of 'schedule' fi if test -f 'startdaemon' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'startdaemon'\" else echo shar: Extracting \"'startdaemon'\" \(595 characters\) sed "s/^X//" >'startdaemon' <<'END_OF_FILE' X#! /usr/local/bin/perl4.019 X X# "start daemon" -- Start the tpaged daemon if it isn't running. X# by Tom Limoncelli, tal@warren.mentorg.com X# Copyright (c) 1992, Tom Limoncelli X X X$BSD = -f '/vmunix'; X$pscmd = $BSD ? "ps -auxww" : "ps -ef"; X open(PS, "$pscmd|") || die "Can't run ps: !$"; X$title = ; X$found = 0; while () { X chop; X $found = 1 if /tpaged/; X} X if (!$found) { X unless (fork) { # this is the child X unless (fork) { # this is the child's child X sleep 1 until getppid == 1; X system "/home/adm/lib/tpage/tpaged &"; X exit 0; X } X exit 0; X } X print "Started tpaged\n"; X} END_OF_FILE if test 595 -ne `wc -c <'startdaemon'`; then echo shar: \"'startdaemon'\" unpacked with wrong size! fi chmod +x 'startdaemon' # end of 'startdaemon' fi if test -f 'table' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'table'\" else echo shar: Extracting \"'table'\" \(623 characters\) sed "s/^X//" >'table' <<'END_OF_FILE' X# beepertab -- table of information about people's beepers. X# [ For use with tpage.pl by Tom Limoncelli (tal@warren.mentorg.com)] X# X# Comment lines begin with "#". X# X# The columns are name, numeric pager phone # (currently unused), IXO X# protocol number to dial, and the person's pagerid. X# X# Aliases are implemented by repeating data. (ooo, fancy!) X# greg 5551212 18006789012 1234567 gregt 5551212 18006789012 1234567 X# ingo 5551213 18006789012 7654321 idean 5551213 18006789012 7654321 X# tom 5551214 18006789012 1122334 tal 5551214 18006789012 1122334 END_OF_FILE if test 623 -ne `wc -c <'table'`; then echo shar: \"'table'\" unpacked with wrong size! fi # end of 'table' fi if test -f 'tpage.l' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tpage.l'\" else echo shar: Extracting \"'tpage.l'\" \(6781 characters\) sed "s/^X//" >'tpage.l' <<'END_OF_FILE' X.ds l local X.TH TPAGE N "30 Jan 1992" X.BY "Tom Limoncelli, tal@warren.mentorg.com" X.SH NAME tpage \- front-end to Tom's Pager System X.br tpaged \- daemon that manages paging queue X.br ixocico \- program that executes the IXO protocol X.SH SYNOPSIS X.B /usr/local/bin/tpage X[ X.B \-T table file X] [ X.B \-S schedule file X] [ X.B \-U X] [ X.B \-d phone num X] [ X.B \-p pin X] [ X.B \-t X] [ X.B \-v X] [ X.B \-m X] [ X.B \-M X] [ X.B \-e X] X.B who X[ X.B message or X.B \- X] X.br X.B /home/adm/lib/tpage/tpaged X.br X.B /home/adm/lib/tpage/ixocico dev phonenum X.br X.SH DESCRIPTION X.I tpage is the front-end to the system. A user can run this program to submit a message to someone's pager. X.PP The program wants 4 things: (1) the phone number to dial, (2) the person's PIN pin (Personal Identification Number), (3) who to email if the page isn't accepted, and (4) the message. X.PP Once it has all that information, it dumps it into a file which is read by X.B tpaged which delivers the message to the paging service using the appropriate protocol. tpaged selects the correct program for you. Each protocol is implemented as a separate program. Currently \fBixocico\fR is the only program to choose from, and it implements the IXO protocol. X.PP The phone number (1) and PIN (2) can be specified with the X.B \-d and X.B \-p options, OR by specifying a name (which instructs tpage to look up the information in the pager table file), OR by specifying the name ``\-'' which means ``whoever is on duty'' (which instructs tpage to find out who is on duty from the schedule file, and then look up their phone number and PIN in the pager table file). X.PP Specifying a phone number without a PIN (or vice-versa) results in the missing data being looked up in the pager table file. X.PP A comma-separated list of names may be given. It is much more efficient to use a list of names than to do single pages. X.PP NOTE: A name must always be specified, so if you use the X.B \-d and X.B \-p options you must also specify a name (such as ``foo'') which will be ignored. Combining a list of names with the X.B \-d and/or X.B \-p options works in a logical but undefined manner. X.PP To specify who to email if there is a problem (3) use the X.B \-e option. The default for this is to not send email to anyone. X.PP The message (4) can be specified on the command line or if ``-'' is given, stdin is read for the message. No matter how many bytes you give it, the high-bit is stripped, RETURNs and TABs are turned into spaces, and groups of spaces are turned into single spaces. The first 160 bytes (configurable in the program) is all that's sent, since that's all that the pager will display. X.PP Command-line options are: X.TP 10 X.BR \-T " table file" X\fItable file\fR is the file that has the table of people and the info needed to communicate with their pager. The default is /home/adm/lib/tpage/table. X.TP X.BR \-S " schedule file" X\fIschedule file\fR is the file that has the is the file that is used to find out who's on duty at this moment. This file is only consulted if ``who'' is ``\-''. The default is X/home/adm/lib/tpage/schedule. X.TP X.B \-U This option marks the message as \fIurgent\fR. If the schedule lists that no one is on duty at that time but the message is marked \fIurgent\fR, a secondary schedule is consulted. X.TP X.BR \-d " phone num" Ignore what the table file says, use this phone number when dialing. If a list of people is specified, this phone number is used for the first person, the others will be looked up in the \-T file. X.TP X.BR \-p " pin" Ignore what the table file says, use this PIN when transmitting the message. If a list of people is specified, this phone number is used for the first person, the others will be looked up in the \-T file. X.TP X.B \-t Act as a ``tee''. Copy stdin to stdout. If you give this option and the message is not coming from X.TP X.B \-v Verbose mode. Currently useless since there isn't anything extra worth printing. X.TP X.B \-m Parse the input as mail. Skip all the headers but extract the ``From'', X``Subject'', and ``Priority'' lines. If they exist, append to the beginning of the message ``F: frominfo'', ``S: subject line'', X``P: priority''. ``F:'' and ``P:'' are clipped to be one screenful in length. They are all padded out to the end of the screen. X.TP X.B \-M Skip ``mail quoted'' lines. Netnews and Mail often have other messages quoted by prefixing each line with greater than symbol. This option skips any input line that begins with zero or more whitespace charactors, followed by zero or more letters or numbers, followed by zero or more of \<, \>, {, or }. This should catch the normal quoting methods as well as anything the perverse superquote.el package for GNU Emacs might come up with. X.TP X.B \-e On error, send email to this person. If any errors happen when the tpage command is beign run, the user is notified. This is for sending email when the page is being processed. In other words, if the PIN is incorrect. If the phone number is incorrect the tpaged daemon will keep redialing and redialing it trying to figure out why it can't get through. X.PP X.IR tpaged tpage is a program that you don't need to know about. Your sysadmin should have installed it for you. It wakes up about every 20 seconds, sees if there are any new messages to send out and tries to send them. It can understand multiple paging protocols (tpage picks the best one for you) though it currently only knows about the IXO protocol. tpage can run as ``root'' but is often just run as ``daemon''. X.PP X.IR ixocico is the message transport program for the tpage system. It is called by X.B tpaged and told what device to use and what phone number to dial on the command line. It gets the PINs and messages to send from stdin. X.PP It co-exists with the uucp programs fine as it uses the same methods to lock the modems. It notices stale locks and blows them away. Not all locking features have been proven to work on HPUX, only SunOS. It will not wait for a modem to be unlocked. X.PP X.B tpaged watches the output of X.B ixocico for lines beginning with # to know success or failure of particular messages and of the entire batch. X.PP X.SH FILES X.ta 6c X.nf X/home/adm/lib/tpage/schedule schedule of who's on duty X/home/adm/lib/tpage/table table of people and their pager info X.SH SEE ALSO uucico(1), xkill(l) X.SH HISTORY Written by Tom Limoncelli (tal@Warren.MENTORG.COM) at Mentor Graphics Corporation, Silicon Design Division, Warren, New Jersey. May be re-distributed only in it's unmodified form. X.SH BUGS If X.B \-d and X.B \-p are specified, a name still must be specified. X.PP It currently only compiles under SunOS even though some defines are inserted so that it doesn't fail all over the place on silly operating systems like HPUX. END_OF_FILE if test 6781 -ne `wc -c <'tpage.l'`; then echo shar: \"'tpage.l'\" unpacked with wrong size! fi chmod +x 'tpage.l' # end of 'tpage.l' fi if test -f 'tpage.pl' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tpage.pl'\" else echo shar: Extracting \"'tpage.pl'\" \(9757 characters\) sed "s/^X//" >'tpage.pl' <<'END_OF_FILE' X#! /usr/local/bin/perl4.019 X X# tpage.pl -- front-end to tpage system. X# by Tom Limoncelli, tal@warren.mentorg.com X# Copyright (c) 1992, Tom Limoncelli X# The sources can be freely copied for non-commercial use only X# and only if they are unmodified. X X# Version 1.0 -- See file HISTORY for details. X X#################################################################### X# X# Parameters that the user can set: X# X#################################################################### X X$debug = 0; X# leave that off X X$MAX_WINDOW = 16; X#This is the number of charactors at a time do you see on your X# pager. This is used when word-wrapping. X X$MAX_MESSAGE = 160; X# How many bytes can one message be. This must be less than 250 X# minus the length of your PIN. This is because each packet in the ixo X# protocol must be less than 250 chars. If you have a pager that can X# receive longer messages, you'll have to modify the ixocico.c program X# to handle the "packet continuation" feature. No biggie, just X# something that I didn't feel like implementing since I can't even X# test it with my pager. X X$DEFAULT_S = '/home/adm/lib/tpage/schedule'; X# (default: '/home/adm/lib/tpage/schedule') X# If you plan on using the schedule feature, this is the file X# name where beep2.pl will look for the schedule. It must be accessable X# on the machine that runs tpage.pl, not the machine that runs the X# daemon (tpaged.pl). X X$DEFAULT_T = '/home/adm/lib/tpage/table'; X# (default: '/home/adm/lib/tpage/table') X# If you plan on using the table feature (that is, store a list X# of people and their paging info), this is the file name where tpage.pl X# will look for the data. It must be accessable on the machine that X# runs tpage.pl, not the machine that runs the daemon (tpaged.pl). X X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/'; X# (default: '/home/adm/lib/tpage/pqueue/' X# This is the directory where messages will be queued. The trailing "/" X# is required. X X#################################################################### X# some helping functions X require("getopts.pl"); X sub strip_string { X local($s) = @_; print "DEBUG: REMOVE_CONTROLS :", $s, ":\n" if $debug; X $s =~ tr/\200-\377/\000\177/; # remove high-bit X $s =~ tr/\000-\037\177//d; # delete unprintables X $s =~ s/\s+/ /g; # change groups of white space into " " X $s =~ s/^ //; # remove spaces from the front X $s =~ s/ $//; # remove spaces from the end X print "DEBUG: REMOVE_DONE :", $s, ":\n" if $debug; X return $s; X} X X#################################################################### X# Here's the actual program X X#################################################################### X# Get the command line options. X X# set the defaults X print "\n"; X X# -S schedule file X$opt_S = $DEFAULT_S; X# -T pager table X$opt_T = $DEFAULT_T; X# -U use urgent schedule if no one is scheduled for that time. X$opt_U = 0; X# -d number to dial. (first name in list only) X$opt_d = ""; X# -p pager id to use. (first name in list only) X$opt_p = ""; X# -t tee all stdin into stdout. X$opt_t = 0; X# -v verbose mode. X$opt_v = 0; X# -m input will be in RFC822, skip boring stuff. X$opt_m = 0; X# -M like -m but also skip >-quoted text. X$opt_M = 0; X# -e if it errors, send email to this person. X$opt_e = ""; X X$line_from = ""; X$line_subj = ""; X$line_prio = ""; X do Getopts('S:T:Ud:p:tvmMe:'); X X# get the wholist X$opt_wholist = shift (@ARGV); X$opt_wholist = "special" if $opt_d && $opt_p; X X#################################################################### X# Get the message (either on the command line or stdin; handle -t -m -M X X# bunch up all the rest X$opt_message = join(' ', @ARGV); print "opt_message = :$opt_message:\n" if $debug; X$opt_message = &strip_string( $opt_message ) if $opt_message; print "opt_message = :$opt_message:\n" if $debug; die "$0: No message. Cat got your tongue?" if ( $opt_message eq "" ); X die "$0: Can't use -m/-M and have message on the command line" X if ($opt_m || $opt_M) && $opt_message ne '-'; X X# maybe get message from stdin, echoing to stdout if $opt_t; if ($opt_message eq '-') { X $opt_message = ''; X # handle -m headers first X if ($opt_m) { X print "DEBUG: Doing -m work\n" if $debug; X local($line) = ""; X while (<>) { X if ( /^\S/ || /^$/ ) { # start of new header, do previous one X $line_from = substr($line, 6) if $line =~ /^From/; X $line_subj = substr($line, 9) if $line =~ /^Subject: /; X $line_prio = substr($line, 10) if $line =~ /^Priority: /; X $line = $_; X } else { X $line .= $_; X } X last if /^$/; # end of headers, start processing X } X } X $line_from = &strip_string( $line_from ) if $line_from; X $line_subj = &strip_string( $line_subj ) if $line_subj; X $line_prio = &strip_string( $line_prio ) if $line_prio; X X while (<>) { X# -M means skip if the line is news quoted email. X# a line is news quoted if it begins with one of the following: X# [white] [word] quote X# where "white" is any amount of whitespace (zero or one times) X# where word is any letters/numbers (userid) (zero or one times) X# where quote is any of >, <, }, or {. X next if $opt_M && /^\s*\S*[\>\}\<\{]/; X print if $opt_t; X $_ = &strip_string( $_ ); X $opt_message .= $_; X $opt_message .= " "; X # once we've got quite a bunch, screw the rest. X if ( length($opt_message) > ($MAX_MESSAGE * 8)) { X while (<>) { print if $opt_t; } X } X } X} X X#################################################################### X# massage the message X if ($debug) { X print "DEBUG: pre-processed messages\n"; X print "FROM=:$line_from:\n"; X print "PRIO=:$line_prio:\n"; X print "SUBJ=:$line_subj:\n"; X print "MESS=:$opt_message:\n"; X} X X$line_from = substr( "F: " . $line_from . ' ' x $MAX_WINDOW, X 0, $MAX_WINDOW) if $line_from; # pad to display size X X$line_prio = substr( "P: " . $line_prio . ' ' x $MAX_WINDOW, X 0, $MAX_WINDOW) if $line_prio; # pad to display size X X$l = $MAX_WINDOW * int ((length($line_subj)+$MAX_WINDOW+2) / $MAX_WINDOW); X$line_subj = substr( "S: " . $line_subj . ' ' x $MAX_WINDOW, X 0, $l) if $line_subj; # pad to display size X X$opt_message = &strip_string( $opt_message ); X# put it all together X$the_message = substr( $line_prio . $line_from . $line_subj . $opt_message, 0, $MAX_MESSAGE); X if ($debug) { X print "DEBUG: post-processed messages\n"; X print "FROM=:$line_from:\n"; X print "PRIO=:$line_prio:\n"; X print "SUBJ=:$line_subj:\n"; X print "MESS=:$opt_message:\n"; X print "COMPLETE=:$the_message:\n"; X} X X#################################################################### X# At this point we can do some more of the sanity checking. X X#die "$0: Conflicting verbosity levels" if ($opt_s && ($opt_v || $opt_V)); die "$0: Schedule file $opt_S can't be read/found" X unless ( ($opt_wholist eq '-') || (-r $opt_S && -r $opt_T) ); die "$0: Pager table $opt_T can't be read" X unless ($opt_d && $opt_p) || ( -r $opt_T ); X X#################################################################### X# use the schedule to fill in "who" if we need. X if ($opt_wholist eq '-') { X local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); X local($l) = $wday; X local($h) = $hour * 2 + int ($hour / 30) + 1; X local($w,$found1) = 0; X print "L = $l\n" if $debug; print "H = $h\n" if $debug; print "U = $opt_U\n" if $debug; X X # Read from schedule until you hit a line beginning with $l. X # At that point, get the char $h bytes in. If that byte is "-", X # and $opt_U, keep going. X print "\nChecking schedule:\n\n"; X open(SCHED, "<$opt_S") || die "Can't open $opt_S: $!"; X while (1) { X $w = ''; X while () { X last if /^${l}/; X } X $w = substr($_, $h, 1); X $found1 = 1 if $w; # we found one! X next if $opt_U && $w eq '-'; X last; X } X X die "$0: Schedule doesn't have a line for this day of the week.\n" unless $found1; X die "$0: No one is assigned to be on duty at this time.\n" if $w eq '-'; X X # Now search until a line begins with $w= and assign line to wholist X $opt_wholist = ''; X while () { X next unless /^${w}\=/; X chop( $opt_wholist = substr($_, 2) ); X } X die "$0: Schedule error: No people assigned to '" . $w . "'\n" unless $opt_wholist; X close SCHED; X} X X#################################################################### X# we we still don't know who to call, bail out. X die "$0: The schedule didn't specify anyone to call!" X unless ($opt_wholist) || ($opt_d && $opt_p); die "$0: There isn't anyone scheduled for this time of day." X if $opt_wholist eq '-'; X X#################################################################### X# rotate through "$opt_wholist" and queue each person X X$cnt = 0; foreach $who ( split(',', $opt_wholist) ) { X $cnt++; X X # look up "who"'s information X open(TABL, "<$opt_T") || die "Can't open $opt_T: $!"; X while () { X next if /^#/; X chop; X local($name,$phonen,$phonea,$pin) = split; X if ($name eq $who) { X $opt_d = $phonea unless $opt_d; # might have it from ARGV X $opt_p = $pin unless $opt_p; # might have it from ARGV X print "Got $who is :$opt_d:$opt_p:\n" if $debug; X last; X } X } X close TABL; X X die "$0: We were not able to find a phone number for $who.\n" unless $opt_d; X die "$0: We were not able to find a PIN for $who.\n" unless $opt_p; X X # write into the queue the proper information. X chop( $thishost = `hostname` ); X $qname = $QUEUE_DIR . "P" . $thishost . time . $cnt; X print "QUEUE=$qname\n" if $debug; X open(QU, ">$qname" ) || die "Can't open $qname for writing: $!"; X print QU "A\n"; X print QU $opt_d, "\n"; X print QU $opt_p, "\n"; X if ($opt_e eq '-') { # '-' means send errors to $who, X print QU $who, "\n"; X } else { X print QU $opt_e, "\n"; X } X print QU $the_message, "\n"; X print QU "X\n"; X close QU; X print "Message queued for $who: $the_message\n"; X X # zap the phone# and PIN so that ARGV's info only effects us once. X $opt_d = ""; X $opt_p = ""; X} X print "\n"; END_OF_FILE if test 9757 -ne `wc -c <'tpage.pl'`; then echo shar: \"'tpage.pl'\" unpacked with wrong size! fi chmod +x 'tpage.pl' # end of 'tpage.pl' fi if test -f 'tpaged.pl' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tpaged.pl'\" else echo shar: Extracting \"'tpaged.pl'\" \(8441 characters\) sed "s/^X//" >'tpaged.pl' <<'END_OF_FILE' X#! /usr/local/bin/perl4.019 X X# tpaged -- back-end to tpage system. X# by Tom Limoncelli, tal@warren.mentorg.com X# Copyright (c) 1992, Tom Limoncelli X# The sources can be freely copied for non-commercial use only X# and only if they are unmodified. X X# Version 1.0 -- See file HISTORY for details. X X#################################################################### X# X# Parameters that the user can set: X# X#################################################################### X X$debug = 0; X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/'; # same as in tpage.pl X$IXOCICO = '/home/tal/work/beep2/ixocico'; # where is ixocico? X$MAIL = '/usr/ucb/mail'; # which mail to use? X X# list of devices to rotate through. X@DEVICES = ( "/dev/ttyz4" ); # currently they are all spoken X# to at the same speed and same parameters. Some day I'll set up X# a modemtab system, but I don't think more than one modem is X# really needed for this system. X X# amount of time to sleep between scans X$SLEEP_TIME = 30; X X#################################################################### X# QUEUE FILES FORMAT: X# X# Files in the queue have the name of the format in the X# first line. Currently there is only one format and it X# is named "A". The first line marks it as the "A" format. X# a subroutine called read_format_A reads this format. Other X# formats can be written (see comments by read_format_A) X# X# The "A" format: X# line contents X# 1: A\n X# 2: number to dial\n X# 3: pin\n X# 4: entire message\n X# 5: X\n X X# read_format_* -- modules that read various data formats. X# Currently implemented: The "A" format. X# do_proto_* -- modules that do various beeper protocols. X# Currently implmented: the ixo protocol. X# Future protocols: numeric-only pagers. X X#################################################################### X# Here's the actual program X X# define some globals X local(%ixo_dial); local(%protocols); X while (1) { X local ($first,$allfiles); X X # We could scoop up all the files and process them, but chances X # are if one file is found, more are on the way. So, instead X # we scoop, if any are found we sleep 5 seconds and re-scoop. X while (1) { X opendir(QDIR, $QUEUE_DIR) || die "$0: Can't open $QUEUE_DIR: $!"; X @allfiles = grep( !/^\./, readdir(QDIR) ); X closedir(QDIR); X X if ($#allfiles!=-1) { # files? take 5 and then process. X sleep 5; X last; X } else { # no files? hibernate. X sleep $SLEEP_TIME; X next; X } X } X X print "DEBUG: allfiles= ", join(' ', @allfiles), "\n" if $debug; X opendir(QDIR, $QUEUE_DIR) || die "$0: Can't open $QUEUE_DIR: $!"; X @allfiles = grep( !/^\./, readdir(QDIR) ); X closedir(QDIR); X print "DEBUG: allfiles= ", join(' ', @allfiles), "\n" if $debug; X X foreach $file (@allfiles) { X print "DEBUG: Doing $file\n" if $debug; X open(DATA, "<" . $QUEUE_DIR . $file) || print "Can't open $file: $!"; X chop( $first = ); print "DEBUG: first=$first\n" if $debug; X eval "do read_format_$first();"; X } X X foreach $proto (sort keys %protocol) { X next unless $protocol{ $proto }; X eval "do do_protocol_$proto();"; X $protocol{ $proto } = 0; X sleep $SLEEP_TIME; X } X} X X# Each read_format_ must: X# read from and then close(DATA). X# %protocol{ protocol name } = 1 (for the protocol to use) X# and stuff the right data into the right variables for that protocol X# to use. X sub read_format_A X{ X local($dial,$pin,$error,$mess,$X); X print "DEBUG: reading format A\n" if $debug; X chop( $dial = ); X chop( $pin = ); X chop( $error = ); X chop( $mess = ); X chop( $X = ); X X return if $X ne "X"; # file isn't in correct format or isn't done. X return if $dial eq ""; X return if $pin eq ""; X return if $mess eq ""; X X $protocol{ 'ixo' } = 1; X $ixo_dial{ $dial } = 1; X eval "unshift(@ixo_${dial}_pin, \$pin)"; X eval "unshift(@ixo_${dial}_error, \$error)"; X eval "unshift(@ixo_${dial}_mess, \$mess)"; X eval "unshift(@ixo_${dial}_file, \$file)"; X} X X# Each do_protocol_ must: X# delete files out of the queue that are successful. X# delete files out of the queue that are aged. X# clean up so that the routine can be called again. X sub do_protocol_ixo { X print "DEBUG: doing protocol IXO\n" if $debug; X# local($pin, $error, $mess, $file, $cmd, $empty); X local($pin, $error, $mess, $file, $cmd); X# $empty = 1; X # build the temp file and the command line X local($tmpfile) = "/tmp/tpaged.$$"; X foreach $dial (keys %ixo_dial) { X next unless $ixo_dial{ $dial }; X print "DEBUG: Number to dial is $dial\n" if $debug; X open(IX, ">$tmpfile" ) || die "$0: Can't create $tmpfile: $!"; X foreach (eval "@ixo_${dial}_pin") { X print IX eval "pop(@ixo_${dial}_pin )", "\n"; X print IX eval "pop(@ixo_${dial}_mess)", "\n"; X unshift(@ixo_list, eval "pop(@ixo_${dial}_error)" ); X unshift(@ixo_list, eval "pop(@ixo_${dial}_file )" ); X# $empty = 0; X } X close IX; X print "DEBUG: ", ($#ixo_list) / 2, " messages to send.\n" if $debug; X# if ($empty) { X# print "DEBUG: nothing to do!\n" if $debug; X# unlink $tmpfile; X# return; X# } X $cmd = $IXOCICO . " <" . $tmpfile . " " X . push(@DEVICES, shift @DEVICES) . " " . $dial; X print "DEBUG: About to execute: $cmd\n" if $debug; X open(IX, $cmd . "|") || die "$0: Can't execute ixocico: $!"; X $mesgnum = 0; # count the messages as they're processed X $success = 0; X # get to the next message (same as #MESOK) X $error = pop(@ixo_list); X $file = pop(@ixo_list); X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug; X while () { X print if $debug; X next unless /^#/; X X print unless $debug; X X /^#WRONGARGS / && X die("$0: Major program bug: $!"); X /^#NOCONN / && do { X printf("$0: Nobody answered the phone!\n") if $debug; X last; X }; X /^#UNKNOWNPROTO / && do { X print "$0: Uhhh, are you sure that's a pager service?\n" if $debug; X last; X }; X /^\#MESOK (\d) / && do { X print "DEBUG: message $1 done.\n" if $debug; X if ($1 == $mesgnum++) { X print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug; X unlink $QUEUE_DIR . $file; X # get to the next message (same as #MESREJECT) X $error = pop(@ixo_list); X $file = pop(@ixo_list); X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug; X } else { X print "Things have gotten out of sync. Restarting.\n" if $debug; X last; X } X }; X /^#MESREJECT (\d) / && do { X if ($1 == $mesgnum++) { X if ($error) { X $cmd = "$MAIL <" . $QUEUE_DIR . $file . " -s 'the following pin and message was rejected' " . $error; X print "DEBUG: About to execute $cmd\n" if $debug; X } X print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug; X unlink $QUEUE_DIR . $file; X # get to the next message (same as #MESOK) X $error = pop(@ixo_list); X $file = pop(@ixo_list); X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug; X } else { X print "Things have gotten out of sync. Restarting.\n" if $debug; X last; X } X }; X /^#FORDIS / && do { X print "Forced disconnect from server.\n" if $debug; X last; X }; X /^#PROTERR / && do { X print "Server not following protocol.\n" if $debug; X last; X }; X ( /^#DONE / || /#BYE / ) && do { X if ($#ixo_list) { X print "Done with sending batch. Waiting BYE.\n" if $debug; X $success = 1; X next; X } else { X print "ixocico is done but more data waiting to be sent! Program bug.\n" if $debug; X last; X } X }; X /^#WRONGANY / && do { X print "We've been notified that one of the batch may have been not xmited.\n(great protocol, eh?)\n" if $debug; X next; X }; X /^#BADQUEUE / && do { X die "$0: Programmer error. Data in queue is bad: $_\n"; X }; X /^#MODOPEN / && do { X print "Modem can't be opened\n" if $debug; X last; X }; X /^#PACKLEN / && do { X die "$0: Protocol error. Should never happen: $_\n"; X }; X /^#GOTMESSEQ / && do { X print "MESSAGE: $_\n" if $debug; X }; X /^#LONELY / && do { X print "Hello? Hello? Either I'm getting the silent treatment or the server is dead." if $debug; X last; X }; X } X close IX; X unlink $tmpfile; X $ixo_dial{ $dial } = 0; X } X X print "DEBUG: ixo_dial = " if $debug; X foreach $i (keys %ixo_dial) { X next unless $ixo_dial{ $dial }; X print $i if $debug; X }; X print "\n" if $debug; X X eval "@ixo_${dial}_pin = ()"; X eval "@ixo_${dial}_mess = ()"; X eval "@ixo_${dial}_error = ()"; X eval "@ixo_${dial}_file = ()"; X $ixo_list = (); X} END_OF_FILE if test 8441 -ne `wc -c <'tpaged.pl'`; then echo shar: \"'tpaged.pl'\" unpacked with wrong size! fi chmod +x 'tpaged.pl' # end of 'tpaged.pl' fi echo shar: End of shell archive. exit 0