[comp.sources.misc] version of uupoll for system V

news@csd_v.UUCP (NEWS NET) (12/17/87)

The following shar file implements a remote site polling mechanism
for USG systems which lack one.

-----cut-----cut-----cut-----cut-----cut-----cut-----cut-----cut-----cut-----cut
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#
#	 1) README
#	 2) uupoll.c
#
# This archive created: Sun Dec 13 02:46:46 1987

export PATH; PATH=/bin:/usr/bin:$PATH

#------------------------------------------------------------------------------#
 
if test -f 'README'
then
	echo "shar: will not over-write existing file 'README'"
else
cat << SHAR_EOF > 'README'
Uupoll is a polling mechanism for sysV unix systems which
are running non-HONEYDANBER uucp.  This version of uucp
lacks such a mechanism.  No attempt has been made to 
duplicate the functionality of either HONEYDANBER or
BSD polling mechanisms since the author does not have
even the slightest idea of what those mechanisms might
be.

The version of uupoll included here has the following
features:

1.  \$ uupoll sysname

creates a file in the spool directory named P.sysname.  No check is made to
see if sysname is included in L.sys.  The poll file at present consists of
a single record

D nn

where nn is the expiration number described in the -d option below.
The following options are valid in this form of the command.  

	-p path 	Specifies the path of an alternate spool directory.  The
				default directory is /usr/spool/uucp.
	-d n		Specifies the number of invocations of "uupoll -c"
				required to expire the poll.  The default is 3 in the
				absence of this switch.

2.  \$ uupoll -[r|l] sysname

removes or lists to standard output the poll files associated with
the named systems.  The special system name "all" is valid with
this form of the command.

3.  \$ uupoll -c

decrements the value n in the poll file and when n reaches 0, deletes
the poll file.  Sends mail to the owner stating that the poll has
expired before a successful connection was established.  This command 
is intended to be run daily.

4.  \$ uupoll

invokes uucico as a child process  for each poll file in the spool directory.
At the completion of the conversation (if any) uustat is invoked to see
if the conversation succeeded.  If so, the poll file is removed from the
spool directory.  The following switches are valid

	-p path		Specifies the path of an alternate spool directory.
	-x          Causes -x9 debugging output from uucico.

If for any reason the conversation was not successful the poll file
is left intact and the poll is retried at the next invocation of the
uupoll command.

EXAMPLE:

The following is an example of the intended usage of the command.
The polling of systems aaa and bbb by user joe at 4 a.m. daily and
of system ccc by fred at 8 and 11 p.m. is accomplished.

# in joe's crontab (have poll in place before the hour):
55 3 * * * uupoll aaa > /dev/null
55 3 * * * uupoll bbb > /dev/null

# in fred's crontab (have poll in place before the hour):
55 19,22 * * * uupoll bbb > /dev/null

# in uucp's crontab (service all polls hourly):
0 * * * * uupoll > /dev/null

# in root's crontab (expire polls daily):
0 2 * * * uupoll -c > /dev/null

To build uupoll the standard compilation command is sufficient:

	\$ cc -O -o uupoll uupoll.c

I would of course appreciate any feedback, suggestions, bug reports, etc.

=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
|| Bruce Kern                                      | uunet!swlabs!csd_v!bak ||
|| Computer Systems Design                         | 1-203-270-0399         ||
|| 29 High Rock Rd., Sandy Hook, Ct. 06482         | This space for rent.   ||
=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
SHAR_EOF
fi


#------------------------------------------------------------------------------#
 
if test -f 'uupoll.c'
then
	echo "shar: will not over-write existing file 'uupoll.c'"
else
cat << SHAR_EOF > 'uupoll.c'
/*

NAME
    uupoll -- poll a remote system for inbound traffic

SYNOPSIS
    uupoll [-p spool_path] [-x]                                      
    uupoll -c                                                        
    uupoll [-d n] [-r|l] [-p spool_path] sysname1 [sysname2 ...]

       ?              --  print this message,                           
       -c             --  clean up expired poll files                   
       -d n           --  expire poll file in n days (default is 7),    
       -r             --  remove poll files for named systems           
       -l             --  list poll files for named systems             
       -p spool_path  --  make spool_dir alternate polling path         
       -x             --  cause uucico to produce debug output (-x9)    

DESCRIPTION
    Uupoll sysname creates a file P.sysname in the spool directory named
    in -p spool_path ( default is /usr/spool/uucp ).

    The contents of the poll file are simpy: D nn.  Here nn is the number
    of invocation of uupoll -c across which the poll file will survive.
    at present the default for nn is 03.  At each call of uupoll -c, all
    poll files have the number nn decremented by one.  When 0 is reached
    the poll file is deleted.

    Uupoll with no arguments searches the spool directory ( -p spool_path )
    for files of the form P.sysname and the initiates a poll to those files
    via uucico.  The -x switch causes uucico debug -x9  output to go to
    standard out. The connection is monitored via the uustat command and
    if CONVERSATION COMPLETE is returned, the P.sysname file is deleted
    from the spool directory.

    The -r option removes the spoll file and the -l option lists the
    poll files.  Each can take the argument 'all' instead of 'sysname'.
    No wild cards are permitted however.


FILES
    P.sysname

SEE ALSO
    uucico(1)

BUGS
    The number in the poll file which represents expiration is
    not really in units of days, but rather in units of invocations
    of "uupoll -c".  If however, this is executed once per day then the
    number maps to days. 

EXAMPLE
    The following crontab entries will cause system aaa to be
    polled twice a day at 8 a.m. and 5 p.m. and system bbb to 
    be polled at 10 a.m. and 3 p.m.  The system specific entries
    can appear in any user's crontab, but the polling entry (i.e.
    the hourly poller wiht no arguments should be in the uucp crontab.
    Also the time should not confict with the regularly scheduled uucp
    demon such as uudemon.hr.  The cleanup uupoll -c should be in the
    root crontab since it must have permission to delete the expired
    poll files of any user.
#
#   Have poll for system aaa on hand at 8 and 5 and
#   for bbb at 10 and 3.
#
55 7,16 * * 0-6 /usr/bin/uupoll aaa > /dev/null
55 9,14 * * 0-6 /usr/bin/uupoll bbb > /dev/null
#
#   Cause polls to be issued on the hour for all systems with outstanding
#   poll files.
#
00 * * * 0-6 /usr/bin/uupoll > /dev/null
#
#   Decrement polls each day for expiration purposes.
#
0 10 * * * /usr/bin/uupoll -c > /dev/null

*/


#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/dir.h>

#define TRUE           1==1
#define FALSE          !TRUE
#define CHILD          case 0
#define PARENT         default
#define RW_R__R__      0133

char *usage = "                                                         \\n\\
Usage: uupoll ?                                                         \\n\\
       uupoll -c                                                        \\n\\
       uupoll [-d n] [-r|l] sysname                                     \\n\\
       uupoll [-p spool_path] [-x]                                      \\n\\
                                                                        \\n\\
       ?              --  print this message,                           \\n\\
       -c             --  clean up expired poll files                   \\n\\
       -d n           --  expire poll file in n days (default is 3),    \\n\\
       -r             --  remove poll files for named systems           \\n\\
       -l             --  list poll files for named systems             \\n\\
       -p spool_path  --  make spool_dir alternate polling path         \\n\\
       -x             --  cause uucico to produce debug output (-x9)    \\n\\
";

char
    *getpoll(),         /* returns consecutive poll file names from spooldir */
    *cuserid();         /* returns the login name of process owner */

FILE
    *fopen(),
    *mk_poll();         /* create a poll file P.sysname */

long
    fseek();

int
    is_poll();          /* determines whether or not poll is outstanding */

void
    uprint(),           /* output system names for poll files */
    uunlink(),          /* unlink poll files */
    clean_up(),         /* removes expired polls from spool dir */
    poll();             /* polls the named file for uucp traffic  */

unsigned int 
    days = 3;           /* number of days in which poll expires */
    clean = FALSE,      /* clean up expired polls */
    remove = FALSE,     /* Cancel outstanding polls */
    udbg = FALSE,       /* set debug flag (-x9) in uucico call */
    list = FALSE;       /* list outstanding polls   */

char 
    spoolpath[30] = "/usr/spool/uucp";  /* directory to place poll files */

main( argc, argv )
int argc; char **argv;
    {
    extern int optind;
    extern char *optarg;
    int opt, getopt(), i;
    unsigned int to_poll=TRUE;

    /*
    **      Is help being requested??
    */
    if( argv[1][0] == '?' )
        {
        fprintf( stderr, usage );
        exit(0);
        }

    /*
    **      Examine the command line options.
    */
    while( ( opt = getopt( argc, argv, "cxd:lp:r" )  ) != EOF )
        {
        switch ( opt )
            {
            case 'c':
            if( list || remove )
                {
                fprintf( stderr,
                         "uupoll: inconsistant switch specification\\n" );
                goto default_case;
                }

            clean = TRUE;
            to_poll = FALSE;
            break;

            case 'd':
            if( sscanf( optarg, "%d", &days ) == 0 ) goto default_case;
            break;

            case 'l':               /* list outstanding polls */
            if( clean || remove )
                {
                fprintf( stderr,
                         "uupoll: inconsistant switch specification\\n" );
                goto default_case;
                }

            list = TRUE;
            to_poll = FALSE;
            break;

            case 'p':               /* change the spool file path */
            strcpy( spoolpath, optarg );
#           ifdef DEBUG
            fprintf( stderr, "uupoll: spool directory is %s\\n",  spoolpath );
#           endif
            break;

            case 'r':               /* remove outstanding polls */
            if( clean || list )
                {
                fprintf( stderr,
                         "uupoll: inconsistant switch specification\\n" );
                goto default_case;
                }

            remove = TRUE;
            to_poll = FALSE;
            break;

            case 'x':
            udbg = TRUE;
            break;

            default_case:
            default:                /* invalid option */
            fprintf( stderr, usage );
            exit( 2 );
            break;
            }
        }
    chdir( spoolpath );


    /*
    **      If clean then check all poll files for expiration
    **      and exit.
    **
    **      If this is a request to poll
    **      do so and exit.
    */
    if( clean )
        {
        clean_up();
        exit( 0 );
        }

    if( optind == argc )        /* then this is a poll request */
        {
        poll();
        exit( 0 );
        }
        

    /*
    **      For each system name on the command line
    **      create a poll file name P.sysname.
    **
    **      If list is requested then output the system names of
    **      all systems with poll files.
    **
    **      If remove then delete poll files for named systems.
    **
    **      Otherwise create poll files for all systems which do
    **      not have them.
    */
    for( i=optind; i<argc; ++i )
        {
        char p_sysname[15];
        unsigned isp;

        sprintf( p_sysname, "P.%s", argv[i] );
        isp = is_poll( p_sysname );

        if( list && isp )   uprint( argv[i] );

        if( remove )
            {
            if( isp )   uunlink( p_sysname );
            else        fprintf( stderr,
                            "uupoll: no poll outstanding for %s\\n", argv[1] );
            }

        if( to_poll && !isp ) mk_poll( argv[i], p_sysname );
        }
}


int is_poll( p_sysname )        /* check if poll file exists for p_sysname */
char *p_sysname;                /* return true if p_name is "all" */
    {
    int rc;

    rc = FALSE;
    if( strcmp( p_sysname, "P.all" ) == 0 ) rc = TRUE;
    if( access( p_sysname, 0 ) == 0 )       rc = TRUE;

#   ifdef DEBUG
    fprintf( stderr, "uupoll: poll file %s%s found\\n", p_sysname, 
        ( rc ? "" : " not" ) );
#   endif

    return rc;
    }


FILE *mk_poll( sysname, p_sysname )     /* create a poll file P.sysname */
char *sysname, *p_sysname;
    {
    FILE *fp;
    unsigned int omask;

    omask = umask( RW_R__R__ );

    if( ( fp = fopen( p_sysname, "w" ) ) == NULL )
        {
        fprintf( stderr, "uupoll: cannot create poll for %s\\n", sysname );
        return NULL;
        }

    umask( omask );

    fprintf( fp, "D %02d\\n", days );
    fclose( fp );
    return fp;
    }


void poll()         /* poll all systems via uucico which have */
    {               /* outstanding poll files in the spool directory */
    int fd;
    char *fname;


/*
**      For each poll file in the spool directory, fork off a uucico
**      process to poll the system.  When the uucico is finished, 
**      unlink the poll file if the poll succeded.  If the poll failed
**      leave the poll file in place for the next invocation of uupoll.
*/
    while( ( fname = getpoll() ) != NULL )
        {
        int pid;

        pid = fork();

        switch( pid )
            {
            char psys[20];
            int wstat;

            PARENT:
                while( wait( &wstat ) != pid )
#               ifdef DEBUG
                    fprintf( stderr, "returned from wait status: %x\\n", wstat);
                fprintf( stderr, "returned from wait status: %x\\n", wstat);
#               endif
                ;
                break;

            CHILD:
                sprintf( psys, "-s%s", &fname[2] );

                if( udbg ) execl( "/usr/lib/uucp/uucico",
                                  "/usr/lib/uucp/uucico",
                                  "-r1", psys, "-x9", (char *) 0 );

                else       execl( "/usr/lib/uucp/uucico",
                                  "/usr/lib/uucp/uucico",
                                  "-r1", psys, (char *) 0 );

                exit(1);    /* NOT REACHED */
            }

        if( poll_successful( &fname[2] ) )
            {
#           ifdef DEBUG
            fprintf( stderr, "uupoll: system %s successfully polled\\n",
                    &fname[2] );
#           endif
            unlink( fname );
            }
        }
    }


int poll_successful( sysname )
char *sysname;
    {
    char ustat1[20],ustat2[20], poll_cmd[40];
    FILE *fpipe;
    int rc;

    /*
    **      Set the default return value to FALSE.
    */
    int result = FALSE;

    /*
    **      Prepare uustat command and
    **      Open a read only pipe to uustat.
    */
    sprintf( poll_cmd, "uustat -m%s\\n", sysname );
    fpipe = popen( poll_cmd, "r" );
    if( fpipe == NULL )
        {
        perror( "uupoll: poll_successful" );
        exit( 1 );
        }

    /*
    **      Read the pipe and pick out the completion string from the
    **      uustat output.
    */
    while( ( rc = fscanf( fpipe, "%*s%*s%s%s", ustat1, ustat2 ) ) != EOF )
        {
#       ifdef DEBUG
        fprintf( stderr, "uupoll: poll_successful: %s -- %s %s\\n",
            sysname, ustat1, ustat2 );
#       endif

        /*
        **      If the "CONVERSATION SUCCEDED" string is recognized
        **      return TRUE otherwise FALSE will be returned by default.
        */
        if( strcmp( ustat1, "CONVERSATION" ) == 0 &&
            strcmp( ustat2, "SUCCEEDED" ) == 0 )     result = TRUE;
        }

#   ifdef DEBUG
    fprintf( stderr, "uupoll: poll_successful: returning -- %d\\n", result );
#   endif

    fclose( fpipe );
    return result;
    }


void clean_up()
    {
    char *fname;

    /*
    **  For each poll file in the spool directory find the
    **  day record (e.g. D nn).  If nn is 0 the poll is
    **  perpetual go to the next file.  If nn is 1 the poll
    **  has expired.  Delete and mail the user notification
    **  that the file has been discarded. the file.  Otherwise 
    **  decrement nn and write the record back to the file.
    */
    while( ( fname = getpoll() ) != NULL )
        {
        FILE *fp;
        long fpos;
        int rc;
        unsigned int days;

        if( ( fp = fopen( fname, "r+" ) ) == NULL )
            {
            fprintf( stderr, "uupoll: clean_up: cannot access %s\\n", fname );
            fclose( fp );
            continue;
            }

        do fpos = fseek( fp, 0L, 1 );
        while( ( rc = fscanf( fp, "D %02d\\n", &days ) ) != 1 && rc != EOF );

        if( rc == EOF ) 
            {
            fprintf( stderr,
                "uupoll: clean_up: cannot find days in %s\\n", fname );
            fclose( fp );
            continue;
            }

        switch( days )
            {
            char mail_cmd[80];
            char line[80];

            case 0:
            fclose( fp );
            break;

            case 1:
            fclose( fp );
            unlink( fname );
            sprintf( mail_cmd, 
                "echo Poll file %s expired -- deleted by uupoll | mail %s\\n",
                fname, cuserid( NULL ) );
            system( mail_cmd );
            break;

            default:
            rewind( fp );
            fseek( fp, fpos, 0 );
            sprintf( line, "D %02d\\n", --days );
            fputs( line, fp );
            fseek( fp, 0L, 2 );
            rewind( fp );
            fclose( fp );
            break;
            }
        }
    }


char *getpoll()
    {
    static  int fd = -1;
    static  struct directx
                {
                ino_t d_ino;
                char  d_name[ DIRSIZ+1 ];
                }
            dir_ent;

    /*
    **  Open the directory file in not opened on a previous call.
    */
    if( fd == -1 )
        {
        if( ( fd = open( ".", O_RDONLY ) ) == -1 )
            {
            fprintf( stderr, "uupoll: cannot access spool directory\\n" );
            exit( 1 );
            }
        dir_ent.d_name[DIRSIZ] = '\\0';
        }

    /*
    **  Read entries until either a poll file is found (e.g. P.sysname)
    **  or EOF is encountered.
    */
    do if( !read( fd, &dir_ent, sizeof( struct  direct ) ) )
        {
        close( fd );
        return NULL;
        }
    while( dir_ent.d_name[0] != 'P' ||
           dir_ent.d_name[1] != '.' ||
           dir_ent.d_ino == 0 );

    return dir_ent.d_name;
    }


void uunlink( p )           /* Unlink the input poll file name or */
char *p;                    /* unlink "all" poll files */
    {
    char *pf;
    if( strcmp( p, "P.all" ) )
        {
        unlink( p );
        return;
        }
    else    while( ( pf=getpoll() ) != NULL )   unlink( pf );
    }


void uprint( sys )          /* Print the poll system name or the */
char *sys;                  /* names of "all" systems with poll files */
    {
    char *psys;
    if( strcmp( sys, "all" ) )                  printf( "%s\\n", sys );
    else while( ( psys=getpoll() ) != NULL )    printf( "%s\\n", &psys[2] );
    }
SHAR_EOF
fi

#------------------------------------------------------------------------------#
 
exit 0
#	End of shell archive