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