home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-05-17 | 53.8 KB | 1,762 lines |
- Newsgroups: comp.sources.misc
- From: tal@Warren.MENTORG.COM (Tom Limoncelli)
- Subject: v37i068: ixobeeper - Talk to alpha-numeric pager from Unix, Part01/02
- Message-ID: <csm-v37i068=ixobeeper.222854@sparky.IMD.Sterling.COM>
- X-Md4-Signature: 5c45478c5bc9061ff28a212b33764b8c
- Date: Tue, 18 May 1993 03:29:53 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: tal@Warren.MENTORG.COM (Tom Limoncelli)
- Posting-number: Volume 37, Issue 68
- Archive-name: ixobeeper/part01
- Environment: UNIX, Sun, HPUX
- Supersedes: ixobeeper: Volume 25, Issue 43
-
- Do you have an alpha-numeric pager (beeper)? Ever wonder about
- that little box that you rent to enable you to send messages?
- The box is just a keyboard, a modem, and a bit of software that
- speaks the IXO protocol. This program lets you speak the IXO
- protocol (manual mode only) and since you can run this program
- from shell scripts, Perl, Sun Netmanager, whatever, your computer
- can keep you up to date no matter where you are.
-
- "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.
-
- The system has the following features:
-
- ...sends pages to any pager system that supports the IXO protocol.
-
- ...additional protocols can be added. (I'll write the touch-tone
- protocol soon).
-
- ...can parse email messages and extract the interesting info from
- them resulting in shorter messages.
-
- ...can copy it's input to stdout and therefore can be used as a "tee".
-
- ...maintains a directory of people's phone numbers/PINs.
-
- ...can page "the person on duty" (searches a schedule).
-
- ...schedule can have slots that are empty, but find someone anyway if
- the message is marked "urgent".
-
- ...with programs like procmail, permits you to send certain email
- messages to your pager.
-
- ...a list of modems can be given to the daemon.
-
- If you aren't using "procmail", you're working too hard. Check
- "archie" for a location near you!
-
- ---- Cut Here and unpack ----
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # Contents: README HISTORY ixo.doc ixocico.c tpage.pl tpaged.pl
- # Wrapped by kent@sparky on Mon May 17 22:12:19 1993
- PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 1 (of 2)."'
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(2321 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- XREADDME -- 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
- Xyou send messages to alpha-numeric pagers using the "IXO" protocol.
- XIt supports a dialing directory, a "who's on duty now" schedule,
- Xand can do special tricks with RFC822-format email.
- X
- XThe 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
- XHow it works (and how all the programs fit together):
- X
- Xo beep2.pl takes command-line and stdin, reads the schedule, looks
- X up people's paging info in the directory, and queues the message.
- Xo tpaged.pl should always be running. Every 30 seconds it wakes up
- X to check the queue for messages.
- Xo tpaged.pl then sorts and batches the messages.
- Xo 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 Messages that can't be deleted from the queue (due to NFS problems
- X or whatever) are added to a "blacklist" in memory, and are not
- X retried.
- X
- XFor installation instructions, read INSTALL.
- XFor program history, read HISTORY.
- X
- XIf you aren't using "procmail", you're working too hard. Check
- X"archie" for a location near you!
- END_OF_FILE
- if test 2321 -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'\" \(2174 characters\)
- sed "s/^X//" >'HISTORY' <<'END_OF_FILE'
- XHISTORY
- 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
- Xbeep2.pl:
- X 1.0 Jan 31, 1992 -- First release.
- X 1.1 Apr 20, 1992 -- Second release.
- X 2.0 May 4, 1992 -- Sets umask before creating queue entries.
- X
- Xtpaged.pl history:
- X 1.0 Jan 31, 1992 -- First release.
- X 1.1 Apr 20, 1992 -- New "wait before dial" algorithm.
- X Hunted for "endless-repeat" bug.
- X Now checks status of unlink.
- X 2.0 May 4, 1992 -- Re-wrote data structures of ixo_* routines
- X to make things more reliable. No more parallel
- X arrays, maintains records instead.
- X Now "blacklists" files that couldn't be deleted.
- X2.1 patch 1, May 6, 1992--
- X-- &do_protocol_ixo should declare $index as a local variable.
- X-- &ixo_listindex should always be called with 1 parameter.
- X-- Rejected messages should be deleted from queue directory and from
- X memory. If they can't be deleted from the directory, blacklist that
- X file. (previously they were not properly deleted from memory and no
- X attempt was made to remove them from disk) (LOOPING BUG FIXED)
- X-- Debug mode prints a few new messages.
- X-- &ixo_mesg_append didn't declare $count as local.
- X-- &ixo_mesg_get didn't declare $pin, $error, $mess, $file as local.
- X
- Xixocico.c
- X 1.0 Jan 31, 1992 -- First release.
- X 1.1 Feb 4, 1992 -- Tuned some of the time-outs
- X 2.0 May 4, 1992 -- Added DJ's 2 patches
- X
- END_OF_FILE
- if test 2174 -ne `wc -c <'HISTORY'`; then
- echo shar: \"'HISTORY'\" unpacked with wrong size!
- fi
- # end of 'HISTORY'
- fi
- if test -f 'ixo.doc' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ixo.doc'\"
- else
- echo shar: Extracting \"'ixo.doc'\" \(8737 characters\)
- sed "s/^X//" >'ixo.doc' <<'END_OF_FILE'
- XHere is a summary of the IXO protocol. All typos and
- Xmisinterpretations are mine. This is based on reading the Glenayre
- XElectronics book. I have not yet implemented the automatic protocol,
- Xbut I have implemented the manual one.
- X
- XYou are "remote entry device" and the system that you dial into is
- X"paging central".
- X
- X(1) Remote sends CR at two second intervals until paging central
- Xresponds with ID= at correct baud rate or until three transmissions
- Xhave been completed. (This step exists to allow for possible future
- Xbaud rate recognition).
- X
- XSome systems have chosen to send ID= from the central if they do not
- Xreceive CR in about two seconds. This variation appears to be
- Xacceptable as the central continues to respond to CR's with ID='s.
- X
- X(2) Central sends ID=. Request for ID returned within one second of
- Xreceipt of CR. Paging central may, at its option, send CR or CR LF
- Xafter ID=
- X
- XPaging central may optionally choose to send ID= if it does not
- Xreceive CR after appropriate waiting time.
- X
- XFOR AUTOMATIC REMOTE ENTRY:
- XAt this point you can start following 2A or 2M depending on which
- Xsub-protocol you want to use.
- X
- X(2A) Remote sends ESC SST (typically ESC PG1)
- XESC signifies entry device intends to talk in automatic dump mode.
- X
- XSS is a set of two alphanumeric charactoers signifying a type of
- Xservice to be accessed:
- X
- XFor a paging service where:
- XField 1 = Pager ID
- XField 2 = Message
- XSS will be sent at "PG".
- X
- XWhere T is a single alphanumeric character relating to the type of
- Xterminal or device attempting to send the message. T = "1" is a
- Xcategory of entry devices using the same protocol. The PETs and IXO
- Xdevices are members of this category. T = "7" or "8" or "9" are
- Xreserved for wild card terminals or devices which may relate to a
- Xspecific users' system.
- X
- X(3A) Remote sends 6-char alphanumeric password then CR. (Password is
- Xoptional and is, in general, reserved for future services. Password
- Xmay be interpreted as either a caller ID or a system entry key.
- XLength of password, when used, may be different in some systems.)
- X
- XWhen an incorrect logon sequence beginning with ESC is received, the
- Xpaging central may respond with an ID= if it requires a
- Xretransmission.
- X
- X(4A) Central responds with a message sequence (lines of text, each one
- Xfollowed by a CR). (Typical messages are "Processing - Please Wait",
- X"4 pages sent" or "Bad Phone Line Call Again".
- X
- X(5A) Central then sends one of three sequences:
- XCR ACK CR This means "logon accepted"
- XCR NAK CR This means "requested again"
- XCR ESC EOT CR This means "Forced disconnect"
- X
- X(6A) Central may then send another message sequence (optional).
- X
- X(7A) Central sends ESC [p CR
- XMessage go ahead is sent when paging central is ready for new
- Xinformation. The "p" is lowercase.
- X
- X(8A) Remote sends STX field1 CR field2 CR field3 CR ... fieldn CR
- XEEE <CHECKSUM> CR
- XYou can send as many fields as you wish. PG terminals send the
- Xpager-id in field1 and the message in field2. (no other fields are
- Xdefined). This entire message can be a max of 250 bytes. A field may
- Xbe any length and where necessary may be continued in following
- Xblocks. A field always ends with a CR. The CR field delimiter
- Xsuggests CR may not be used within a field. A block always begins
- Xwith a STX and ends with a checksum followed by a CR. The characters
- Xpreceding the checksum depends on what, if anything, is continued
- Xbeyond the block boundary.
- X
- XEEE can be an ETX, ETB, or RS.
- X
- XThe ETX is used as a block termination indicator if a given
- Xtransaction (fields 1 through N) ends within the block being
- Xtransmitted.
- X
- XThe ETB is used as a block terminator if the transaction is continued
- Xinto the next block, but the last field in the current block is
- Xcomplete.
- X
- XIf the last field within the current block is to be continued in the
- Xnext block, no CR is inserted at the end of the first portion of the
- Xfield and the US character is used as a block termination character.
- XThe CR terminating the broken field is sent at the end of the field in
- Xwhatever block the field actually terminates.
- X
- XNo limit is established within the protocol itself regarding the
- Xnumber of transactions, the number of fields or the number of blocks
- Xper field; however, a particular user system may have limits on any of
- Xthese items.
- X
- XSome systems may be limited to one block per transaction and one
- Xtransaction per telephone connection.
- X
- XEach checksum is computed by performing the simple arithmetic sum of
- X7-bit values of all characters preceding it in that block. (This
- Xmeans that the STX and ETB/ETX are included in the sum). The checksum
- Xis then the least significant 12 bits of this resulting sum.
- X
- XThe checksum is transmitted as three printable ASCII characters having
- Xvalues between HEX 30 and HEX 3F (the characters 0123456789:;<=>?).
- XThe most significant four bits of the sum are encoded as the 4 LSB of
- Xthe third chacter. See example.
- X
- XA normal paging system will have two fields only:
- X
- XField 1 = Pager ID
- X(normally up to eight digits. May include function and check digit).
- X
- XField 2 = Message
- XField 1 or field 2 may be empty. For example, when a page is tone only,
- Xfield 2 will be empty. Even when empty, a field is followed by a CR.
- XSome systems will reject transactions which have an empty field 2 for
- Xa display page or transactions which have an empty field 1. Other
- Xsystems are less restrictive.
- X
- X(9A) The response to each block is one of four:
- XMessage sequence CR ACK CR = OK, send next block.
- XMessage sequence CR NAK CR = Checksum or transmission error, send last
- X block again.
- XMessage sequence CR RS CR = Abandon current transaction and go to next.
- X RS may occur when the checksum is OK,
- X but the current transaction violates a
- X system rule. At the option of the
- X system, it may occure in other cases.
- XMessage sequence CR ESC EOT CR = Begin disconnect.
- X
- XAny of the responses may have an optional message sequence before
- Xthem, although the system designer should understand the consequences
- Xto the user with all planned entry devices.
- X
- XIt is expected that many systems will save their message sequence
- Xresponses until immediately before disconnect. For some entry
- Xdevices, it may also be desirable that message describing non-checksum
- Xerror assocaiated with a particular transaction in a PG service will
- Xbegin with the letter ID followed by the contents of field 1 for that
- Xtransaction.
- X
- X(10A) Remote sends EOT CR
- XAfter reception of an ACK or RS for the last transaction in a given
- Xservice, the entry device sends EOT CR meaning there are no more
- Xtransactions remaining in this service.
- X
- X(11A) Optional: Central sends a message sequence CR.
- XAlthough optional, it is highly desirable.
- X
- X(12A) Central sends RS CR
- XAn RS CR should be sent at this point if the paging central finds any
- Xdata ACK in previous steps by the system to be unacceptable because of
- Xcontent (e.g. invalid pager number or a message field inappropriate
- Xfor the type of pages, etc.)
- X
- X(13A) Central sends ESC EOT CR
- Xthen hangs up.
- X
- X(14A) stop.
- X
- X(2M) Remote sends M CR (capitol M then CR)
- XLack of ESC at beginning of response to ID signifies manual operation
- Xwhere applicable. From here on it's undefined, but most systems ask
- X(in English) for the info they need and then return you to step (1).
- X
- XNOTES:
- XBell 103 300bps modem is standard, others may be used. Protocol is
- XASCII with XON, XOFF either direction using 10-bit code (1 start, 7
- Xdata, 1 parity, 1 stop) with even parity.
- X
- XIn the case of delays, the paging central shall wait at least four
- Xseconds (eight seconds for (1) (2) (2A)) before disconnecting the
- Xremote entry device. The remote entry device shall wait at least 10
- Xseconds for a character from the central before hanging up.
- X
- XPaging central may, at its option, send CR LF in place of CR at the
- Xend of any sequence.
- X
- XFor inital use of protocol, the paging central shall be equipped to
- Xreceive full duplex using a Bell 103 compatible modem at 300 baud.
- XOptionally, certain inputs may be capable of receiving 110 baud Bell
- X103 full duplex, 300/1200 baud Bell 212 full duplex. No echo shall be
- Xemployed in full duplex mode. Any attempts at automatic baud rate
- Xdetermination shall be within the restraints of the specified
- Xprotocol.
- X
- X
- XSAMPLE Checksum Example:
- X
- XSTX 000 0010
- X1 011 0001
- X2 011 0010
- X3 011 0011
- XCR 000 1101
- XA 100 0001
- XB 100 0010
- XC 100 0011
- XCR 000 1101
- XETX 000 0011
- X 10111 1011
- X 1 7 ;
- X
- XSo, the checksum is "17;"
- X
- XThere is also a really nice flowchart, which I can't reproduce here
- Xwithout much too much trouble. ...and you wouldn't have known it
- Xexisted if I hadn't told you. :-)
- END_OF_FILE
- if test 8737 -ne `wc -c <'ixo.doc'`; then
- echo shar: \"'ixo.doc'\" unpacked with wrong size!
- fi
- # end of 'ixo.doc'
- 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'\" \(12894 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 2.0 -- See file HISTORY for details.
- X** $Id: ixocico.c,v 1.4 1992/09/22 17:31:01 root Exp $
- X
- X$Log: ixocico.c,v $
- X * Revision 1.4 1992/09/22 17:31:01 root
- X * set serial parameters to 300bps, 7e1
- X *
- X * Revision 1.3 1992/09/21 19:41:31 root
- X * made a small change to a comment
- X *
- X * Revision 1.2 1992/09/21 19:40:22 root
- X * changed parity to ODD
- X *
- X * Revision 1.1 1992/09/21 19:38:02 root
- X * Initial revision
- X *
- X
- 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 small 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
- Xthe grabmodem() routine. */
- X
- X/* END OF USER CONFIGURABLE OPTIONS */
- X/****************************************************************/
- X
- X#include <stdio.h>
- X#include <fcntl.h>
- X#include <string.h>
- X/* #include <strings.h> */
- X#include <ctype.h>
- X#include <errno.h>
- X#include <sys/termios.h>
- X
- X#ifdef REAL_OS
- X#include <sys/time.h> /* required for <sys/resource.h> */
- X#include <sys/resource.h> /* 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
- Xint modem = 0;
- Xchar *lockname = NULL;
- X
- X/* print a string without worrying about unprintable charactors */
- Xvoid
- Xsafeprint(str)
- Xchar *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 */
- Xchar *checksum(pk)
- Xchar *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
- Xprintf("CHECKSUM=:"); safeprint(check); printf(":\n");
- X return check;
- X}
- X
- X/* open the modem. You should have done a lockmodem() first */
- Xint grabmodem(dev)
- Xchar *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;
- X ti.c_cflag |= B300; /* baud=300 */
- X /* ti.c_cflag |= B1200; /* baud=1200 */
- X
- X ti.c_cflag &= ~CSIZE; /* 7-bit bytes */
- X ti.c_cflag |= CS7;
- X/* ti.c_cflag |= CS8; */
- X
- X ti.c_cflag &= ~CSTOPB; /* one stop bit */
- X/* ti.c_cflag |= CSTOPB; /* two stop bit */
- X
- X ti.c_cflag |= PARENB; /* parity */
- X ti.c_cflag &= ~PARODD; /* even parity */
- X/* ti.c_cflag |= PARODD; /* odd parity */
- X
- 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 */
- Xvoid send(fd, str)
- Xint fd;
- Xchar *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 retries permitted ) */
- Xint match(mod, str, err)
- X int mod;
- X char *str;
- X int err;
- X{
- X int len;
- X char c;
- X
- Xprintf("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) {
- X str++;
- X }
- X if (!(*str)) break; /* matched all? Exit loop */
- X }
- X if (!err--) {
- X printf("NOT MATCHED\n");
- X return 1;
- X }
- X }
- Xprintf("MATCHED\n");
- X return 0;
- X}
- X
- X/* hang up the modem */
- Xvoid
- Xhangup_modem(fd)
- Xint 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 */
- Xvoid unlockmodem(name)
- Xchar *name;
- X{
- X printf("Unlocking modem.\n");
- X (void)unlink(name);
- X return;
- X}
- X
- X/* clean up and leave this program */
- Xvoid 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*/
- Xchar *lockmodem(dev)
- Xchar *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
- Xprintf("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 */
- Xchar *getline(line)
- Xchar *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
- Xdisplay it. If you get an invalid pack DIE */
- X
- Xvoid getpacket(fd, str)
- Xint fd;
- Xchar *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 return; /* ddj 4/30 */
- 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
- Xint main(argc, argv)
- Xint argc;
- Xchar *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", 6)) {
- 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
- Xprintf("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", 30)) {
- X printf("#NOCONN no connect\n");
- X bail_out();
- X }
- X
- Xprintf("Waiting for ID=\n");
- X
- X /* send a CR ever second until "ID=" comes back */
- X failcnt = 10;
- X /* CRs needed by PacTel DJ */
- X send(modem, "\r");
- X /* end of DJ */
- X while (failcnt--) {
- X if (match(modem, "ID=", 4)) send(modem, "\r");
- X else break;
- X }
- X if (failcnt==-1) bail_out();
- X
- Xprintf("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
- Xprintf("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 12894 -ne `wc -c <'ixocico.c'`; then
- echo shar: \"'ixocico.c'\" unpacked with wrong size!
- fi
- # end of 'ixocico.c'
- 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'\" \(10116 characters\)
- sed "s/^X//" >'tpage.pl' <<'END_OF_FILE'
- X#! /usr/local/bin/perl4.035
- 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# $Header: /home/tal/work/beep2/RCS/tpage.pl,v 1.2 1992/09/21 20:11:51 root Exp $
- X
- X# Version 2.0 -- See file HISTORY for details.
- X
- X# $Log: tpage.pl,v $
- X# Revision 1.2 1992/09/21 20:11:51 root
- X# new tr's to remove high bits
- X#
- X# Revision 1.2 1992/09/21 20:11:51 root
- X# new tr's to remove high bits
- X#
- X# Revision 1.1 1992/09/21 20:09:37 root
- X# Initial revision
- 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 = 110;
- 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
- Xrequire("getopts.pl");
- X
- Xsub strip_string {
- X local($s) = @_;
- Xprint "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
- Xprint "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
- Xprint "\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
- Xdo 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);
- Xprint "opt_message = :$opt_message:\n" if $debug;
- X$opt_message = &strip_string( $opt_message ) if $opt_message;
- Xprint "opt_message = :$opt_message:\n" if $debug;
- Xdie "$0: No message. Cat got your tongue?" if ( $opt_message eq "" );
- X
- Xdie "$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;
- Xif ($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
- Xif ($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 - 1);
- X
- Xif ($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));
- Xdie "$0: Schedule file $opt_S can't be read/found"
- X unless ( ($opt_wholist eq '-') || (-r $opt_S && -r $opt_T) );
- Xdie "$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
- Xif ($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
- Xprint "L = $l\n" if $debug;
- Xprint "H = $h\n" if $debug;
- Xprint "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 (<SCHED>) {
- 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 (<SCHED>) {
- 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
- Xdie "$0: The schedule didn't specify anyone to call!"
- X unless ($opt_wholist) || ($opt_d && $opt_p);
- Xdie "$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;
- Xforeach $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 (<TABL>) {
- 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 local($um) = umask 2;
- X open(QU, ">$qname" ) || die "Can't open $qname for writing: $!";
- X umask $um;
- 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
- Xprint "\n";
- END_OF_FILE
- if test 10116 -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'\" \(12085 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 2.0 -- See file HISTORY for details.
- X
- X####################################################################
- X#
- X# Parameters that the user can set:
- X#
- X####################################################################
- X
- X$debug = 0;
- X# $| = 1; open( STDOUT, ">/home/adm/lib/tpage/log.txt" ) if $debug; $| = 1;
- X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/'; # same as in tpage.pl
- X#$IXOCICO = '/home/tal/work/beep2/ixocico'; # where is ixocico?
- X$IXOCICO = '/home/adm/lib/tpage/ixocico'; # where is ixocico?
- X$MAIL = '/usr/ucb/mail'; # which mail to use?
- X # Recommended mailers: SunOS & BSD's: /usr/ucb/mail, AT&T Unix's xmail
- X # Not recommended mailers: /bin/mail
- 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 of the queue
- X$SLEEP_TIME = 150; # 2.5 minutes
- X$SLEEP_TIME = 10 if $debug; # smaller when I'm debugging
- X# Small amount of time to wait between finding anything in the queue
- X# and doing a real scan of the queue.
- X$MULT_SLEEP_TIME = 10;
- 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
- Xlocal(%protocols);
- X
- Xwhile (1) {
- X local ($first, @allfiles, @anyfiles);
- 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
- X # wait for any files to appear.
- X while (1) {
- X @anyfiles = &scan_queue;
- X print "DEBUG: anyfiles= ", join(' ', @anyfiles), "\n" if $debug;
- X
- X if ($#anyfiles!=-1) { # files? take a rest and then process.
- X sleep $MULT_SLEEP_TIME unless $debug;
- X last;
- X } else { # no files? hibernate.
- X sleep $SLEEP_TIME;
- X next;
- X }
- X }
- X
- X # re-get the files in the queue
- X @allfiles = &scan_queue;
- X print "DEBUG: allfiles= ", join(' ', @allfiles), "\n" if $debug;
- X
- X # get all the data out of the queue'd files.
- 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 = <DATA> );
- Xprint "DEBUG: first=$first\n" if $debug;
- X eval "do read_format_$first()";
- X }
- X
- X # process all the extracted data (do_protocol_* should delete the files)
- X foreach $proto (keys %protocols) {
- X eval "do do_protocol_$proto()";
- X delete $protocols{ $proto };
- X sleep $SLEEP_TIME;
- X }
- X}
- X
- X# scan the queue for entries (avoid "blacklisted" files)
- Xsub scan_queue {
- X local(@files);
- X # scan the directory for "P files (pager files)
- X opendir(QDIR, $QUEUE_DIR) || die "$0: Can't open $QUEUE_DIR: $!";
- X @files = grep( /^P/, readdir(QDIR) );
- X closedir(QDIR);
- X print "DEBUG: filescan= ", join(' ', @files), "\n" if $debug;
- X # remove the blacklisted files
- X @files = grep( ! defined $blacklist_data{ $_ }, @files);
- X print "DEBUG: goodfiles= ", join(' ', @files), "\n" if $debug;
- X # return the files
- X @files;
- X}
- X
- X# blacklist a file in the queue (couldn't delete it for some reason
- X# and we don't want to repeat it)
- Xsub blacklist {
- X local($file) = @_;
- X $blacklist_data{ $file } = 1;
- X}
- X
- X# Each read_format_ must:
- X# read from <DATA> and then close(DATA).
- X# %protocols{ 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
- Xsub read_format_A
- X{
- X local($dial,$pin,$error,$mess,$X); # $file is by sideeffect
- X print "DEBUG: reading format A\n" if $debug;
- X chop( $dial = <DATA> );
- X chop( $pin = <DATA> );
- X chop( $error = <DATA> );
- X chop( $mess = <DATA> );
- X chop( $X = <DATA> );
- 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 $protocols{ 'ixo' } = 1;
- X &ixo_mesg_append( $dial, $pin, $error, $mess, $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
- Xsub do_protocol_ixo {
- X print "DEBUG: doing protocol IXO\n" if $debug;
- X local($pin, $error, $mess, $file, $cmd, $status, $index);
- X local($general_reject, $general_error_message);
- X # build the temp file and the command line
- X local($tmpfile) = "/tmp/tpaged.$$";
- X foreach $dial ( &ixo_listphones ) {
- X print "DEBUG: Number to dial is $dial\n" if $debug;
- X
- X # fill the data file
- X open(IX, ">$tmpfile" ) || die "$0: Can't create $tmpfile: $!";
- X foreach $index ( &ixo_listindexes( $dial ) ) {
- X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
- X # put it in the file for ixocico to use
- X print IX "$pin\n$mess\n";
- X }
- X close IX;
- X
- X print "DEBUG: messages to send", &ixo_listindexes( $dial ), "\n" if $debug;
- X
- X $general_reject = 1; # when done, 1=cancel remaining; 0=retry remaining
- X $general_error_message = "SHOULD NOT HAPPEN"; # if all messages are cancelled
- 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
- X while (<IX>) {
- 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 $general_reject = 0;
- X last;
- X };
- X /^#UNKNOWNPROTO / && do {
- X print "$0: Uhhh, are you sure that's a pager service?\n" if $debug;
- X $general_reject = 1;
- X $general_error_message = "other end using different protocol";
- X last;
- X };
- X /^\#MESOK (\d) / && do {
- X $index = $1;
- X print "DEBUG: message $index done.\n" if $debug;
- X
- X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
- X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug;
- X
- X print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug;
- X $status = unlink $QUEUE_DIR . $file;
- X print "DEBUG: unlink status=$status; $!\n" if $debug;
- X &blacklist( $file) unless $status;
- X
- X # remove from queue
- X &ixo_mesg_delete( $dial, $index );
- X };
- X /^#MESREJECT (\d) / && do { # very similar to #MESOK
- X $index = $1;
- X print "DEBUG: message $index rejected.\n" if $debug;
- X
- X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
- X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug;
- X
- X # notify anyone that wants to know about failures
- X if ($error + 0) {
- X $cmd = "$MAIL <"
- X . $QUEUE_DIR . $file
- X . " -s 'TPAGE_MESSAGE: request rejected by service' "
- X . $error;
- X print "DEBUG: About to execute $cmd\n" if $debug;
- X system $cmd;
- X }
- X
- X print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug;
- X $status = unlink $QUEUE_DIR . $file;
- X print "DEBUG: unlink status=$status; $!\n" if $debug;
- X &blacklist( $file) unless $status;
- X
- X # remove from queue
- X &ixo_mesg_delete( $dial, $index );
- X };
- X /^#FORDIS / && do {
- X print "Forced disconnect from server.\n" if $debug;
- X $general_reject = 1;
- X $general_error_message = "other end requesting disconnect";
- X last;
- X };
- X /^#PROTERR / && do {
- X print "Server not following protocol.\n" if $debug;
- X $general_reject = 1;
- X $general_error_message = "other end having a protocol error";
- X last;
- X };
- X ( /^#DONE / || /#BYE / ) && do {
- X print "Done with sending batch. Waiting BYE.\n" if $debug;
- X $general_reject = 0;
- X $general_error_message = "been told we're done but weren't".
- X next;
- 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 $general_reject = 0;
- 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 $general_reject = 0;
- X last;
- X };
- X }
- X close IX;
- X unlink $tmpfile;
- X
- X print "DEBUG: rejecting remaining messages\n" if $debug;
- X # now reject remaining messages
- X foreach $index ( &ixo_listindexes( $dial) ) {
- X # if general_reject then we have work to do
- X if ($general_reject) {
- X print "DEBUG: removing $dial:$index\n" if $debug;
- X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
- X ###### mail a warning
- X if ($error + 0) {
- X $cmd = "$MAIL <"
- X . $QUEUE_DIR . $file
- X . " -s 'TPAGE_MESSAGE: unprocessed message deleted due to "
- X . $general_error_message . "' "
- X . $error;
- X print "DEBUG: About to execute $cmd\n" if $debug;
- X system $cmd;
- X }
- X ###### make sure it gets deleted
- X print "DEBUG: unlinking (leftover) " . $QUEUE_DIR . $file . "\n" if $debug;
- X $status = unlink $QUEUE_DIR . $file;
- X print "DEBUG: unlink status=$status; $!\n" if $debug;
- X &blacklist( $file) unless $status;
- X }
- X print "DEBUG: deleting from memory $dial:$index\n" if $debug;
- X # delete it from the ixo list
- X &ixo_mesg_delete( $dial, $index );
- X }
- X # at this point %ixo_data should be empty
- X &ixo_end_asserts;
- X
- X
- X # now do the next phone number
- X }
- X}
- X
- Xsub ixo_end_asserts {
- X # test a couple assertions
- X print "DEBUG: testing assertions\n" if $debug;
- X
- X # $ixo_count{ $dial } should be zero
- X die "$0: bug1\n" if $ixo_count{ $dial };
- X
- X # %ixo_data should be empty at this point
- X die "$0: bug2\n" if grep(1,keys %ixo_data); # fast key counter
- X}
- X
- Xsub ixo_mesg_append {
- X local($dial, $pin, $error, $mess, $file, $count) = @_;
- X print "APPEND: dial=$dial pin=$pin error=$error file=$file mess=$mess\n" if $debug;
- X $count = 0 + $ixo_count{ $dial }++;
- X $ixo_data{ "$dial:$count" } = "$pin\n$error\n$mess\n$file";
- X print "APPEND: data=", $ixo_data{ "$dial:$count" }, "\n" if $debug;
- X}
- X
- Xsub ixo_mesg_get {
- X local($dial, $index) = @_;
- X local($pin, $error, $mess, $file, @list);
- X print "GET: dial=$dial index=$index\n" if $debug;
- X @list = split( '\n', $ixo_data{ "$dial:$index" } );
- X ($pin, $error, $mess, $file) = @list;
- X print "GET: pin=$pin error=$error file=$file mess=$mess\n" if $debug;
- X @list;
- X}
- X
- Xsub ixo_mesg_delete {
- X local($dial, $index) = @_;
- X print "DELETE: dial=$dial, index=$index\n" if $debug;
- X delete $ixo_data{ "$dial:$index" };
- X $ixo_count{ $dial }--;
- X}
- X
- Xsub ixo_listindexes {
- X local($dial) = @_;
- X
- X # gather and sort the second field
- X sort grep( s/^$dial:(.+)/$1/, keys %ixo_data );
- X}
- X
- Xsub ixo_listphones {
- X local(@list);
- X local($l) = undef;
- X
- X # gather and sort the first field.
- X @list = sort grep( s/^(.+):.+$/$1/, keys %ixo_data );
- X # uniq them
- X @list = grep (!($_ eq $l || ($l = $_, 0)), @list );
- X # return them
- X @list;
- X}
- END_OF_FILE
- if test 12085 -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 archive 1 \(of 2\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 2 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked both archives.
- rm -f ark[1-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-