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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28/*
29 * Copyright (c) 2016 by Delphix. All rights reserved.
30 */
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32
33/*
34 *
35 *	ct [-h] [-v] [-w n] [-x n] [-s speed] telno ...
36 *
37 *	dials the given telephone number, waits for the
38 *	modem to answer, and initiates a login process.
39 *
40 *	ct uses several routines from uucp:
41 *	- getto(flds) takes a vector of fields needed to make
42 *	  a connection and returns a file descriptor or -1
43 *	- rddev( ... ) takes several arguments and returns lines
44 *	  from the /etc/uucp/Devices that match the type
45 *	  (in ct the type will be ACU)
46 *	- fdig(string) takes a string that is zero or more
47 *	  alphabetic characters follow by a number (baud rate)
48 *	  and returns a pointer to the first digit in the string.
49 *	- fn_cklock(dev) takes a device name [/dev/]term/11 and
50 *	  checks whether the appropriate lock file exists. It returns
51 *	  FAIL if it does.
52 *	- rmlock(pointer) removes the lock file.  In ct pointer is
53 *	  always CNULL (a null pointer) causing rmlock to remove
54 *	  all lock files associated with this execution of ct.
55 */
56
57#include "uucp.h"
58#include "sysfiles.h"
59#include <pwd.h>
60#include <utmpx.h>
61
62#ifdef DATAKIT
63#include <dk.h>
64extern int dkminor();
65#endif
66
67#define ROOT	0
68#define SYS	3
69#define TTYGID	(gid_t) 7		/* group id for terminal */
70#define TTYMOD	(mode_t) 0622
71#define DEV	"/dev/"
72#define TELNOSIZE	32		/* maximum phone # size is 31 */
73#define LEGAL	"0123456789-*#="
74#define USAGE	"[-h] [-v] [-w n] [-x n] [-s speed] telno ..."
75#define LOG	"/var/adm/ctlog"
76#define	TTYMON	"/usr/lib/saf/ttymon"
77#define TRUE	1
78#define FALSE	0
79
80static
81int	_Status;		/* exit status of child */
82
83static
84pid_t	_Pid = 0;		/* process id of child */
85
86static
87char
88	_Tty[sizeof DEV+12] = "",  /* /dev/term/xx for connection device */
89	*_Dev[D_MAX + 1],	/* Filled in by rddev and used globally */
90	_Devbuf[BUFSIZ];	/* buffer for rddev */
91
92static
93char
94	*_Num,			/* pointer to a phone number */
95	*_Flds[7];		/* Filled in as if finds() in uucp did it */
96
97static
98time_t	_Log_on,
99	_Log_elpsd;
100
101static
102FILE	*_Fdl;
103
104extern int  optind;
105extern char *optarg, *fdig();
106extern  void cleanup();
107extern struct passwd  *getpwuid ();
108
109extern int getto(), rddev();
110static int gdev(), logproc(), exists();
111static void startat(), stopat(), disconnect(), zero();
112
113/*
114 * These two dummy routines are needed because the uucp routines
115 * used by ct reference them, but they will never be
116 * called when executing from ct
117 */
118
119/*VARARGS*/
120/*ARGSUSED*/
121void
122assert (s1, s2, i1, s3, i2)
123char *s1, *s2, *s3;
124int i1, i2;
125{ }		/* for ASSERT in gnamef.c */
126
127/*ARGSUSED*/
128void
129logent (s1, s2)
130char *s1, *s2;
131{ }		/* so we can load ulockf() */
132
133jmp_buf Sjbuf;			/* used by uucp routines */
134
135int
136main (argc, argv)
137int argc;
138char   *argv[];
139{
140    int    	c;
141    int		found = 0,
142		errors = 0,
143		first = TRUE;
144    int     count,
145	    logprocflag,	/* is there a login process on the line */
146            hangup = 1,		/* hangup by default */
147            minutes = 0;	/* number of minutes to wait for dialer */
148    int     fdl;
149    struct termio   termio;
150    typedef void (*save_sig)();
151    save_sig	save_hup,
152		save_quit,
153		save_int;
154    extern void	setservice(), devreset();
155    extern int sysaccess();
156
157    save_hup = signal (SIGHUP, cleanup);
158    save_quit = signal (SIGQUIT, cleanup);
159    save_int = signal (SIGINT, cleanup);
160    (void) signal (SIGTERM, cleanup);
161    (void) strcpy (Progname, "ct");
162
163    setservice("cu");
164    if ( sysaccess(EACCESS_DEVICES) != 0 ) {
165	(void) fprintf(stderr, "ct: can't access Devices file\n");
166	cleanup(101);
167    }
168
169    /* Set up the _Flds vector as if finds() [from uucico] built it */
170    _Flds[F_NAME] = "dummy";		/* never used */
171    _Flds[F_TIME] = "Any";		/* never used */
172    _Flds[F_TYPE] = "ACU";
173    _Flds[F_CLASS] = "1200";		/* default at 1200 */
174    _Flds[F_PHONE] = "";			/* filled in by arguments */
175    _Flds[F_LOGIN] = "";			/* never used */
176    _Flds[6] = NULL;
177
178    while ((c = getopt (argc, argv, "hvw:s:x:")) != EOF) {
179	switch (c) {
180	    case 'h':
181		hangup = 0;
182		break;
183
184	    case 'v':
185		Verbose = 1;
186		break;
187
188	    case 'w':
189		minutes = atoi (optarg);
190		if (minutes < 1) {
191		    (void) fprintf(stderr,
192			"\tusage: %s %s\n", Progname, USAGE);
193		    (void) fprintf(stderr, "(-w %s) Wait time must be > 0\n",
194			optarg);
195		    cleanup(101);
196		}
197		break;
198
199	    case 's':
200		_Flds[F_CLASS] = optarg;
201		break;
202
203	    case 'x':
204		Debug = atoi(optarg);
205		if (Debug < 0 || Debug > 9) {
206		    (void) fprintf(stderr,
207			"\tusage: %s %s\n", Progname, USAGE);
208		    (void) fprintf(stderr, "(-x %s) value must be 0-9\n",
209			optarg);
210		    cleanup(101);
211		}
212		break;
213
214	    case '?':
215		(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
216		cleanup(101);
217		/* NOTREACHED */
218	}
219    }
220
221    if (optind == argc) {
222	(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
223	(void) fprintf(stderr, "No phone numbers specified!\n");
224	cleanup(101);
225    }
226
227    /* check for valid phone number(s) */
228    for (count = argc - 1; count >= optind; --count) {
229	_Num = argv[count];
230	if (strlen(_Num) >= (size_t)(TELNOSIZE - 1)) {
231	    (void) fprintf(stderr, "ct: phone number too long -- %s\n", _Num);
232	    ++errors;
233	}
234	if ((int)strspn(_Num, LEGAL) < (int)strlen(_Num)) {
235	    (void) fprintf(stderr, "ct: bad phone number -- %s\n", _Num);
236	    ++errors;
237	}
238    }
239    if (errors)
240	cleanup(101);
241
242    /************************************************************/
243    /*		Begin Loop:  Find an available Dialer		*/
244    /************************************************************/
245    for (count = 0;; count++) { /* count will be wait time after first
246				 * time through the loop.
247				 * break will be used exit loop.
248				 */
249	if ( (found = gdev (_Flds)) > 0) {  /* found a dialer */
250	    (void) fprintf(stdout, "Allocated dialer at %s baud\n",
251		_Flds[F_CLASS]);
252	    break;
253	}
254	else if (found == 0) {	/* no dialers of that on system */
255	    (void) fprintf(stdout, "No %s dialers on this system\n",
256		fdig(_Flds[F_CLASS]) );
257    	    cleanup(101);
258	}
259
260	if (!first) { /* not the first time in loop */
261	    VERBOSE("%s busy", (found == -1) ? "Dialer is" : "Dialers are");
262	    VERBOSE(" (%d minute(s))\n", count);
263	    if (count < minutes) {
264	        sleep(60);
265	        continue;
266	    }
267	    /* This is the end of the loop - no time left */
268	    break;
269	}
270
271	/**************************************************************/
272	/* First time through loop - get wait minutes if no -w option */
273	/**************************************************************/
274	first = FALSE;
275	(void) fprintf(stdout, "The (%d) %s dialer%s busy\n", -found,
276	    _Flds[F_CLASS], (found == -1 ? " is" : "s are"));
277	if (minutes) {	/* -w already set wait minutes */
278	    (void) fprintf(stdout, "Waiting for %d minute%s\n", minutes,
279		(minutes > 1 ? "s" : "") );
280	    sleep(60);
281	    continue;
282	}
283
284	if (!isatty(0) )  {  /* not a terminal - get out */
285	    cleanup(101);
286	}
287
288	/* Ask user if they want to wait */
289	(void) fputs("Do you want to wait for dialer? (y for yes): ", stdout);
290	if ((c = getchar ()) == EOF || tolower (c) != 'y')
291	    cleanup(101);
292	while ( (c = getchar()) != EOF && c != '\n')
293	    ;
294
295	(void) fputs ("Time, in minutes? ", stdout);
296	(void) scanf ("%d", &minutes);
297	while ( (c = getchar()) != EOF && c != '\n')
298	    ;
299
300	if (minutes <= 0)
301	    cleanup(101);
302
303	(void) fputs ("Waiting for dialer\n", stdout);
304	sleep(60);
305	continue;
306
307    }
308    /************************************************************/
309    /*		End Loop:  Find an available Dialer		*/
310    /************************************************************/
311
312    /* check why loop terminated */
313    if (found < 0) {	/* no dialer found - get out */
314        (void) fputs("*** TIMEOUT ***\n", stdout);
315        cleanup(101);
316    }
317
318    (void) signal(SIGHUP, SIG_IGN);
319    /* found a dialer. now try to call */
320    if (!isatty(0))
321        hangup = 0;
322
323    if (hangup) {  /* -h option not specified */
324	do {
325            (void) fputs ("Confirm hang-up? (y/n): ", stdout);
326	    switch (c=tolower(getchar())) {
327	   case EOF:
328	   case 'n':
329		    cleanup(101);
330		    break;
331	   case 'y':
332		    break;
333	    default:
334		    while ( c != EOF && c != '\n' )
335			c=getchar();
336		    break;
337	    }
338	} while (c != 'y');
339
340	/* close stderr if it is not redirected */
341        if ( isatty(2) ) {
342            Verbose = 0;
343	    Debug = 0;
344            (void) close (2);
345	}
346
347	(void) ioctl (0, TCGETA, &termio);
348        termio.c_cflag = 0;	/* speed to zero for hangup */
349        (void) ioctl (0, TCSETAW, &termio);  /* hang up terminal */
350        (void) sleep (5);
351    }
352    (void) close(0);
353    (void) close(1);
354
355    /* Try each phone number until a connection is made, or non work */
356    for (count = optind; count < argc; count++) {
357	/* call getto routine to make connection */
358	_Flds[F_PHONE] = argv[count];
359	rmlock(CNULL);	/* remove temporary lock set by gdev */
360	devreset();
361	fdl = getto(_Flds);
362	if (fdl >= 0) {
363	    /*
364	     * If there is a login process on the line, get rid
365	     * of the lock file quickly so that when the process
366	     * reads the first character, the lock file will be gone
367	     * indicating that the process should handle the data.
368	     */
369	    if ( (logprocflag = logproc(Dc)) ) /* really an assignment! */
370		rmlock(CNULL);
371
372	    _Fdl = fdopen(fdl, "r+");
373	    (void) sprintf(_Tty, "%s%s", DEV, Dc);
374	    /* NOTE:  Dc is set in the caller routines */
375	    break;
376	}
377    }
378
379    /* check why the loop ended (connected or no more numbers to try) */
380    if (count == argc)
381	cleanup(101);
382
383    /****** Successfully made connection ******/
384    VERBOSE("Connected\n%s", "");
385
386#ifdef	DATAKIT
387 	if (!strcmp(_Dev[D_CALLER], "DK")) {
388 		strcpy(_Tty, dtnamer(dkminor(fdl)));
389 		strcpy(Dc, (strrchr(_Tty, '/')+1));
390 		if ((_Fdl = fopen(_Tty, "r+")) == NULL) {
391 			(void) fprintf(stderr, "ct: Cannot open %s, errno %d\n",
392 				_Tty, errno);
393 			cleanup(101);
394 		}
395 	}
396#endif
397
398    /* ignore some signals if they were ignored upon invocation of ct */
399    /* or else, have them go to graceful disconnect */
400    if (save_hup == SIG_IGN)
401	(void) signal (SIGHUP, SIG_IGN);
402    else
403	(void) signal (SIGHUP, disconnect);
404
405    if (save_quit == SIG_IGN)
406	(void) signal (SIGQUIT, SIG_IGN);
407    else
408	(void) signal (SIGQUIT, disconnect);
409
410    if (save_int == SIG_IGN)
411	(void) signal (SIGINT, SIG_IGN);
412    else
413	(void) signal (SIGINT, disconnect);
414
415    (void) signal (SIGTERM, disconnect);
416    (void) signal (SIGALRM, disconnect);
417
418    (void) sleep (2);		/* time for phone line/modem to settle */
419
420    _Log_on = time ((time_t *) 0);
421
422    /*
423     * if there is a login process on this line,
424     * tell the user to hit a carriage return to make
425     * the waiting process get past the inital read,
426     * Then exit.
427     */
428    if (logprocflag) {	/* there is a login process on the line */
429	(void) fputs("Hit carriage return ", _Fdl);
430	(void) fclose(_Fdl);
431	CDEBUG(4, "there is a login process; exit\n%s", "");
432	exit(0);
433    }
434
435    CDEBUG(4, "start login process (%s ", TTYMON);
436    CDEBUG(4, "-g -h -t 60 -l %s)\n", fdig(_Flds[F_CLASS]));
437    for (;;) {
438	pid_t w_ret;
439	switch(_Pid = fork()) {
440	case -1:	/* fork failed */
441	    if ((!hangup || Verbose))
442		(void) fputs ("ct: can't fork for login process\n", stderr);
443	    cleanup(101);
444	    /*NOTREACHED*/
445
446	case 0:		/* child process */
447	    startat ();
448	    (void) close(2);
449	    /* ttymon will use open fd 0 for connection */
450	    if ( fdl != 0 ) {
451		(void) close(0);
452		dup(fdl);
453	    }
454	    (void) signal(SIGHUP, SIG_DFL);  /* so child will exit on hangup */
455	    (void) execl(TTYMON, "ttymon", "-g", "-h", "-t", "60",
456			"-l", fdig(_Flds[F_CLASS]), (char *) 0);
457	    /* exec failed */
458	    cleanup(101);
459	    /*NOTREACHED*/
460
461	default:	/* parent process */
462	    break;
463	}
464
465	/* Parent process */
466
467	while ((w_ret = wait(&_Status)) != _Pid)
468	    if (w_ret == -1 && errno != EINTR) {
469		VERBOSE("ct: wait failed errno=%d\n", errno);
470		cleanup(101);
471	    }
472	if ((_Status & 0xff00) < 0) {
473	    if (!hangup)
474		VERBOSE("ct: can't exec login process\n%s", "");
475	    cleanup(101);
476	}
477
478	stopat(_Flds[F_PHONE]);
479
480        rewind (_Fdl);	/* flush line */
481        (void) fputs ("\nReconnect? ", _Fdl);
482
483        rewind (_Fdl);
484        (void) alarm (20);
485        c = getc (_Fdl);
486
487        if (c == EOF || tolower (c) == 'n')
488	    disconnect (0);	/* normal disconnect */
489        while ( (c = getc(_Fdl)) != EOF && c != '\n')
490    	    ;
491        (void) alarm (0);
492    }
493}
494
495static void
496disconnect (code)
497{
498    struct termio   termio;
499
500    (void) alarm(0);
501    (void) signal (SIGALRM, SIG_IGN);
502    (void) signal (SIGINT, SIG_IGN);
503    (void) signal (SIGTERM, SIG_IGN);
504
505    _Log_elpsd = time ((time_t *) 0) - _Log_on;
506
507    (void) ioctl (fileno(_Fdl), TCGETA, &termio);
508    termio.c_cflag = 0;				/* speed to zero for hangup */
509    (void) ioctl (fileno(_Fdl), TCSETAW, &termio);  /* hang up terminal */
510    (void) fclose (_Fdl);
511
512    DEBUG(5, "Disconnect(%d)\n", code);
513    VERBOSE("Disconnected\n%s", "");
514
515    /* For normal disconnect or timeout on "Reconnect?" message,
516       we already cleaned up above */
517
518    if ((code != 0) && (code != SIGALRM))
519	stopat(_Flds[F_PHONE]);
520
521    cleanup(code);
522}
523
524/*
525 * clean and exit with "code" status
526 */
527void
528cleanup (code)
529int    code;
530{
531    CDEBUG(5, "cleanup(%d)\n", code);
532    rmlock (CNULL);
533    if (*_Tty != '\0') {
534	CDEBUG(5, "chmod/chown %s\n", _Tty);
535	if (chown(_Tty , UUCPUID, TTYGID) < 0 ) {
536	    CDEBUG(5, "Can't chown to uid=%u, ", UUCPUID);
537	    CDEBUG(5, "gid=%u\n", TTYGID);
538	}
539	if (chmod(_Tty , TTYMOD) < 0) {
540	    CDEBUG(5, "Can't chmod to %lo\n", (unsigned long) TTYMOD);
541	}
542    }
543    if (_Pid) { /* kill the child process */
544	(void) signal(SIGHUP, SIG_IGN);
545	(void) signal(SIGQUIT, SIG_IGN);
546	(void) kill (_Pid, SIGKILL);
547    }
548    exit (code);
549}
550
551/*	gdev()
552 * Find an available line with a dialer on it.
553 * Set a temporary lock file for the line.
554 * Return:
555 *	>0 - got a dialer
556 *	<0 - failed - return the number of possible dialers
557 *	0 - not dialers of requested class on the system.
558 */
559
560static int
561gdev (flds)
562char   *flds[];
563{
564    int	count = 0;
565    extern void	devreset();
566
567    devreset();
568    while (rddev ("ACU", _Dev, _Devbuf, D_MAX) != FAIL) {
569	/* check caller type */
570	if (!EQUALS (flds[F_TYPE] /* "ACU" */, _Dev[D_TYPE]))
571	    continue;
572	/* check class, check (and possibly set) speed */
573	if (!EQUALS (flds[F_CLASS] /* speed */, _Dev[D_CLASS]))
574	    continue;
575	count++;
576
577	if (fn_cklock(_Dev[D_LINE]) == FAIL)
578	    continue;
579
580	/* found available dialer and set temporary lock */
581	return (count);
582
583    }
584    return (- count);
585}
586
587/*
588 * Check if there is a login process active on this line.
589 * Return:
590 *	0 - there is no login process on this line
591 *	1 - found a login process on this line
592 */
593
594static int
595logproc(line)
596char *line;
597{
598    struct utmpx   *u;
599
600    while ((u = getutxent()) != NULL) {
601	if (u->ut_type == LOGIN_PROCESS
602	    && EQUALS(u->ut_line, line)
603	    && EQUALS(u->ut_user, "LOGIN") ) {
604		CDEBUG(7, "ut_line %s, ", u->ut_line);
605		CDEBUG(7, "ut_user %s, ", u->ut_user);
606		CDEBUG(7, "ut_id %.4s, ", u->ut_id);
607		CDEBUG(7, "ut_pid %d\n", u->ut_pid);
608
609		/* see if the process is still active */
610		if (kill(u->ut_pid, 0) == 0 || errno == EPERM) {
611		    CDEBUG(4, "process still active\n%s", "");
612		    return(1);
613		}
614	}
615    }
616    return(0);
617}
618
619/*
620 * Create an entry in utmpx file if one does not already exist.
621 */
622static void
623startat ()
624{
625    struct utmpx utmpxbuf, *u;
626    int fd;
627
628/*	Set up the prototype for the utmpx structure we want to write.	*/
629
630    u = &utmpxbuf;
631    zero (&u -> ut_user[0], sizeof (u -> ut_user));
632    zero (&u -> ut_line[0], sizeof (u -> ut_line));
633
634/*	Fill in the various fields of the utmpx structure.		*/
635
636    u -> ut_id[0] = 'c';
637    u -> ut_id[1] = 't';
638    u -> ut_id[2] = _Tty[strlen(_Tty)-2];
639    u -> ut_id[3] = _Tty[strlen(_Tty)-1];
640    u -> ut_pid = getpid ();
641
642    u -> ut_exit.e_termination = 0;
643    u -> ut_exit.e_exit = 0;
644    u -> ut_type = INIT_PROCESS;
645    time (&u -> ut_xtime);
646    setutxent ();		/* Start at beginning of utmpx file. */
647
648/*	For INIT_PROCESSes put in the name of the program in the	*/
649/*	"ut_user" field.						*/
650
651    strncpy (&u -> ut_user[0], "ttymon", sizeof (u -> ut_user));
652    strncpy (&u -> ut_line[0], Dc, sizeof (u -> ut_line));
653
654/*	Write out the updated entry to utmpx file.			*/
655    pututxline (u);
656
657/*	Now attempt to add to the end of the wtmpx file.  Do not create	*/
658/*	if it doesn't already exist. Do not overwrite any info already	*/
659/*	in file.							*/
660
661    if ((fd = open(WTMPX_FILE, O_WRONLY | O_APPEND)) != -1) {
662	(void) write(fd, u, sizeof(*u));
663	(void) close(fd);
664    }
665    endutxent ();
666    return;
667}
668
669/*
670 * Change utmpx file entry to "dead".
671 * Make entry in ct log.
672 */
673
674static void
675stopat (num)
676char   *num;
677{
678    struct utmpx utmpxbuf, *u;
679    int fd;
680    FILE * fp;
681
682/*	Set up the prototype for the utmpx structure we want to write.	*/
683
684    setutxent();
685    u = &utmpxbuf;
686    zero (&u -> ut_user[0], sizeof (u -> ut_user));
687    zero (&u -> ut_line[0], sizeof (u -> ut_line));
688
689/*	Fill in the various fields of the utmpx structure.		*/
690
691    u -> ut_id[0] = 'c';
692    u -> ut_id[1] = 't';
693    u -> ut_id[2] = _Tty[strlen(_Tty)-2];
694    u -> ut_id[3] = _Tty[strlen(_Tty)-1];
695    u -> ut_pid = (pid_t) _Pid;
696    u -> ut_type = USER_PROCESS;
697
698/*	Find the old entry in the utmpx file with the user name and	*/
699/*	copy it back.							*/
700
701    if (u = getutxid (u)) {
702	utmpxbuf = *u;
703	u = &utmpxbuf;
704    }
705
706    u -> ut_exit.e_termination = _Status & 0xff;
707    u -> ut_exit.e_exit = (_Status >> 8) & 0xff;
708    u -> ut_type = DEAD_PROCESS;
709    time (&u -> ut_xtime);
710
711/*	Write out the updated entry to utmpx file.			*/
712
713    pututxline (u);
714
715/*	Now attempt to add to the end of the wtmpx file.  Do not create	*/
716/*	if it doesn't already exist. Do not overwrite any info already	*/
717/*	in file.							*/
718
719    if ((fd = open(WTMPX_FILE, O_WRONLY | O_APPEND)) != -1) {
720	(void) write(fd, u, sizeof(*u));
721	(void) close(fd);
722    }
723    endutxent ();
724
725/*	Do the log accounting 					*/
726
727    if (exists (LOG) && (fp = fopen (LOG, "a")) != NULL) {
728	char   *aptr;
729	int     hrs,
730	        mins,
731	        secs;
732
733 	/* ignore user set TZ for logfile purposes */
734	if ( (aptr = getenv ("TZ")) != NULL )
735		*aptr = '\0';
736
737	(aptr = ctime (&_Log_on))[16] = '\0';
738	hrs = _Log_elpsd / 3600;
739	mins = (_Log_elpsd %= 3600) / 60;
740	secs = _Log_elpsd % 60;
741	(void) fprintf(fp, "%-8s ", getpwuid (getuid ()) -> pw_name);
742	(void) fprintf(fp, "(%4s)  %s ", fdig(_Flds[F_CLASS]), aptr);
743	if (hrs)
744	    (void) fprintf(fp, "%2d:%.2d", hrs, mins);
745	else
746	    (void) fprintf(fp, "   %2d", mins);
747	(void) fprintf(fp, ":%.2d  %s\n", secs, num);
748	(void) fclose (fp);
749    }
750    return;
751}
752
753static int
754exists (file)
755char   *file;
756{
757    struct stat statb;
758
759    if (stat (file, &statb) == -1 && errno == ENOENT)
760	return (0);
761    return (1);
762}
763
764static void
765zero (adr, size)
766char  *adr;
767int    size;
768{
769    while (size--)
770	*adr++ = '\0';
771    return;
772}
773