root/usr/src/cmd/bnu/perfstat.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
* This module is intended to collect performance statistics about the
* operation of uucico.  All instances of uucico will write their log
* entries to the files who's path is defined by PERFLOG.  Statistics
* will only be collected if PERFLOG exists when uucico starts, it will
* not be created automatically.  This gives the SA an easy way to turn
* statistics collection on or off at run time.  Three types
* of records will be written to the file, and each record will be
* identified by a mnemonic type at the begining of the record.  The record
* types are as follows:
*
*       conn -          Contains statistics about the establishment of
*                       a connection.
*
*       xfer -          Contains statistics about a file transfer.
*
* The intention is to use grep to select the conn and xfer records and put
* them in two Unity data bases.  No attempt will be made to process the
* error records with Unity.
*
* Both the conn and the xfer records will contain a time stamp field.
* This field will be written in case there is a desire to do time of
* day traffic studies.  The time that will be written will be GMT
* to avoid the vagaries of time zone setting for uucico.  The time
* stamp will contain 12 digits of the form YYMMDDhhmmss.  This allows
* proper sorting by time, and the fixed length field type of Unity
* can be used to pick it apart if necessary.  The time stamp is the
* time that the record is written.
*
* Statistics will be collected on the wall clock (real) time to perform
* an action and CPU consumption to perform an action.  These times will
* be written in seconds and fractions of a second to two decimal places.
*
* The conn and xfer records will be written so that they can be processed
* with the following Unity schema (D files).  For those not familiar with
* Unity, the columns are:
*
*       column 1 -      field name
*       column 2 -      field type (t=variable width) and field separator.
*       column 3 -      number of columns to use when printing the field
*                       with uprint.
*       column 4 -      a user friendly field name.
*
* Conn:
*
*       type    t|      4       record type (always conn)
*       ts      t|      12      time stamp
*       procid  t|      5       uucico's process id
*       myname  t|      6       name of the machine where the record is written
*       role    t|      1       M = master, S = slave
*       remote  t|      6       name of remote system
*       device  t|      6       name of device used for connection
*       protocol t|     1       the protocal that is used for communication
*       netid   t|      6       physical network ID
*       real    t|      6       real time to connect
*       user    t|      6       user time to connect
*       sys     t\n     6       system (kernal) time to connect
*
* The timer for connection processing starts immediately after the
* command line processing is complete, and it is stopped after the
* protocol has been selected.
*
* Xfer:
*
*       type    t|      4       record type (always xfer)
*       jobgrade t|     1       job grade ID
*       ts      t|      12      time stamp
*       procid  t|      5       uucico's process id
*       myname  t|      6       name of the machine where the record is written
*       role    t|      1       M = master, S = slave
*       remote  t|      6       name of remote system
*       device  t|      6       name of device used for connection
*       protocol t|     1       the protocal that is used for communication
*       netid   t|      6       physical network ID
*       job     t|      7       name of the job.  (Master only).
*       inqueue t|      6       time in seconds that file was in queue (Master
*                                       only).
*       tat     t|      6       turn around time in sec.  (Master only).
*       bytes   t|      6       size of the file that was transferred
*       flags   t|      3       m = mail to requester on completion,
*                               n = notify remote user, s = write status
*                               file.  (Master only).
*       streal  t|      6       real time to start up transfer (master only).
*       stuser  t|      6
*       stsys   t|      6
*       xfrreal t|      6       real time to transfer file
*       xfruser t|      6
*       xfrsys  t|      6
*       trmreal t|      6       real time to terminate the transfer
*       trmuser t|      6
*       trmsys  t|      6
*       text    t|      12      "PARTIAL FILE" if the data is being transmitted
*                               before breaking the transmission; blank if the
*                               partial file after the breakpoint or the whole
*                               file is being transmitted completely.
*
* Start up time includes the time for the master to search the queues
* for the next file, for the master and slave to exchange work vectors,
* and time to open files.  It is only recorded on the master.
* Xfer times is the time to transfer the data, close the file, and
* exchange confirmation messages.  Termination time is the time to send
* mail notifications and write status files.  Turn around time is the
* difference between the time that the file was queued and the time that
* the final notification was sent.
*/

#include        "uucp.h"
#include        "log.h"

/*
*               SYMBOL DEFINITIONS
*/

#define FS              '|'     /* Field seperator for output records. */
#define LOGCHECK        {if ((Initialized == FALSE) || \
                                (Collecting == FALSE)) return; }

/* Subscripts for connection time marks: */

#define CT_START        0       /* Start connection establishment. */
#define CT_CONNECTED    1       /* Connection completed. */
#define CT_SIZE         2       /* Number of elements in array. */

/* Subscripts for xfer time marks: */

#define XT_LOOK         0       /* Start looking for a file (master only). */
#define XT_FOUND        1       /* File found (master only). */
#define XT_BEGXFER      2       /* Start of xfer of data. */
#define XT_ENDXFER      3       /* Data xfer complete. */
#define XT_ENDFILE      4       /* Done mailing and notifying. */
#define XT_SIZE         5       /* Number of elements in array. */

/*
*               STRUCTURE DEFINITIONS
*/

typedef struct timeUsed         /* Time consummed between events. */
                {
                        float   tu_real;        /* Real time used. */
                        float   tu_user;        /* User time used. */
                        float   tu_sys;         /* System time used. */
                } TUSED;

typedef struct timeMark         /* Holds times for an event. */
                {
                        int     tm_valid;       /* True if data present. */
                        long    tm_real;        /* Relative wall clock. */
                        struct tms tm_cycles;   /* CPU consumption. */
                } TMARK;

struct connData                 /* Data for construction of conn record. */
                {
                        char    cn_role;        /* Master/slave indicator. */
                        TMARK   cn_times[CT_SIZE]; /* Event data. */
                };

struct xferData                 /* Data for construction of xfer record. */
                {
                        char    xf_role;        /* Master/slave indicator. */
                        char    xf_direction;   /* Send/receive indicator. */
                        time_t  xf_intoque;     /* Time that file was placed
                                                 *   in the queue. (master
                                                 *   only). */
                        long    xf_deque;       /* Time that file was
                                                 *   dequeued. (master only)*/
                        long    xf_filedone;    /* Time that file was
                                                 *   completed. */
                        char    xf_jobname[MODSTR]; /* C. file (master only)*/
                        char    xf_jobgrade[MODSTR]; /* job grade id */
                        off_t   xf_bytes;       /* Bytes transferred. */
                        char    xf_flags[MODSTR]; /* Notification flags. */
                        TMARK   xf_times[XT_SIZE]; /* Event data. */
                };

/*
*               LOCAL DATA
*/

static int              Collecting = FALSE; /* True if we are collecting
                                             *   data. */
static struct connData  Conn = {0};     /* Connection data. */
static char             Device[MODSTR] = ""; /* Type of communication
                                              *    device. */
static int              Initialized = FALSE; /* True if we have been
                                              *   initialized. */
static int              LogFile = CLOSED; /* Log file file destriptor. */
static char             LogName[] = PERFLOG; /* Name of our log file. */
static pid_t            Procid = {0};   /* Our processid. */
static char             Record[LOGSIZE]; /* Place to build log records. */
static char             Remote[MODSTR] = ""; /* Name of the remote system. */
static char             myname[MAXBASENAME+1] = ""; /* Name of the source system
                                                        . */
static char             Protocol[MODSTR]; /* Protocol in use */
static char             Netid[MODSTR] = NOTAVAIL; /* Network ID in use */
static struct xferData  Xfer = {0};     /* Transfer data. */

/* Messages: */

static char     Msg_badopen[] = "failed to open %s.  Errno=%%d\n";
static char     Msg_opening[] = "attempting to open %s\n";
static char     Msg_write[] = "error in writing to %s.  Errno=%%d.\n";

/*
*               LOCAL FUNCTIONS
*/

/* Declarations of functions: */

STATIC_FUNC void        grabTimes();
STATIC_FUNC void        pfloat();
STATIC_FUNC void        reportConn();
STATIC_FUNC void        reportFile();
STATIC_FUNC void        reportTimes();
STATIC_FUNC void        subTimes();


/*
* Local Function:       grabTimes - Get Real and CPU Times
*
* This function uses times(2) to obtain the current real time and CPU
* consumption.  The time mark is also marked as valid.
*
* Parameters:
*
*       markptr -       Address of structure to save times.
*
* Return:
*
*       none.
*/

STATIC_FUNC void
grabTimes (markptr)

register TMARK *        markptr;

{
        markptr->tm_real = times(&markptr->tm_cycles);
        if (markptr->tm_real != FAIL)
                markptr->tm_valid = TRUE;
        return;
}


/*
* Local Function:       pfloat - Print a Floating Number
*
* Format a floating point number for output to the Unity data base.
* If the number is NOTIME, "na" will be displayed instead.
*
* Parameters:
*
*       dest -          The result will be concatenated to this string.
*
*       number -        The number to be formated.
*
*       sep -           Field separator character.
*/

STATIC_FUNC void
pfloat (dest, number, sep)

char *          dest;
double          number;         /* float is promoted to double for args. */
char            sep;

{
        static char     rformat[] = "%c%.2f";
        static char     naformat[] = "%c%s";

        register char * cp;

        cp = dest + strlen(dest);
        if (number == (float) NOTIME)
                sprintf(cp, naformat, sep, NOTAVAIL);
        else
                sprintf(cp, rformat, sep, number);
        return;
}

/*
* Local Function:       reportConn - Write Out Conn Record
*
* This function writes a conn record to the logfile.
*
* Parameters:
*
*       None.
*
* Returns:
*
*       None.
*/

STATIC_FUNC void
reportConn ()

{
        TUSED   contimes;       /* Times to make connection. */
        static char     format[] = "%s%c%s%c%ld%c%s%c%c%c%s%c%s%c%s%c%s";

        sprintf(Record, format,
                "conn", FS,             /* record type. */
                gmt(), FS,              /* current time. */
                (long) Procid, FS,      /* our process id. */
                myname, FS,             /* name of local system */
                Conn.cn_role, FS,       /* slave or master. */
                Remote, FS,             /* name of remote system. */
                Device, FS,             /* device used for communication. */
                Protocol, FS,           /* protocol used for comm. */
                Netid                   /* Network ID */
               );
        subTimes(&contimes, &Conn.cn_times[CT_CONNECTED],
                        &Conn.cn_times[CT_START]);
        reportTimes(Record, &contimes, FS);
        strcat(Record, EOR);
        writeLog(Record,&LogFile,LogName,&Collecting);
        return;
}

/*
* Local Function:       reportFile - Write File Statistics to Log
*
* This function writes statistics about the current file to the log
* file.
*
* Parameters:
*
*       none.
*/

STATIC_FUNC void
reportFile (breakmsg)
char * breakmsg;

{
                                     /* minuend,        subtrahand */
        static int      drvtab[] = {
                                        XT_FOUND,       XT_LOOK, /* startup */
                                        XT_ENDXFER,     XT_BEGXFER, /* xfer */
                                        XT_ENDFILE,     XT_ENDXFER /* term. */
                                   };
        static char     format1[] = "%s%c%s%c%s%c%ld%c%s%c%c%c%s%c%s%c%s%c%s%c%s";
        static char     format2[] = "%c%ld%c%s"; /* Bytes & flags. */

        register struct xferData *      xdptr;
        register TMARK *                tdptr;
        register int                    i;

        TUSED           diff;           /* time difference between events. */
        float           inque;          /* time in queue. */
        int             lastbyte;       /* Offset to last byte in Record. */
        char *          na = NOTAVAIL;  /* String to show data not available*/
        char            role;           /* Current master/slave status. */
        float           tat;            /* Turn around time. */

        xdptr = &Xfer;                  /* Point to Xfer data. */
        role = xdptr->xf_role;
        sprintf(Record, format1,
                "xfer", FS,             /* Record type. */
                (role == MCHAR) ? xdptr->xf_jobgrade : na ,FS, /* job grade */
                gmt(), FS,              /* Current time. */
                (long) Procid, FS,      /* Our process id. */
                myname, FS,             /* name of local system */
                role, FS,               /* master/slave. */
                Remote, FS,             /* remote. */
                Device, FS,             /* communications device. */
                Protocol, FS,           /* protocol used for comm. */
                Netid, FS,                      /* Network ID */
                (role == MCHAR) ? xdptr->xf_jobname : na
               );

        /* Do time in queue and turn around time. */

        if (role == MCHAR)
        {
                inque = (float) (xdptr->xf_deque - xdptr->xf_intoque);
                tat = (float) (xdptr->xf_filedone - xdptr->xf_intoque);
        } else
        {
                inque = (float) NOTIME; /* Not app. if not master. */
                tat = (float) NOTIME;
        }
        pfloat(Record, inque, FS);
        pfloat(Record, tat, FS);

        /*
        * Report bytes transferred and notification flags.
        */

        lastbyte = strlen(Record);
        (void) sprintf(Record+lastbyte, format2,
                        FS, getfilesize(),FS,
                        (role == MCHAR) ? xdptr->xf_flags : na
                      );

        /*
        * Report resource consumption for file startup, file transfer,
        * and file termination.  This means reporting the differences
        * between pairs of elements in the xf_times array of Xfer.  This
        * will be controled by drvtab which contains pairs of subscripts
        * to designate the xf_times elements.
        */

        tdptr = &xdptr->xf_times[0];
        for (i = 0; i < sizeof(drvtab)/(sizeof(int)); i += 2)
        {
                subTimes(&diff, (tdptr + drvtab[i]), (tdptr + drvtab[i+1]));
                reportTimes(Record, &diff, FS);
        }

        /*
        * write file status
        */

        lastbyte = strlen(Record);
        (void) sprintf(Record+lastbyte, "%c%s%c",
                        FS, (*breakmsg == NULLCHAR) ? NOTAVAIL : breakmsg, FS);

        /* Terminate the record and write it out. */

        (void) strcat(Record, EOR);
        writeLog(Record,&LogFile,LogName,&Collecting);
        return;
}

/*
* Local Function:       reportTimes - Print Real, User, and Sys Times
*
* This function is used to convert the real, user, and system times from
* a TUSED structure to Ascii strings.  The results are concatenated to
* the dest string.  If any of the times are NOTIME, they will be reported
* as "na".  The fields will be seperated by the sep character and the
* sep character will be the first character concatenated to the buffer.  No
* seperator character will be placed at the end.  Thus, the output string
* will be of the form:
*
*       |real|user|sys
*
* Parameters:
*
*       dest -          String to receive Ascii times.
*
*       diffptr -       Address of the time data.
*
*       sep -           The field seperator character.
*/

STATIC_FUNC void
reportTimes (dest, diffptr, sep)

register char *         dest;
register TUSED *        diffptr;
char                    sep;

{
        pfloat(dest, diffptr->tu_real, sep);
        pfloat(dest, diffptr->tu_user, sep);
        pfloat(dest, diffptr->tu_sys, sep);
        return;
}

/*
* Local Function:       subTimes - Subtract Times Between Events
*
* This function takes the output from two calls to times(2) in the form
* of two TMARK structures, and determines the amount of time consummed
* for various categories.  The result is stored in the specified
* TUSED structure.
*
* Parameters:
*
*       diff -          Place to store the result of the subtraction.
*       minuend -       The second time event.
*       subtra -        The subtrahend in the subtraction.  This should
*                       be the first of two time events.
*
* On the large scale this function does the following:
*
*       diff = minuend - subtra
*/

STATIC_FUNC void
subTimes (diff, minuend, subtra)

register TUSED *        diff;
register TMARK *        minuend;
register TMARK *        subtra;

{
        register struct tms *   mintms;
        register struct tms *   subtms;

        long    ltemp;          /* Temporary storage for long arith. */
        float   ticks;          /* Clock interrupts per second. */

        if ((minuend->tm_valid != TRUE) || (subtra->tm_valid != TRUE))
        {                               /* If data has not been collected. */
                diff->tu_real = NOTIME;
                diff->tu_user = NOTIME;
                diff->tu_sys = NOTIME;
        } else
        {
                ticks = (float) HZ;     /* HZ defined in <sys/param.h>. */
                mintms = &minuend->tm_cycles;
                subtms = &subtra->tm_cycles;

                /* Calculate real time. */

                ltemp = minuend->tm_real - subtra->tm_real;
                diff->tu_real = ((float) ltemp)/ticks;

                /* Calculate user time. */

                ltemp =   mintms->tms_utime
                        - subtms->tms_utime
                        + mintms->tms_cutime
                        - subtms->tms_cutime;
                diff->tu_user = ((float) ltemp)/ticks;

                /* Calculate user time. */

                ltemp =   mintms->tms_stime
                        - subtms->tms_stime
                        + mintms->tms_cstime
                        - subtms->tms_cstime;
                diff->tu_sys = ((float) ltemp)/ticks;
        }
        return;
}

/*
*               EXTERNAL FUNCTIONS
*/

/*
* Function:     gmt - Generate Current Time String
*
* This function returns the address a string containing the current
* GMT in the form YYMMDDhhmmss.
*
* Parameters:
*
*       none
*
* Return:
*
*       An address of a static character array containing the date.
*/

char *
gmt()

{
        static char     date[] = "YYMMDDhhmmss";

        register struct tm *    td;
        time_t                  now;    /* Current time. */

        now = time((time_t *) 0);
        td = gmtime(&now);
        (void) sprintf(date, "%02d%02d%02d%02d%02d%02d",
                                (td->tm_year % 100),
                                td->tm_mon + 1,
                                td->tm_mday,
                                td->tm_hour,
                                td->tm_min,
                                td->tm_sec
                      );
        return date;
}


/*
* Function:     writeLog - Write String to Log File
*
* After insuring that the log file is open, this function will write
* the specified string to the log file.  If a write error occurs,
* statistics collection will be disabled.
*
* Parameters:
*
*       string - Null terminated string to be written out.
*       logfile - file descripter
*       logname - name of log file.
*       collecting - log enable/disable
*/

void
writeLog (string, logfile, logname, collecting)

char *  string;
int *   logfile;
char *  logname;
int *   collecting;

{
        register int    length;         /* Length of the string. */
        register int    rv;             /* Return value from write. */

        char            errmsg[BUFSIZ]; /* Place for error messages. */

        if (openLog(logfile,logname) != SUCCESS){
                *collecting = FALSE;
                return;
        }
        length = strlen(string);
        do
        {
                rv = write(*logfile, string, (unsigned) length);
        } while ((rv < 0) && (errno == EINTR)); /* Retry if interrupted. */
        if (rv < length)
        {                               /* Error or incomplete output. */
                (void) sprintf(errmsg, Msg_write, logname);
                DEBUG(DB_IMPORTANT, errmsg, errno);

                /* If we had a write error, lets give up on loggine. */

                closeLog(logfile);
                *collecting = FALSE;
        }
        return;
}

/*
* Function:     closeLog - Close the Log File
*
* This function allows uucico to close the log file in preparation for
* forking.
*
* Parameters:
*
*       log file descriptor
*/

void
closeLog (logfile)
int     *logfile;

{
        if (*logfile != CLOSED)
        {
                (void) close(*logfile);
                *logfile = CLOSED;
        }
        return;
}


/*
* Function: copyText - Copy String to Dynamic Memory
*
* This function copies a string to a buffer.  It insures that there is
* no overflow of the buffer and that the result is null terminated.
*
* Parameters:
*
*       tptr -          address of the buffer where the string is to
*                       be stored.
*
*       size -          number of bytes in the buffer.
*
*       string -        string to be saved.
*
* Returns:
*
*       none.
*/

void
copyText (tptr, size, string)

register char * tptr;
register int    size;
char *          string;

{
        (void) strncpy(tptr, string, size);
        *(tptr + size - 1) = NULLCHAR;
        return;
}

/*
* Function:     pfConnected - Report Connection Completion
*
* Uucico uses pfConnected to tell this performance package that a connection
* has been established with the remote system.
*
* Parameters:
*
*       remote -        name of the remote system.
*
*       device -        the type of device being used for communicaitons.
*/

void
pfConnected (remote, device)

char *  remote;
char *  device;

{
        register int            i;
        register TMARK *        tptr;

        LOGCHECK;
        grabTimes(&Conn.cn_times[CT_CONNECTED]);
        copyText(Remote, sizeof(Remote), remote);
        copyText(Device, sizeof(Device), device);
        reportConn();
        tptr = &Conn.cn_times[0];

        /*
        * Mark connection times as invalid.  This is really unnecessary
        * since there should only be one connection per invocation of uucico.
        * We do it for consistency with use of the transfer data.
        */

        for (i = 0; i < CT_SIZE; i++, tptr++)
                tptr->tm_valid = FALSE;
        return;
}


/*
* Function:     pfEndFile - Report End of File
*
* Uucico uses pfEndFile to tell our statistics collection package that
* all processing has been finished on the current file.  PfEndfile should
* be called after all notifications have been done and after the status
* file has been written.  PfEndfile writes out a xfer record for the
* file that just completed.
*
* Parameters:
*
*       none
*/

void
pfEndfile (breakmsg)
char * breakmsg;
{
        register int            i;
        register TMARK *        tdptr;
        register struct xferData *      xptr = &Xfer;

        LOGCHECK;
        grabTimes(&Xfer.xf_times[XT_ENDFILE]);
        Xfer.xf_filedone = time((time_t *) 0);
        reportFile(breakmsg);

        /* Now that we have reported them, mark all times as invalid. */

        copyText(xptr->xf_flags, sizeof(xptr->xf_flags), NOTAVAIL);
        tdptr = &Xfer.xf_times[0];
        for (i = 0; i < XT_SIZE; i++, tdptr++)
                tdptr->tm_valid = FALSE;
        return;
}

/*
* Function:     pfEndXfer - File Transfer Complete
*
* Calling pfEndXfer tells the performance package that a file transfer
* has been completed.  It should be called after the destination site
* closes the file and confirms receipt, but before notifications are done.
*
* Parameters:
*
*       none
*/

void
pfEndXfer ()

{
        LOGCHECK;
        grabTimes(&Xfer.xf_times[XT_ENDXFER]);
        return;
}

/*
* Function:     pfFindFile - Looking for Another File
*
* Uucico uses pfFindFile to announce that it is going to explore the
* queues for another file transfer to do.  PfFindFile is only called
* when uucico is in the role of master.
*
* Parameters:
*
*       none
*/

void
pfFindFile ()

{
        LOGCHECK;
        grabTimes(&Xfer.xf_times[XT_LOOK]);
        return;
}

/*
* Function:     pfFound - Found Another File
*
* PfFound is a counterpart of pfFindFile.  It is called when a new file
* has been found.  Like pfFindFile it is called only by a master uucico.
*
* Parameters:
*
*       jobid -         The name of the job that was found.
*
*       flags -         Options flags that were stored in the queue.
*                       These flags are originally set by uucp.
*
*       intoQue -       The time that the C. file was placed in the queue.
*/

void
pfFound (jobid, flags, intoQue)

char *  jobid;
char *  flags;
time_t  intoQue;

{
        register struct xferData *      xptr = &Xfer;

        LOGCHECK;
        grabTimes(&xptr->xf_times[XT_FOUND]);
        copyText(xptr->xf_jobname, sizeof(xptr->xf_jobname), jobid);
        xptr->xf_jobgrade[0] = jobid[strlen(jobid)-5];
        xptr->xf_jobgrade[1] = NULLCHAR;/* get job grade from jobid */
        copyText(xptr->xf_flags, sizeof(xptr->xf_flags), flags);

        /* Save time that file was placed in queue and current time. */

        xptr->xf_intoque = intoQue;
        xptr->xf_deque = time((time_t *) 0);
        return;
}

/*
* Function:     pfInit - Initialize Performance Package
*
* This function allows the performance package to initialize its internal
* data structures.  It should be called one time only when uucico starts
* running.
*
* Parameters:
*
*       none
*/

void
pfInit ()

{
        register struct xferData *      xptr = &Xfer;

        if (Initialized == TRUE)
                return;
        Procid = getpid();
        myName(myname);
        copyText(xptr->xf_flags, sizeof(xptr->xf_flags), NOTAVAIL);

        /*
        * Attempt to open the log file.  If we can't do it, then we
        * won't collect statistics.
        */

        if (openLog(&LogFile,LogName) == SUCCESS)
                Collecting = TRUE;
        else
                Collecting = FALSE;
        Initialized = TRUE;
        return;
}

/*
* Function:     pfStrtConn - Going to Establish Connection
*
* Uucico uses pfStrtConn to announce that it is going to attempt
* to establish a connection.
*
* Parameters:
*
*       role -          An indication of whether uucico is currently
*                       running in master or slave mode.  M = master,
*                       S = slave.
*/

void
pfStrtConn (role)

char    role;
{
        LOGCHECK;
        grabTimes(&Conn.cn_times[CT_START]);
        Conn.cn_role = role;
        return;
}

/*
* Function:     pfStrtXfer - Starting File Transfer
*
* This function should be called just as the first byte of data is
* about to be transferred.
*
* Parameters:
*
*       role -          An indication of whether uucico is currently
*                       running in master or slave mode.  M = master,
*                       S = slave.
*
*       direction -     Direction of file transfer.  S = sending to
*                       remote, R = receiving from remote.
*/

void
pfStrtXfer(role, direction)

char    role;
char    direction;

{
        register struct xferData *      xptr = &Xfer;

        LOGCHECK;
        grabTimes(&xptr->xf_times[XT_BEGXFER]);
        xptr->xf_role = role;
        xptr->xf_direction = direction;
        return;
}

/*
        A protocol which both master and slave sides agree on
*/

void
pfPtcl(str)
char    *str;
{
        strcpy(Protocol,str);
        return;
}

/*
* Function:     openLog  - Open the Log File
*
* If the log file is already open this function immediately returns
* success.  Otherwise, an attempt is made to open the logfile in append
* mode.
*
* Parameters:
*
*       logfile - file descripter
*       logname - name of log file.
*
* Returns:
*
*       SUCCESS -       The log file is open.
*       FAIL -          Unable to open logfile.
*/

int
openLog (logfile,logname)
int     *logfile;
char    *logname;
{
        register int    fd;             /* File descriptor of log file. */

        int             level;          /* Level for debug message. */
        char            msgbuf[BUFSIZ];

        /* See if file already open. */

        if (*logfile != CLOSED)
                return (SUCCESS);

        /* Attempt to open the file. */

        DEBUG(DB_TRACE, Msg_opening, logname);
        do
        {
                fd = open(logname, O_WRONLY | O_APPEND);
        } while ((fd < 0) && (errno == EINTR)); /* Retry if interrupted. */
        if (fd < 0) {   /* Error on open. */
                (void) sprintf(msgbuf, Msg_badopen, logname);
                if (errno == ENOENT)
                        level = DB_DETAIL; /* If the file is not there
                                            *   it will usually mean
                                            *   that the SA doesn't
                                            *   want to collect
                                            *   statisitcs. */
                else
                        level = DB_IMPORTANT;   /* Unexpected error */
                DEBUG(level, msgbuf, errno); /* No log file. */
                return FAIL;
        } else {
                *logfile = fd;
                return SUCCESS;
        }
}

#ifdef BSD4_2
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>

static clock_t
scale60(tvp)
        register struct timeval *tvp;
{
        return (tvp->tv_sec * 60 + tvp->tv_usec / 16667);
}

clock_t
times(tmsp)
        register struct tms *tmsp;
{
        struct rusage ru;
        struct timeval now;
        static time_t epoch;

        if (getrusage(RUSAGE_SELF, &ru) < 0)
                return (clock_t)(-1);
        tmsp->tms_utime = scale60(&ru.ru_utime);
        tmsp->tms_stime = scale60(&ru.ru_stime);
        if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
                return (clock_t)(-1);
        tmsp->tms_cutime = scale60(&ru.ru_utime);
        tmsp->tms_cstime = scale60(&ru.ru_stime);
        if (gettimeofday(&now, (struct timezone *)0) < 0)
                return (clock_t)(-1);
        if (epoch == 0)
                epoch = now.tv_sec;
        now.tv_sec -= epoch;
        return (scale60(&now));
}
#endif /* BSD4_2 */