xref: /titanic_51/usr/src/cmd/bnu/perfstat.c (revision 40db2e2b777b79f3dd0d6d9629593a07f86b9c0a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 /*
33 * This module is intended to collect performance statistics about the
34 * operation of uucico.  All instances of uucico will write their log
35 * entries to the files who's path is defined by PERFLOG.  Statistics
36 * will only be collected if PERFLOG exists when uucico starts, it will
37 * not be created automatically.  This gives the SA an easy way to turn
38 * statistics collection on or off at run time.  Three types
39 * of records will be written to the file, and each record will be
40 * identified by a mnemonic type at the begining of the record.  The record
41 * types are as follows:
42 *
43 *	conn -		Contains statistics about the establishment of
44 *			a connection.
45 *
46 *	xfer -		Contains statistics about a file transfer.
47 *
48 * The intention is to use grep to select the conn and xfer records and put
49 * them in two Unity data bases.  No attempt will be made to process the
50 * error records with Unity.
51 *
52 * Both the conn and the xfer records will contain a time stamp field.
53 * This field will be written in case there is a desire to do time of
54 * day traffic studies.  The time that will be written will be GMT
55 * to avoid the vagaries of time zone setting for uucico.  The time
56 * stamp will contain 12 digits of the form YYMMDDhhmmss.  This allows
57 * proper sorting by time, and the fixed length field type of Unity
58 * can be used to pick it apart if necessary.  The time stamp is the
59 * time that the record is written.
60 *
61 * Statistics will be collected on the wall clock (real) time to perform
62 * an action and CPU consumption to perform an action.  These times will
63 * be written in seconds and fractions of a second to two decimal places.
64 *
65 * The conn and xfer records will be written so that they can be processed
66 * with the following Unity schema (D files).  For those not familiar with
67 * Unity, the columns are:
68 *
69 *	column 1 -	field name
70 *	column 2 -	field type (t=variable width) and field separator.
71 *	column 3 -	number of columns to use when printing the field
72 *			with uprint.
73 *	column 4 -	a user friendly field name.
74 *
75 * Conn:
76 *
77 *	type	t|	4	record type (always conn)
78 *	ts	t|	12	time stamp
79 *	procid	t|	5	uucico's process id
80 *	myname	t|	6	name of the machine where the record is written
81 *	role	t|	1	M = master, S = slave
82 *	remote	t|	6	name of remote system
83 *	device	t|	6	name of device used for connection
84 *	protocol t|	1	the protocal that is used for communication
85 *	netid	t|	6	physical network ID
86 *	real	t|	6	real time to connect
87 *	user	t|	6	user time to connect
88 *	sys	t\n	6	system (kernal) time to connect
89 *
90 * The timer for connection processing starts immediately after the
91 * command line processing is complete, and it is stopped after the
92 * protocol has been selected.
93 *
94 * Xfer:
95 *
96 *	type	t|	4	record type (always xfer)
97 *	jobgrade t|	1	job grade ID
98 *	ts	t|	12	time stamp
99 *	procid	t|	5	uucico's process id
100 *	myname	t|	6	name of the machine where the record is written
101 *	role	t|	1	M = master, S = slave
102 *	remote	t|	6	name of remote system
103 *	device	t|	6	name of device used for connection
104 *	protocol t|	1	the protocal that is used for communication
105 *	netid	t|	6	physical network ID
106 *	job	t|	7	name of the job.  (Master only).
107 *	inqueue	t|	6	time in seconds that file was in queue (Master
108 *					only).
109 *	tat	t|	6	turn around time in sec.  (Master only).
110 *	bytes	t|	6	size of the file that was transferred
111 *	flags	t|	3	m = mail to requester on completion,
112 *				n = notify remote user, s = write status
113 *				file.  (Master only).
114 *	streal	t|	6	real time to start up transfer (master only).
115 *	stuser	t|	6
116 *	stsys	t|	6
117 *	xfrreal	t|	6	real time to transfer file
118 *	xfruser	t|	6
119 *	xfrsys	t|	6
120 *	trmreal	t|	6	real time to terminate the transfer
121 *	trmuser	t|	6
122 *	trmsys	t|	6
123 *	text	t|	12	"PARTIAL FILE" if the data is being transmitted
124 *				before breaking the transmission; blank if the
125 *				partial file after the breakpoint or the whole
126 *				file is being transmitted completely.
127 *
128 * Start up time includes the time for the master to search the queues
129 * for the next file, for the master and slave to exchange work vectors,
130 * and time to open files.  It is only recorded on the master.
131 * Xfer times is the time to transfer the data, close the file, and
132 * exchange confirmation messages.  Termination time is the time to send
133 * mail notifications and write status files.  Turn around time is the
134 * difference between the time that the file was queued and the time that
135 * the final notification was sent.
136 */
137 
138 #include	"uucp.h"
139 #include	"log.h"
140 
141 /*
142 *		SYMBOL DEFINITIONS
143 */
144 
145 #define	FS		'|'	/* Field seperator for output records. */
146 #define LOGCHECK	{if ((Initialized == FALSE) || \
147 				(Collecting == FALSE)) return; }
148 
149 /* Subscripts for connection time marks: */
150 
151 #define	CT_START	0	/* Start connection establishment. */
152 #define	CT_CONNECTED	1	/* Connection completed. */
153 #define	CT_SIZE		2	/* Number of elements in array. */
154 
155 /* Subscripts for xfer time marks: */
156 
157 #define	XT_LOOK		0	/* Start looking for a file (master only). */
158 #define	XT_FOUND	1	/* File found (master only). */
159 #define	XT_BEGXFER	2	/* Start of xfer of data. */
160 #define	XT_ENDXFER	3	/* Data xfer complete. */
161 #define	XT_ENDFILE	4	/* Done mailing and notifying. */
162 #define	XT_SIZE		5	/* Number of elements in array. */
163 
164 /*
165 *		STRUCTURE DEFINITIONS
166 */
167 
168 typedef struct timeUsed		/* Time consummed between events. */
169 		{
170 			float	tu_real;	/* Real time used. */
171 			float	tu_user;	/* User time used. */
172 			float	tu_sys;		/* System time used. */
173 		} TUSED;
174 
175 typedef struct timeMark		/* Holds times for an event. */
176 		{
177 			int	tm_valid;	/* True if data present. */
178 			long	tm_real;	/* Relative wall clock. */
179 			struct tms tm_cycles;	/* CPU consumption. */
180 		} TMARK;
181 
182 struct connData			/* Data for construction of conn record. */
183 		{
184 			char	cn_role;	/* Master/slave indicator. */
185 			TMARK	cn_times[CT_SIZE]; /* Event data. */
186 		};
187 
188 struct xferData			/* Data for construction of xfer record. */
189 		{
190 			char	xf_role;	/* Master/slave indicator. */
191 			char	xf_direction;	/* Send/receive indicator. */
192 			time_t	xf_intoque;	/* Time that file was placed
193 						 *   in the queue. (master
194 						 *   only). */
195 			long	xf_deque;	/* Time that file was
196 						 *   dequeued. (master only)*/
197 			long	xf_filedone;	/* Time that file was
198 						 *   completed. */
199 			char	xf_jobname[MODSTR]; /* C. file (master only)*/
200   			char	xf_jobgrade[MODSTR]; /* job grade id */
201 			off_t	xf_bytes;	/* Bytes transferred. */
202 			char	xf_flags[MODSTR]; /* Notification flags. */
203 			TMARK	xf_times[XT_SIZE]; /* Event data. */
204 		};
205 
206 /*
207 *		LOCAL DATA
208 */
209 
210 static int		Collecting = FALSE; /* True if we are collecting
211 					     *   data. */
212 static struct connData	Conn = {0};	/* Connection data. */
213 static char		Device[MODSTR] = ""; /* Type of communication
214 					      *    device. */
215 static int		Initialized = FALSE; /* True if we have been
216 					      *   initialized. */
217 static int		LogFile = CLOSED; /* Log file file destriptor. */
218 static char		LogName[] = PERFLOG; /* Name of our log file. */
219 static pid_t		Procid = {0};	/* Our processid. */
220 static char		Record[LOGSIZE]; /* Place to build log records. */
221 static char		Remote[MODSTR] = ""; /* Name of the remote system. */
222 static char		myname[MAXBASENAME+1] = ""; /* Name of the source system
223 							. */
224 static char 		Protocol[MODSTR]; /* Protocol in use */
225 static char 		Netid[MODSTR] = NOTAVAIL; /* Network ID in use */
226 static struct xferData	Xfer = {0};	/* Transfer data. */
227 
228 /* Messages: */
229 
230 static char	Msg_badopen[] = "failed to open %s.  Errno=%%d\n";
231 static char	Msg_opening[] =	"attempting to open %s\n";
232 static char	Msg_write[] = "error in writing to %s.  Errno=%%d.\n";
233 
234 /*
235 *		LOCAL FUNCTIONS
236 */
237 
238 /* Declarations of functions: */
239 
240 STATIC_FUNC void	grabTimes();
241 STATIC_FUNC void	pfloat();
242 STATIC_FUNC void	reportConn();
243 STATIC_FUNC void	reportFile();
244 STATIC_FUNC void	reportTimes();
245 STATIC_FUNC void	subTimes();
246 
247 
248 /*
249 * Local Function:	grabTimes - Get Real and CPU Times
250 *
251 * This function uses times(2) to obtain the current real time and CPU
252 * consumption.  The time mark is also marked as valid.
253 *
254 * Parameters:
255 *
256 *	markptr -	Address of structure to save times.
257 *
258 * Return:
259 *
260 *	none.
261 */
262 
263 STATIC_FUNC void
264 grabTimes (markptr)
265 
266 register TMARK *	markptr;
267 
268 {
269 	markptr->tm_real = times(&markptr->tm_cycles);
270 	if (markptr->tm_real != FAIL)
271 		markptr->tm_valid = TRUE;
272 	return;
273 }
274 
275 
276 /*
277 * Local Function:	pfloat - Print a Floating Number
278 *
279 * Format a floating point number for output to the Unity data base.
280 * If the number is NOTIME, "na" will be displayed instead.
281 *
282 * Parameters:
283 *
284 *	dest -		The result will be concatenated to this string.
285 *
286 *	number -	The number to be formated.
287 *
288 *	sep -		Field separator character.
289 */
290 
291 STATIC_FUNC void
292 pfloat (dest, number, sep)
293 
294 char *		dest;
295 double		number;		/* float is promoted to double for args. */
296 char		sep;
297 
298 {
299 	static char	rformat[] = "%c%.2f";
300 	static char	naformat[] = "%c%s";
301 
302 	register char *	cp;
303 
304 	cp = dest + strlen(dest);
305 	if (number == (float) NOTIME)
306 		sprintf(cp, naformat, sep, NOTAVAIL);
307 	else
308 		sprintf(cp, rformat, sep, number);
309 	return;
310 }
311 
312 /*
313 * Local Function:	reportConn - Write Out Conn Record
314 *
315 * This function writes a conn record to the logfile.
316 *
317 * Parameters:
318 *
319 *	None.
320 *
321 * Returns:
322 *
323 *	None.
324 */
325 
326 STATIC_FUNC void
327 reportConn ()
328 
329 {
330 	TUSED	contimes;	/* Times to make connection. */
331   	static char	format[] = "%s%c%s%c%ld%c%s%c%c%c%s%c%s%c%s%c%s";
332 
333 	sprintf(Record, format,
334 		"conn", FS,		/* record type. */
335 		gmt(), FS,		/* current time. */
336 		(long) Procid, FS,	/* our process id. */
337 		myname, FS,		/* name of local system */
338 		Conn.cn_role, FS,	/* slave or master. */
339 		Remote, FS,		/* name of remote system. */
340 		Device, FS,		/* device used for communication. */
341   		Protocol, FS,		/* protocol used for comm. */
342   		Netid			/* Network ID */
343 	       );
344 	subTimes(&contimes, &Conn.cn_times[CT_CONNECTED],
345 			&Conn.cn_times[CT_START]);
346 	reportTimes(Record, &contimes, FS);
347 	strcat(Record, EOR);
348 	writeLog(Record,&LogFile,LogName,&Collecting);
349 	return;
350 }
351 
352 /*
353 * Local Function:	reportFile - Write File Statistics to Log
354 *
355 * This function writes statistics about the current file to the log
356 * file.
357 *
358 * Parameters:
359 *
360 *	none.
361 */
362 
363 STATIC_FUNC void
364 reportFile (breakmsg)
365 char * breakmsg;
366 
367 {
368 				     /* minuend,	subtrahand */
369 	static int	drvtab[] = {
370 					XT_FOUND,	XT_LOOK, /* startup */
371 					XT_ENDXFER,	XT_BEGXFER, /* xfer */
372 					XT_ENDFILE,	XT_ENDXFER /* term. */
373 				   };
374   	static char	format1[] = "%s%c%s%c%s%c%ld%c%s%c%c%c%s%c%s%c%s%c%s%c%s";
375 	static char	format2[] = "%c%ld%c%s"; /* Bytes & flags. */
376 
377 	register struct xferData *	xdptr;
378 	register TMARK *		tdptr;
379 	register int			i;
380 
381 	TUSED		diff;		/* time difference between events. */
382 	float		inque;		/* time in queue. */
383 	int		lastbyte;	/* Offset to last byte in Record. */
384 	char *		na = NOTAVAIL;	/* String to show data not available*/
385 	char		role;		/* Current master/slave status. */
386 	float		tat;		/* Turn around time. */
387 
388 	xdptr = &Xfer;			/* Point to Xfer data. */
389 	role = xdptr->xf_role;
390 	sprintf(Record, format1,
391 		"xfer", FS,		/* Record type. */
392   		(role == MCHAR) ? xdptr->xf_jobgrade : na ,FS, /* job grade */
393 		gmt(), FS,		/* Current time. */
394 		(long) Procid, FS,	/* Our process id. */
395 		myname, FS,		/* name of local system */
396 		role, FS,		/* master/slave. */
397 		Remote, FS,		/* remote. */
398 		Device, FS,		/* communications device. */
399 		Protocol, FS,		/* protocol used for comm. */
400   		Netid, FS,			/* Network ID */
401 		(role == MCHAR) ? xdptr->xf_jobname : na
402 	       );
403 
404 	/* Do time in queue and turn around time. */
405 
406 	if (role == MCHAR)
407 	{
408 		inque = (float) (xdptr->xf_deque - xdptr->xf_intoque);
409 		tat = (float) (xdptr->xf_filedone - xdptr->xf_intoque);
410 	} else
411 	{
412 		inque = (float) NOTIME;	/* Not app. if not master. */
413 		tat = (float) NOTIME;
414 	}
415 	pfloat(Record, inque, FS);
416 	pfloat(Record, tat, FS);
417 
418 	/*
419 	* Report bytes transferred and notification flags.
420 	*/
421 
422 	lastbyte = strlen(Record);
423 	(void) sprintf(Record+lastbyte, format2,
424 			FS, getfilesize(),FS,
425   			(role == MCHAR) ? xdptr->xf_flags : na
426 		      );
427 
428 	/*
429 	* Report resource consumption for file startup, file transfer,
430 	* and file termination.  This means reporting the differences
431 	* between pairs of elements in the xf_times array of Xfer.  This
432 	* will be controled by drvtab which contains pairs of subscripts
433 	* to designate the xf_times elements.
434 	*/
435 
436 	tdptr = &xdptr->xf_times[0];
437 	for (i = 0; i < sizeof(drvtab)/(sizeof(int)); i += 2)
438 	{
439 		subTimes(&diff, (tdptr + drvtab[i]), (tdptr + drvtab[i+1]));
440 		reportTimes(Record, &diff, FS);
441 	}
442 
443 	/*
444 	* write file status
445 	*/
446 
447 	lastbyte = strlen(Record);
448   	(void) sprintf(Record+lastbyte, "%c%s%c",
449   			FS, (*breakmsg == NULLCHAR) ? NOTAVAIL : breakmsg, FS);
450 
451 	/* Terminate the record and write it out. */
452 
453 	(void) strcat(Record, EOR);
454 	writeLog(Record,&LogFile,LogName,&Collecting);
455 	return;
456 }
457 
458 /*
459 * Local Function:	reportTimes - Print Real, User, and Sys Times
460 *
461 * This function is used to convert the real, user, and system times from
462 * a TUSED structure to Ascii strings.  The results are concatenated to
463 * the dest string.  If any of the times are NOTIME, they will be reported
464 * as "na".  The fields will be seperated by the sep character and the
465 * sep character will be the first character concatenated to the buffer.  No
466 * seperator character will be placed at the end.  Thus, the output string
467 * will be of the form:
468 *
469 *	|real|user|sys
470 *
471 * Parameters:
472 *
473 *	dest -		String to receive Ascii times.
474 *
475 *	diffptr -	Address of the time data.
476 *
477 *	sep -		The field seperator character.
478 */
479 
480 STATIC_FUNC void
481 reportTimes (dest, diffptr, sep)
482 
483 register char *		dest;
484 register TUSED *	diffptr;
485 char			sep;
486 
487 {
488 	pfloat(dest, diffptr->tu_real, sep);
489 	pfloat(dest, diffptr->tu_user, sep);
490 	pfloat(dest, diffptr->tu_sys, sep);
491 	return;
492 }
493 
494 /*
495 * Local Function:	subTimes - Subtract Times Between Events
496 *
497 * This function takes the output from two calls to times(2) in the form
498 * of two TMARK structures, and determines the amount of time consummed
499 * for various categories.  The result is stored in the specified
500 * TUSED structure.
501 *
502 * Parameters:
503 *
504 *	diff -		Place to store the result of the subtraction.
505 *	minuend -	The second time event.
506 *	subtra -	The subtrahend in the subtraction.  This should
507 *			be the first of two time events.
508 *
509 * On the large scale this function does the following:
510 *
511 *	diff = minuend - subtra
512 */
513 
514 STATIC_FUNC void
515 subTimes (diff, minuend, subtra)
516 
517 register TUSED *	diff;
518 register TMARK *	minuend;
519 register TMARK *	subtra;
520 
521 {
522 	register struct tms *	mintms;
523 	register struct tms *	subtms;
524 
525 	long	ltemp;		/* Temporary storage for long arith. */
526 	float	ticks;		/* Clock interrupts per second. */
527 
528 	if ((minuend->tm_valid != TRUE) || (subtra->tm_valid != TRUE))
529 	{				/* If data has not been collected. */
530 		diff->tu_real = NOTIME;
531 		diff->tu_user = NOTIME;
532 		diff->tu_sys = NOTIME;
533 	} else
534 	{
535 		ticks = (float) HZ;	/* HZ defined in <sys/param.h>. */
536 		mintms = &minuend->tm_cycles;
537 		subtms = &subtra->tm_cycles;
538 
539 		/* Calculate real time. */
540 
541 		ltemp = minuend->tm_real - subtra->tm_real;
542 		diff->tu_real = ((float) ltemp)/ticks;
543 
544 		/* Calculate user time. */
545 
546 		ltemp =	  mintms->tms_utime
547 			- subtms->tms_utime
548 			+ mintms->tms_cutime
549 			- subtms->tms_cutime;
550 		diff->tu_user = ((float) ltemp)/ticks;
551 
552 		/* Calculate user time. */
553 
554 		ltemp =	  mintms->tms_stime
555 			- subtms->tms_stime
556 			+ mintms->tms_cstime
557 			- subtms->tms_cstime;
558 		diff->tu_sys = ((float) ltemp)/ticks;
559 	}
560 	return;
561 }
562 
563 /*
564 *		EXTERNAL FUNCTIONS
565 */
566 
567 /*
568 * Function:	gmt - Generate Current Time String
569 *
570 * This function returns the address a string containing the current
571 * GMT in the form YYMMDDhhmmss.
572 *
573 * Parameters:
574 *
575 *	none
576 *
577 * Return:
578 *
579 *	An address of a static character array containing the date.
580 */
581 
582 char *
583 gmt()
584 
585 {
586 	static char	date[] = "YYMMDDhhmmss";
587 
588 	register struct tm *	td;
589 	time_t			now;	/* Current time. */
590 
591 	now = time((time_t *) 0);
592 	td = gmtime(&now);
593 	(void) sprintf(date, "%02d%02d%02d%02d%02d%02d",
594 				(td->tm_year % 100),
595 				td->tm_mon + 1,
596 				td->tm_mday,
597 				td->tm_hour,
598 				td->tm_min,
599 				td->tm_sec
600 		      );
601 	return date;
602 }
603 
604 
605 /*
606 * Function:	writeLog - Write String to Log File
607 *
608 * After insuring that the log file is open, this function will write
609 * the specified string to the log file.  If a write error occurs,
610 * statistics collection will be disabled.
611 *
612 * Parameters:
613 *
614 *	string - Null terminated string to be written out.
615 *	logfile - file descripter
616 *	logname - name of log file.
617 *	collecting - log enable/disable
618 */
619 
620 void
621 writeLog (string, logfile, logname, collecting)
622 
623 char *	string;
624 int *	logfile;
625 char *	logname;
626 int *	collecting;
627 
628 {
629 	register int	length;		/* Length of the string. */
630 	register int	rv;		/* Return value from write. */
631 
632 	char		errmsg[BUFSIZ];	/* Place for error messages. */
633 
634 	if (openLog(logfile,logname) != SUCCESS){
635 		*collecting = FALSE;
636 		return;
637 	}
638 	length = strlen(string);
639 	do
640 	{
641 		rv = write(*logfile, string, (unsigned) length);
642 	} while ((rv < 0) && (errno == EINTR));	/* Retry if interrupted. */
643 	if (rv < length)
644 	{				/* Error or incomplete output. */
645 		(void) sprintf(errmsg, Msg_write, logname);
646 		DEBUG(DB_IMPORTANT, errmsg, errno);
647 
648 		/* If we had a write error, lets give up on loggine. */
649 
650 		closeLog(logfile);
651 		*collecting = FALSE;
652 	}
653 	return;
654 }
655 
656 /*
657 * Function:	closeLog - Close the Log File
658 *
659 * This function allows uucico to close the log file in preparation for
660 * forking.
661 *
662 * Parameters:
663 *
664 *	log file descriptor
665 */
666 
667 void
668 closeLog (logfile)
669 int	*logfile;
670 
671 {
672 	if (*logfile != CLOSED)
673 	{
674 		(void) close(*logfile);
675 		*logfile = CLOSED;
676 	}
677 	return;
678 }
679 
680 
681 /*
682 * Function: copyText - Copy String to Dynamic Memory
683 *
684 * This function copies a string to a buffer.  It insures that there is
685 * no overflow of the buffer and that the result is null terminated.
686 *
687 * Parameters:
688 *
689 *	tptr -		address of the buffer where the string is to
690 *			be stored.
691 *
692 *	size -		number of bytes in the buffer.
693 *
694 *	string -	string to be saved.
695 *
696 * Returns:
697 *
698 *	none.
699 */
700 
701 void
702 copyText (tptr, size, string)
703 
704 register char *	tptr;
705 register int	size;
706 char *		string;
707 
708 {
709 	(void) strncpy(tptr, string, size);
710 	*(tptr + size - 1) = NULLCHAR;
711 	return;
712 }
713 
714 /*
715 * Function:	pfConnected - Report Connection Completion
716 *
717 * Uucico uses pfConnected to tell this performance package that a connection
718 * has been established with the remote system.
719 *
720 * Parameters:
721 *
722 *	remote -	name of the remote system.
723 *
724 *	device -	the type of device being used for communicaitons.
725 */
726 
727 void
728 pfConnected (remote, device)
729 
730 char *	remote;
731 char *	device;
732 
733 {
734 	register int		i;
735 	register TMARK *	tptr;
736 
737 	LOGCHECK;
738 	grabTimes(&Conn.cn_times[CT_CONNECTED]);
739 	copyText(Remote, sizeof(Remote), remote);
740 	copyText(Device, sizeof(Device), device);
741 	reportConn();
742 	tptr = &Conn.cn_times[0];
743 
744 	/*
745 	* Mark connection times as invalid.  This is really unnecessary
746 	* since there should only be one connection per invocation of uucico.
747 	* We do it for consistency with use of the transfer data.
748 	*/
749 
750 	for (i = 0; i < CT_SIZE; i++, tptr++)
751 		tptr->tm_valid = FALSE;
752 	return;
753 }
754 
755 
756 /*
757 * Function:	pfEndFile - Report End of File
758 *
759 * Uucico uses pfEndFile to tell our statistics collection package that
760 * all processing has been finished on the current file.  PfEndfile should
761 * be called after all notifications have been done and after the status
762 * file has been written.  PfEndfile writes out a xfer record for the
763 * file that just completed.
764 *
765 * Parameters:
766 *
767 *	none
768 */
769 
770 void
771 pfEndfile (breakmsg)
772 char * breakmsg;
773 {
774 	register int		i;
775 	register TMARK *	tdptr;
776 	register struct xferData *	xptr = &Xfer;
777 
778 	LOGCHECK;
779 	grabTimes(&Xfer.xf_times[XT_ENDFILE]);
780 	Xfer.xf_filedone = time((time_t *) 0);
781 	reportFile(breakmsg);
782 
783 	/* Now that we have reported them, mark all times as invalid. */
784 
785 	copyText(xptr->xf_flags, sizeof(xptr->xf_flags), NOTAVAIL);
786 	tdptr = &Xfer.xf_times[0];
787 	for (i = 0; i < XT_SIZE; i++, tdptr++)
788 		tdptr->tm_valid = FALSE;
789 	return;
790 }
791 
792 /*
793 * Function:	pfEndXfer - File Transfer Complete
794 *
795 * Calling pfEndXfer tells the performance package that a file transfer
796 * has been completed.  It should be called after the destination site
797 * closes the file and confirms receipt, but before notifications are done.
798 *
799 * Parameters:
800 *
801 *	none
802 */
803 
804 void
805 pfEndXfer ()
806 
807 {
808 	LOGCHECK;
809 	grabTimes(&Xfer.xf_times[XT_ENDXFER]);
810 	return;
811 }
812 
813 /*
814 * Function:	pfFindFile - Looking for Another File
815 *
816 * Uucico uses pfFindFile to announce that it is going to explore the
817 * queues for another file transfer to do.  PfFindFile is only called
818 * when uucico is in the role of master.
819 *
820 * Parameters:
821 *
822 *	none
823 */
824 
825 void
826 pfFindFile ()
827 
828 {
829 	LOGCHECK;
830 	grabTimes(&Xfer.xf_times[XT_LOOK]);
831 	return;
832 }
833 
834 /*
835 * Function:	pfFound - Found Another File
836 *
837 * PfFound is a counterpart of pfFindFile.  It is called when a new file
838 * has been found.  Like pfFindFile it is called only by a master uucico.
839 *
840 * Parameters:
841 *
842 *	jobid -		The name of the job that was found.
843 *
844 *	flags -		Options flags that were stored in the queue.
845 *			These flags are originally set by uucp.
846 *
847 *	intoQue -	The time that the C. file was placed in the queue.
848 */
849 
850 void
851 pfFound (jobid, flags, intoQue)
852 
853 char *	jobid;
854 char *	flags;
855 time_t	intoQue;
856 
857 {
858 	register struct xferData *	xptr = &Xfer;
859 
860 	LOGCHECK;
861 	grabTimes(&xptr->xf_times[XT_FOUND]);
862 	copyText(xptr->xf_jobname, sizeof(xptr->xf_jobname), jobid);
863   	xptr->xf_jobgrade[0] = jobid[strlen(jobid)-5];
864   	xptr->xf_jobgrade[1] = NULLCHAR;/* get job grade from jobid */
865 	copyText(xptr->xf_flags, sizeof(xptr->xf_flags), flags);
866 
867 	/* Save time that file was placed in queue and current time. */
868 
869 	xptr->xf_intoque = intoQue;
870 	xptr->xf_deque = time((time_t *) 0);
871 	return;
872 }
873 
874 /*
875 * Function:	pfInit - Initialize Performance Package
876 *
877 * This function allows the performance package to initialize its internal
878 * data structures.  It should be called one time only when uucico starts
879 * running.
880 *
881 * Parameters:
882 *
883 *	none
884 */
885 
886 void
887 pfInit ()
888 
889 {
890 	register struct xferData *	xptr = &Xfer;
891 
892 	if (Initialized == TRUE)
893 		return;
894 	Procid = getpid();
895 	myName(myname);
896 	copyText(xptr->xf_flags, sizeof(xptr->xf_flags), NOTAVAIL);
897 
898 	/*
899 	* Attempt to open the log file.  If we can't do it, then we
900 	* won't collect statistics.
901 	*/
902 
903 	if (openLog(&LogFile,LogName) == SUCCESS)
904 		Collecting = TRUE;
905 	else
906 		Collecting = FALSE;
907 	Initialized = TRUE;
908 	return;
909 }
910 
911 /*
912 * Function:	pfStrtConn - Going to Establish Connection
913 *
914 * Uucico uses pfStrtConn to announce that it is going to attempt
915 * to establish a connection.
916 *
917 * Parameters:
918 *
919 *	role -		An indication of whether uucico is currently
920 *			running in master or slave mode.  M = master,
921 *			S = slave.
922 */
923 
924 void
925 pfStrtConn (role)
926 
927 char	role;
928 {
929 	LOGCHECK;
930 	grabTimes(&Conn.cn_times[CT_START]);
931 	Conn.cn_role = role;
932 	return;
933 }
934 
935 /*
936 * Function:	pfStrtXfer - Starting File Transfer
937 *
938 * This function should be called just as the first byte of data is
939 * about to be transferred.
940 *
941 * Parameters:
942 *
943 *	role -		An indication of whether uucico is currently
944 *			running in master or slave mode.  M = master,
945 *			S = slave.
946 *
947 *	direction -	Direction of file transfer.  S = sending to
948 *			remote, R = receiving from remote.
949 */
950 
951 void
952 pfStrtXfer(role, direction)
953 
954 char	role;
955 char	direction;
956 
957 {
958 	register struct xferData *	xptr = &Xfer;
959 
960 	LOGCHECK;
961 	grabTimes(&xptr->xf_times[XT_BEGXFER]);
962 	xptr->xf_role = role;
963 	xptr->xf_direction = direction;
964 	return;
965 }
966 
967 /*
968 	A protocol which both master and slave sides agree on
969 */
970 
971 void
972 pfPtcl(str)
973 char 	*str;
974 {
975 	strcpy(Protocol,str);
976 	return;
977 }
978 
979 /*
980 * Function:	openLog	 - Open the Log File
981 *
982 * If the log file is already open this function immediately returns
983 * success.  Otherwise, an attempt is made to open the logfile in append
984 * mode.
985 *
986 * Parameters:
987 *
988 *	logfile - file descripter
989 *	logname - name of log file.
990 *
991 * Returns:
992 *
993 *	SUCCESS -	The log file is open.
994 *	FAIL -		Unable to open logfile.
995 */
996 
997 int
998 openLog (logfile,logname)
999 int	*logfile;
1000 char	*logname;
1001 {
1002 	register int	fd;		/* File descriptor of log file. */
1003 
1004 	int		level;		/* Level for debug message. */
1005 	char		msgbuf[BUFSIZ];
1006 
1007 	/* See if file already open. */
1008 
1009 	if (*logfile != CLOSED)
1010 		return (SUCCESS);
1011 
1012 	/* Attempt to open the file. */
1013 
1014 	DEBUG(DB_TRACE, Msg_opening, logname);
1015 	do
1016 	{
1017 		fd = open(logname, O_WRONLY | O_APPEND);
1018 	} while ((fd < 0) && (errno == EINTR)); /* Retry if interrupted. */
1019 	if (fd < 0) {	/* Error on open. */
1020 		(void) sprintf(msgbuf, Msg_badopen, logname);
1021 		if (errno == ENOENT)
1022 			level = DB_DETAIL; /* If the file is not there
1023 					    *   it will usually mean
1024 					    *   that the SA doesn't
1025 					    *   want to collect
1026 					    *   statisitcs. */
1027 		else
1028 			level = DB_IMPORTANT;	/* Unexpected error */
1029 		DEBUG(level, msgbuf, errno); /* No log file. */
1030 		return FAIL;
1031 	} else {
1032 		*logfile = fd;
1033 		return SUCCESS;
1034 	}
1035 }
1036 
1037 #ifdef BSD4_2
1038 #include <sys/time.h>
1039 #include <sys/times.h>
1040 #include <sys/resource.h>
1041 
1042 static clock_t
1043 scale60(tvp)
1044 	register struct timeval *tvp;
1045 {
1046 	return (tvp->tv_sec * 60 + tvp->tv_usec / 16667);
1047 }
1048 
1049 clock_t
1050 times(tmsp)
1051 	register struct tms *tmsp;
1052 {
1053 	struct rusage ru;
1054 	struct timeval now;
1055 	static time_t epoch;
1056 
1057 	if (getrusage(RUSAGE_SELF, &ru) < 0)
1058 		return (clock_t)(-1);
1059 	tmsp->tms_utime = scale60(&ru.ru_utime);
1060 	tmsp->tms_stime = scale60(&ru.ru_stime);
1061 	if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1062 		return (clock_t)(-1);
1063 	tmsp->tms_cutime = scale60(&ru.ru_utime);
1064 	tmsp->tms_cstime = scale60(&ru.ru_stime);
1065 	if (gettimeofday(&now, (struct timezone *)0) < 0)
1066 		return (clock_t)(-1);
1067 	if (epoch == 0)
1068 		epoch = now.tv_sec;
1069 	now.tv_sec -= epoch;
1070 	return (scale60(&now));
1071 }
1072 #endif /* BSD4_2 */
1073