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