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