[comp.sources.misc] uucost -- Display uucp costs on screen.

jack@swlabs.UUCP (Jack Bonn) (08/08/87)

This is a set of routines for displaying the costs of all uucp sessions
so far this month and so far today.  It is intended for use with HDB and
runs on System V.  There is nothing fancy here, modification to other
environments should be easy.  The output of uulog is used to determine
the duration of calls to each site; modification would only entail changing
the routine (scan_cost.c) which scans the output of the uulog to handle
other formats.

#! /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:
#	Makefile
#	form_cost.c
#	scan_cost.c
#	costs
#	README
#	uucost
#	uucost.da
#	uucost.mo
# This archive created: Fri Aug  7 13:08:19 1987
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
# makefile for uucost.
#
#
all: form_cost scan_cost

shar:
	shar Makefile form_cost.c scan_cost.c costs README \
	uucost uucost.da uucost.mo >uucost.shar

install:
	chmod u+x uucost uucost.da uucost.mo
	chown uucp *
	>cost.log

lint:
	lint scan_cost.c
	lint form_cost.c

clean:
	>cost.log
	rm scan_cost form_cost
SHAR_EOF
fi
if test -f 'form_cost.c'
then
	echo shar: "will not over-write existing file 'form_cost.c'"
else
cat << \SHAR_EOF > 'form_cost.c'
#include <stdio.h>
#include <string.h>

#define SYSTEMS 100	/* Max number of systems to track */
#define SYS_SIZE 11	/* Number of chars in system name (including null) */
#define BUF_SIZE 80	/* Max chars in line */

FILE *fopen(), *costs_file;

struct costs {
    char system[SYS_SIZE];
    int first;
    int cost;
    int minimum;
    long minutes;
    long tot_cost;
    int used;
} system_costs[SYSTEMS];

struct costs *c_ptr;

#define max(a,b) ((a)>(b)?(a):(b))

int systems;

int lookfor();

int main() {
    int mins;
    int i;
    char sys[SYS_SIZE];
    long this_cost;
    int sc;
    char buffer[BUF_SIZE];
    long total;
    long tot_mins;

    /*
	Read the costs file, one record per system.
    */
    costs_file = fopen("/usr/lib/uucp/uucost/costs","r");
    c_ptr = system_costs;

    while (fgets (buffer, (int)sizeof(buffer), costs_file)) {
	if (buffer[0] != '#') { /* ignore comments */
	    sc = sscanf(buffer, "%s%d%d%d", c_ptr->system, &c_ptr->first, 
		&c_ptr->cost, &c_ptr->minimum);
	    if (sc == 4) {
		c_ptr->minutes = 0L;
		c_ptr->tot_cost = 0L;
		c_ptr->used = 0;
		c_ptr++;
	    } else {
		(void)fprintf (stderr, "Format error in costs file.  ");
		(void)fprintf (stderr, "Input was: %s\n", buffer);
	    }
	}
    }

    systems = c_ptr - system_costs;  /* number of system_costs entries used */

    /*
	For each line of standard input:

	1) look up system in systems_costs structure
	2) add the time to the running count for this system
	3) figure the cost of the session and add it in
    */
    while (scanf("%s%s%d", sys, buffer, &mins) != EOF) {
	if ((i = lookfor(sys)) == -1) {
    	    (void) fprintf (stderr,"System %s not found in costs file.\n", sys);
	} else {
	    c_ptr = &system_costs[i];
	    c_ptr->used = 1;
	    c_ptr->minutes += (long)mins;
	    this_cost = (long)c_ptr->first;
	    this_cost += (long)c_ptr->cost * (long)(mins-1);
	    this_cost = max(this_cost, (long)c_ptr->minimum);
	    c_ptr->tot_cost += this_cost;
#ifdef DEBUG
	    (void) printf ("%-10s %-30s %3d mins = $%3ld.%.2ld\n", 
		sys, buffer, mins, this_cost/100L, this_cost%100L);
#endif /* DEBUG */
	}
    }

    /*
	For each system that had activity:
	1) print the name and minutes and cost
	2) keep total of minutes used
	3) keep total of cents used
    */
    total = 0L;
    tot_mins = 0L;
    (void) printf ("\nSystem      Mins       Cost\n");
    for (c_ptr = system_costs; c_ptr < &system_costs[systems]; c_ptr++) {
	if (c_ptr->used) {
	    (void) printf ("%-10s %5ld    $%3ld.%.2ld\n", c_ptr->system, 
		c_ptr->minutes, c_ptr->tot_cost/100L, c_ptr->tot_cost%100L);
	    total += c_ptr->tot_cost;
	    tot_mins += c_ptr->minutes;
	}
    }
    (void) printf ("\nTOTAL      %5ld    $%3ld.%.2ld\n", 
	tot_mins, total/100L, total%100L);
    return (0);
}
	

int lookfor(sys)
    char *sys;
{
    int i;

    for (i = 0; i < systems; i++) {
	if (!strcmp (sys, system_costs[i].system))
	    return(i);
    }
    return (-1); /* Indicate system not found */
}
SHAR_EOF
fi
if test -f 'scan_cost.c'
then
	echo shar: "will not over-write existing file 'scan_cost.c'"
else
cat << \SHAR_EOF > 'scan_cost.c'
#include <stdio.h>
#include <string.h>

#define PARMSZ 200
#define DAY_TIME(h,m,s)	(((long)h*60L+(long)m)*60L+(long)s)
#define MINS_PER_DAY	(24L*60L)

char buffer[200];

FILE *fopen();
int sc, sscanf();
char *fgets();
char parm1[PARMSZ], parm2[PARMSZ], parm3[PARMSZ],
     parm4[PARMSZ], parm5[PARMSZ], parm6[PARMSZ];
char start_time[PARMSZ];


int main() {

    int running_flag = 0;	/* indicates whether a session is open */

    /*
	For each record of standard input:

	1) Read the fields
	2) if the 4th field is SUCCEEDED, start a session
	    - remember the time for later
	3) if the 4th file is OK and the 5th is (conversation, finish a session
	4) if the 4th file is LOGIN and the 5th is FAILED finish a session
    */
    while (fgets(buffer,(int)sizeof(buffer),stdin)) {

	sc = sscanf(buffer,"%s%s%s%s%s%s",
	    parm1, parm2, parm3, parm4, parm5, parm6);

	if (sc >= 5) {

	    if (!strcmp(parm4,"SUCCEEDED")) {
		if (running_flag)
		    (void)fprintf (stderr, "Dangling open at %s\n", start_time);
		(void)strcpy (start_time, parm3);
		running_flag = 1;
	    }

	    if ((!strcmp(parm4,"OK") && !strcmp(parm5,"(conversation"))
	    || (!strcmp(parm4,"LOGIN") && !strcmp(parm5,"FAILED"))
	    || (!strcmp(parm4,"FAILED") && !strcmp(parm5,"(conversation"))
	    || (!strcmp(parm4,"HANDSHAKE") && !strcmp(parm5,"FAILED"))) {
		if (running_flag) {
		    talk_end (parm2, start_time, parm3);
		    running_flag = 0;
		}
	    }

	    if ((!strcmp(parm5,"FAILED") && !strcmp(parm6,"(conversation"))) {
		if (running_flag) {
		    talk_end (parm2, start_time, parm4);
		    running_flag = 0;
		}
	    }
	}
    }
    return (0);
}

long time_secs();

/*
    talk_end - subtract times and output record
*/
talk_end (system, from, to)
    char *system, *from, *to;
{
    long from_secs, to_secs, diff_secs;
    long diff_min;

    from_secs = time_secs (from);
    if (from_secs == -1L)
	return;
    to_secs = time_secs (to);
    if (to_secs == -1L)
	return;
    diff_secs = to_secs-from_secs;
    diff_min = (diff_secs + 59L) / 60L;
    if (diff_min < 0)
	diff_min += MINS_PER_DAY;
    (void)printf ("%s %s %ld\n", system, from, diff_min);
}
    

/*
    time_secs - convert a string to seconds since midnight
*/
long time_secs (time_string)
    char *time_string;
{
    int h,m,s;

    if (sscanf(time_string, "(%*d/%*d-%d:%d:%d", &h, &m, &s) == 3) {
        return (DAY_TIME (h,m,s));
    } else {
	(void)fprintf(stderr, "Error converting time string %s/n", time_string);
	return (-1L);
    }
}
SHAR_EOF
fi
if test -f 'costs'
then
	echo shar: "will not over-write existing file 'costs'"
else
cat << \SHAR_EOF > 'costs'
# costs
#
# This file has costs for each uucp destination of the form
#
#	system	first	addtl	minimum
#
# where
#	system	uucp node name for the system,
#	first	cost of the first minute (in cents),
#	addtl	cost for additional minutes (in cents), and
#	minimum	minimum cost (in cents).
#
# system	first	addtl	minimum
#
csd_v		11	5	0
godfre		0	0	0
intel		0	0	0
jca		0	0	0
omen		0	0	0
Pcrat		15	11	0
pcrat		0	0	0
rivera		0	0	0
uunet		5	5	15
zeus		15	11	0
SHAR_EOF
fi
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
This is a package which lets you keep keep track of the costs incurred by
uucp.  It provides a command (uucost) which gives a screen display of the
costs incurred so far this month and so far today.  Additionally, there is
a script which is run every night to capture today's data.  There is also
a script to be run every month which sends mail to the uucp account giving 
a summary of this month's usage.

This is intended for use with HDB uucp, but could be altered for others.  
Modifications for links whose costs vary depending on the time of the 
connection will probably be made by the author in the future.  

The files and their contents are as follows:

README		This file.
Makefile	The file used to build the package.
cost.log	A log file that will be created (as null) at installation.
costs		A file containing the cost to originate a connection to 
		each site you call.  Documentation within the file describe
		the format.
form_cost.c	Formats the output of scan_cost for human reading.
scan_cost.c	Scans the output of a uulog to extract relevant info.
uucost		A script file for terminal display of current cost data.
uucost.da	A daily script to update cost.log.
uucost.mo	A monthly script to send mail on previous month's usage.

The package is set up to run in the directory /usr/lib/uucp/uucost.
After extracting the package:

1) make
2) make install
3) edit costs file, adding costs for machines you call
4) type "uucost", to verify proper operation
5) put a call to the shell script uucost.da at the beginning of uudemon.cleanu
6) add a cron entry to run uucost.mo on the 1st of the month, after the 
   previous night's uucleanup has completed

If you have any problems, send me e-mail.


Jack Bonn
Software Labs, Ltd.
seismo!uunet!swlabs!jack
SHAR_EOF
fi
if test -f 'uucost'
then
	echo shar: "will not over-write existing file 'uucost'"
else
cat << \SHAR_EOF > 'uucost'
#! /bin/sh
echo
echo For Month:
/usr/lib/uucp/uucost/form_cost </usr/lib/uucp/uucost/cost.log
echo
echo Today:
uulog | /usr/lib/uucp/uucost/scan_cost | /usr/lib/uucp/uucost/form_cost
echo
SHAR_EOF
fi
if test -f 'uucost.da'
then
	echo shar: "will not over-write existing file 'uucost.da'"
else
cat << \SHAR_EOF > 'uucost.da'
#! /bin/sh
#
# uucost.da -	Do the daily cleanup.  This is intended to be included
#		in the uudemon.cleanu script, BEFORE the logs are copied
#		and zeroed.
#
uulog | /usr/lib/uucp/uucost/scan_cost >>/usr/lib/uucp/uucost/cost.log
SHAR_EOF
fi
if test -f 'uucost.mo'
then
	echo shar: "will not over-write existing file 'uucost.mo'"
else
cat << \SHAR_EOF > 'uucost.mo'
#! /bin/sh
#
# uucost.mo -	Do the monthly cleanup.  This is intended to be included
#		in a crontab entry that is run in the wee hours of a new
#		month, AFTER uudemon.cleanu script has been run.
#
echo Uucp cost summary for the month
echo
/usr/lib/uucp/uucost/form_cost </usr/lib/uucp/uucost/cost.log
>/usr/lib/uucp/uucost/cost.log
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
Jack Bonn, <> Software Labs, Ltd, Box 451, Easton CT  06612
seismo!uunet!swlabs!jack