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