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