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