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 32 /* 33 * 34 * ct [-h] [-v] [-w n] [-x n] [-s speed] telno ... 35 * 36 * dials the given telephone number, waits for the 37 * modem to answer, and initiates a login process. 38 * 39 * ct uses several routines from uucp: 40 * - getto(flds) takes a vector of fields needed to make 41 * a connection and returns a file descriptor or -1 42 * - rddev( ... ) takes several arguments and returns lines 43 * from the /etc/uucp/Devices that match the type 44 * (in ct the type will be ACU) 45 * - fdig(string) takes a string that is zero or more 46 * alphabetic characters follow by a number (baud rate) 47 * and returns a pointer to the first digit in the string. 48 * - fn_cklock(dev) takes a device name [/dev/]term/11 and 49 * checks whether the appropriate lock file exists. It returns 50 * FAIL if it does. 51 * - rmlock(pointer) removes the lock file. In ct pointer is 52 * always CNULL (a null pointer) causing rmlock to remove 53 * all lock files associated with this execution of ct. 54 */ 55 56 #include "uucp.h" 57 #include "sysfiles.h" 58 #include <pwd.h> 59 #include <utmpx.h> 60 61 #ifdef DATAKIT 62 #include <dk.h> 63 extern int dkminor(); 64 #endif 65 66 #define ROOT 0 67 #define SYS 3 68 #define TTYGID (gid_t) 7 /* group id for terminal */ 69 #define TTYMOD (mode_t) 0622 70 #define DEV "/dev/" 71 #define TELNOSIZE 32 /* maximum phone # size is 31 */ 72 #define LEGAL "0123456789-*#=" 73 #define USAGE "[-h] [-v] [-w n] [-x n] [-s speed] telno ..." 74 #define LOG "/var/adm/ctlog" 75 #define TTYMON "/usr/lib/saf/ttymon" 76 #define TRUE 1 77 #define FALSE 0 78 79 int Dologin = 0; 80 static 81 int _Status; /* exit status of child */ 82 83 static 84 pid_t _Pid = 0; /* process id of child */ 85 86 static 87 char 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 92 static 93 char 94 *_Num, /* pointer to a phone number */ 95 *_Flds[7]; /* Filled in as if finds() in uucp did it */ 96 97 static 98 time_t _Log_on, 99 _Log_elpsd; 100 101 static 102 FILE *_Fdl; 103 104 extern int optind; 105 extern char *optarg, *fdig(); 106 extern void cleanup(); 107 extern struct passwd *getpwuid (); 108 109 extern int getto(), rddev(); 110 static int gdev(), logproc(), exists(); 111 static 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*/ 121 void 122 assert (s1, s2, i1, s3, i2) 123 char *s1, *s2, *s3; 124 int i1, i2; 125 { } /* for ASSERT in gnamef.c */ 126 127 /*ARGSUSED*/ 128 void 129 logent (s1, s2) 130 char *s1, *s2; 131 { } /* so we can load ulockf() */ 132 133 jmp_buf Sjbuf; /* used by uucp routines */ 134 135 int 136 main (argc, argv) 137 int argc; 138 char *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 495 static void 496 disconnect (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 */ 527 void 528 cleanup (code) 529 int 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 560 static int 561 gdev (flds) 562 char *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 594 static int 595 logproc(line) 596 char *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 */ 622 static void 623 startat () 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 674 static void 675 stopat (num) 676 char *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 753 static int 754 exists (file) 755 char *file; 756 { 757 struct stat statb; 758 759 if (stat (file, &statb) == -1 && errno == ENOENT) 760 return (0); 761 return (1); 762 } 763 764 static void 765 zero (adr, size) 766 char *adr; 767 int size; 768 { 769 while (size--) 770 *adr++ = '\0'; 771 return; 772 } 773