home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume2 / 3b1-status-line / newmgr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-07  |  16.9 KB  |  670 lines

  1. /*    newmgr:  Loop forever, printing a status display and checking the
  2.     Suspend, Resume and shifted Function Keys.  Meant to be used on the
  3.     top line of a Unix-PC (7300/3B1) screen that has had the Phone Manager,
  4.     Status Manager and Window Manager disabled.  What it shows:
  5.  
  6.     - Phone line states
  7.         o Idle - completely free line
  8.         o Ansr - computer answering line (getty job)
  9.         o Mach - incoming machine
  10.         o User - incoming user
  11.         o Call - any outbound
  12.     - Boot date and time
  13.     - Current run level
  14.     - Current date and time
  15.     - Number of users
  16.  
  17.     When Suspend is hit, it makes the next window current; Resume
  18.     makes the last window current.
  19.  
  20.     The following functions are mapped to the function keys:
  21.  
  22.     Shift-F1:  process display, similar to 'ps -ef'
  23.     Shift-F2:  utmp display, like 'who'
  24.     Shift-F3:  lists UUCP LCK.. files, if any
  25.     Shift-F4:  directory of /usr/mail
  26.     Shift-F5:  Root filesystem freespace and free inodes
  27.     Shift-F6:  lists UUCP C. files, if any
  28.     Shift-F7:  lists UUCP X. files, if any
  29.     Shift-F8:  directory of /usr/spool/uucp
  30.  
  31.     This is started by startmgr (which should be included in any
  32.     distribution) so that the fork/exec/open code doesn't have to be
  33.     carried around with this program.
  34.  
  35.     The process table stuff is heavily inspired by the 'fuser.c' program
  36.     posted to the Usenet by Michael 'Ford' Ditto.
  37.  
  38.     This software is Copyright (c) 1987 by Scott Hazen Mueller.
  39.  
  40.     Permission is hereby granted to copy, reproduce, redistribute or
  41.     otherwise use this software as long as: there is no monetary
  42.     profit gained specifically from the use or reproduction or this
  43.     software, it is not sold, rented, traded or otherwise marketed, and
  44.     this copyright notice is included prominently in any copy
  45.     made.
  46.  
  47.     The author make no claims as to the fitness or correctness of
  48.     this software for any use whatsoever, and it is provided as is. 
  49.     Any use of this software is at the user's own risk.
  50.  
  51.     (Copyright notice courtesy of News 2.11 :-)
  52.  
  53.     Additionally:  you break it, you bought it.  I've listed the problems
  54.     that I know of in comments in the code; if you come up with fixes, I'd
  55.     like to see them, but I'm not planning on supporting anything.  It's
  56.     "good enough"; that's all that I'm looking for.    */
  57.  
  58. #include    <stdio.h>
  59. #include    <sys/types.h>
  60. #include    <sys/phone.h>
  61. #include    <sys/window.h>
  62. #include    <sys/signal.h>
  63. #include    <sys/filsys.h>
  64. #include    <sys/proc.h>
  65. #include    <sys/user.h>
  66. #include    <sys/syslocal.h>
  67. #include    <sys/tune.h>
  68. #include    <sys/stat.h>
  69. #include    <fcntl.h>
  70. #include    <time.h>
  71. #include    <utmp.h>
  72. #include    <nlist.h>
  73. #include    <pwd.h>
  74. #include    "ndir.h"
  75.  
  76. #define        PHONE1        "ph0"
  77. #define        PHONE2        "ph1"
  78. #define        IDLE        0
  79. #define        ANSR        1
  80. #define        MACH        2
  81. #define        USER        3
  82. #define        CALL        4
  83. #define        BARFORM        "%s1 %s2 | Up Since %s | %s | %s | %d Users | w%d \r"
  84. #define        MACHLEN        15
  85. #define        MAXPATHLEN    255
  86. #define        LOCKDIR        "/usr/spool/uucp/"
  87. #define        MAILDIR        "/usr/mail/"
  88. #define        DEVDIR        "/dev/"
  89. #define        LOCKPREFIX    "LCK.."
  90. #define        COMPREFIX    "C."
  91. #define        UUXPREFIX    "X."
  92. #define        TRUE        (1)
  93. #define        FALSE        (0)
  94. #define        TIMEFORM    "%.2d/%.2d %.2d:%.2d"
  95. #define        TICK        15
  96. #define        WHOSLEEP    15
  97. #define        NOWHERE        0
  98. #define        FORWARD        1
  99. #define        BACKWARD    2
  100. #define        HERE        3
  101. #define        MINWIN        1
  102. #define        MAXWIN        12
  103.  
  104. char    *phstat[5] = { "Idle", "Ansr", "Mach", "User", "Call" };
  105.  
  106. main()
  107. {
  108. int        nusers;
  109. char    curtime[26], boottime[26], rl[12], line[80], *fmttime();
  110. struct    utmp *ut, *getutent();
  111. long    temp;
  112.  
  113. /*    Ignore keyboard interrupt signals.    */
  114.  
  115. signal( SIGINT, SIG_IGN );
  116. signal( SIGQUIT, SIG_IGN );
  117.  
  118. /*    Open up the utmp file and find the boot time; save for display.    */
  119.  
  120. while ( ( ut = getutent() ) != NULL )
  121.     if ( (int) ut->ut_type == BOOT_TIME ) {
  122.         strcpy( boottime, fmttime( localtime( &( ut->ut_time ) ) ) );
  123.         break;
  124.     }
  125. endutent();
  126.  
  127. for (;;) {
  128.  
  129. /*    Scan the utmp file, noting the run level and totting up users.    */
  130.  
  131.     nusers = 0;
  132.     while ( ( ut = getutent() ) != NULL ) {
  133.         switch ( (int) ut->ut_type ) {
  134.             case USER_PROCESS : nusers++; break;
  135.             case RUN_LVL : strcpy( rl, ut->ut_line ); break;
  136.             default : break;
  137.             }
  138.         }
  139.     endutent();
  140.  
  141. /*    Figure out the current time.  Temp is needed 'cause localtime wants
  142.     an address.    */
  143.  
  144.     temp = time( (long *) 0 );
  145.     strcpy( curtime, fmttime( localtime( &temp ) ) );
  146.  
  147. /*    Format and print.  Flush stdout to make it really get printed.    */
  148.  
  149.     sprintf( line, BARFORM, phstat[ phone_stat( PHONE1 ) ],
  150.         phstat[ phone_stat( PHONE2 ) ], boottime, rl, curtime, nusers,
  151.         curwinno() );
  152.     printf( "%s", line );
  153.     fflush( stdout );
  154.  
  155. /*  Take care of the window management stuff; also do some sleeping down
  156.     there.    */
  157.  
  158.     wcheckz();
  159.     }
  160. }
  161.  
  162. char *fmttime( tm )
  163.  
  164. /*    Format a time structure into a string the way *I* want it.    */
  165.  
  166. struct tm *tm;
  167. {
  168. char  abuf[26];
  169.  
  170. sprintf( abuf, TIMEFORM, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min );
  171. return( abuf );
  172. }
  173.  
  174. phone_stat( phone )
  175. char    *phone;
  176.  
  177. /*    Figure out the phone line status.  There are five possible states:
  178.         Idle - completely free line
  179.         Ansr - getty
  180.         Mach - incoming machine
  181.         User - incoming user
  182.         Call - outbound
  183.  
  184.     These five states are defined roughly as follows:
  185.  
  186.         Ansr - utmp -> LOGIN_PROCESS.
  187.         Mach - utmp -> USER_PROCESS; E LCK.
  188.         User - utmp -> USER_PROCESS; ~E LCK.
  189.         Call - utmp -> nothing; E LCK.
  190.         Idle - utmp -> nothing; ~E LCK.
  191.  
  192.     Basically, we decide what we have by a process of elimination.
  193.  
  194.     Known bugs:  This may have problems with HDB UUCP, since I've got no
  195.     way to test against it.  One thing that I am sure of is that since I
  196.     believe that uugetty creates a LCK..device file, this routine will
  197.     consistently mistake users logged in through uugetty for machines
  198.     logged into uucico.    */
  199.  
  200. {
  201. struct utmp    ut_try, *ut, *getutline();
  202. short    utmptype;
  203. char    machine[MACHLEN];
  204.  
  205. /*    Look for the phone line in the utmp file.  getutline() should return
  206.     only LOGIN_PROCESS, USER_PROCESS, or NULL (for failure).  We need to
  207.     save some values before the endutent() call because it clears out the
  208.     static structure pointed to by the return value of getutline().  We
  209.     save the ut_user field because uucico creates a machine LCK file using
  210.     the first six characters of this name.    */
  211.  
  212. strcpy( ut_try.ut_line, phone );
  213. ut = getutline( &ut_try );
  214. utmptype = ut->ut_type;
  215. strcpy( machine, ut->ut_user );
  216.  
  217. /*    AT&T UUCP specifies a maximum length of six characters.  Remove this
  218.     line for less brain-damaged implementations.    */
  219.  
  220. machine[6] = '\0';
  221. endutent();
  222.  
  223. /*    First condition:  was there a utmp entry?    */
  224.  
  225. if ( ut != NULL )
  226.  
  227. /*    There was.  If the type field is LOGIN_PROCESS, we have a getty and can
  228.     return the appropriate value and quit.    */
  229.  
  230.     if ( utmptype == LOGIN_PROCESS )
  231.         return( ANSR );
  232.     else
  233.  
  234. /*    A USER_PROCESS is incoming and can be either a user or a uucico.  An
  235.     incoming uucico has a LCK..machine file, so we check for that.    */
  236.  
  237.         return( locked( machine ) ? MACH : USER );
  238. else
  239.  
  240. /*    There is no utmp entry.  There are only two possibilities here; if there
  241.     is an outgoing call of some sort, there will be a LCK..device file; if
  242.     not, the line is really idle.    */
  243.  
  244.         return( locked( phone ) ? CALL : IDLE );
  245.  
  246. /*    Shouldn't reach here.    */
  247.  
  248. }
  249.  
  250. locked( device )
  251. char    *device;
  252.  
  253. /*    See if a lock file exists.    */
  254.  
  255. {
  256. char    temp[MAXPATHLEN];
  257. int        fd;
  258.  
  259. /*    Make up the full path name.    */
  260.  
  261. strcpy( temp, LOCKDIR );
  262. strcat( temp, LOCKPREFIX );
  263. strcat( temp, device );
  264.  
  265. /*    Attempt to open the lock file.  Assume that if the open fails the lock
  266.     file does not exist.    */
  267.  
  268. fd = open( temp, O_RDONLY );
  269. if ( fd == -1 )
  270.     return( FALSE );
  271. close( fd );
  272. return( TRUE );
  273. }
  274.  
  275. curwinno()
  276.  
  277. /*    Figure out the current window; we need a starting point, after all.
  278.     Known bug:  this sometimes returns the wrong value if it gets into a
  279.     race condition with a terminating/restarting (eg, getty) job.  It also
  280.     doesn't check to see if the ioctl fails.    */
  281.  
  282. {
  283. int        foowin, dummy, temp;
  284.  
  285. foowin = open( "/dev/window", O_RDONLY );
  286. temp = ioctl( foowin, WIOCGCURR, dummy );
  287. close( foowin );
  288. return( temp );
  289. }
  290.  
  291. wcheckz()
  292.  
  293. /*    This is where the code to check for change-window keys lives.  Also,
  294.     sleep for the main program here.  It should return about every TICK
  295.     seconds.    */
  296.  
  297. {
  298. int        tock, newwin, dir;
  299.  
  300. tock = 0;
  301.  
  302. do {
  303. /*    Check for a command key.    */
  304.  
  305.     if ( ( dir = checkey() ) != NOWHERE ) {
  306.  
  307. /*    Determine which window is next and select it.    */
  308.  
  309.         if ( dir != HERE ) {
  310.             newwin = nexwin( curwinno(), dir );
  311.             ioctl( newwin, WIOCSELECT );
  312.             close( newwin );
  313.             }
  314.         tock = TICK;        /*    Kludge to force return and redisplay.    */
  315.         }
  316.     }
  317.  
  318. /*    Return after TICK tocks.    */
  319.  
  320. while ( tock++ < TICK );
  321. }
  322.  
  323. justgoon()
  324.  
  325. /*    Stub routine to "handle" the alarm signal.  Doesn't do a lot.    */
  326.  
  327. {
  328. return;
  329. }
  330.  
  331. checkey()
  332.  
  333. /*    Check and see if one of our keys has been poked.  The only keys this window
  334.     is supposed to respond to all generate three character escape sequences.
  335.     Set and catch an alarm signal to keep from getting hung in the fread();
  336.     this probably could be done more neatly some other way, but...    */
  337.  
  338. {
  339. int        direction, i;
  340. FILE    *fw;
  341. char    key[3];
  342.  
  343. /*    Set the alarm signal.    */
  344.  
  345. signal( SIGALRM, justgoon );
  346. alarm( 1 );
  347. if ( fread( key, 1, 3, stdin ) == 3 ) {
  348.  
  349. /*    Reset the alarm signal to be ignored.    */
  350.  
  351.     signal( SIGALRM, SIG_IGN );
  352.     alarm( 0 );
  353.  
  354. /*    Pick a function.    */
  355.  
  356.     switch ( key[2] ) {
  357.         case 'C' : showprocs(); direction=HERE; break;
  358.         case 'D' : showutmp(); direction=HERE; break;
  359.         case 'E' : showdir( LOCKDIR, LOCKPREFIX ); direction=HERE; break;
  360.         case 'F' : showdir( MAILDIR, "" ); direction=HERE; break;
  361.         case 'G' : showfsys(); direction=HERE; break;
  362.         case 'H' : showdir( LOCKDIR, COMPREFIX ); direction=HERE; break;
  363.         case 'I' : showdir( LOCKDIR, UUXPREFIX ); direction=HERE; break;
  364.         case 'J' : showdir( LOCKDIR, "" ); direction=HERE; break;
  365.         case 'p' : 
  366.         case 'P' : direction = FORWARD; break;
  367.         case 'q' :
  368.         case 'Q' : direction = BACKWARD; break;
  369.         case 'Z' : help(); direction = HERE; break;
  370.         default : direction = NOWHERE; break;
  371.         }
  372.     }
  373.     else
  374.         direction = NOWHERE;
  375.  
  376. /*    Reset the alarm signal to be ignored.    */
  377.  
  378. signal( SIGALRM, SIG_IGN );
  379. alarm( 0 );
  380. return( direction );
  381. }
  382.  
  383. nexwin( winno, dir )
  384. int winno, dir;
  385.  
  386. /*    Decide what should be the next window.  This relies on the fact that
  387.     when you open a window device directly the open call will fail if it has
  388.     not already been opened by someone else; this is how we find the open
  389.     windows.  Invisible windows should have their user-text set to "Invisible"
  390.     if they wish to be ignored.    */
  391.  
  392. {
  393. int        wd;
  394. char    windex[12];
  395. struct    utdata    wintext;
  396.  
  397. /*    Trivial loop; at worst, we'll wind up back where we started.  I suppose
  398.     it's possible to have a system with no windows except those marked as
  399.     "Invisible"; it doesn't seem too useful, though.    */
  400.  
  401. while ( 1 ) {
  402.  
  403. /*    Forward/backward sort-of modulo arithmetic.  Real modulo arithmetic
  404.     starts at zero.    */
  405.  
  406.     winno += ( dir == FORWARD ? 1 : -1 );
  407.     if ( winno > MAXWIN )
  408.         winno = MINWIN;
  409.     else if ( winno < MINWIN )
  410.         winno = MAXWIN;
  411.  
  412. /*    Generate a window name and test for existence.    */
  413.     sprintf( windex, "/dev/w%d", winno );
  414.     if ( ( wd = open( windex, O_RDONLY ) ) != -1 ) {
  415.  
  416. /*    It exists, now look at its user text info.  This is where "Invisible"
  417.     gets skipped.    */
  418.  
  419.         wintext.ut_num = WTXTUSER;
  420.         ioctl( wd, WIOCGETTEXT, &wintext );
  421.         if ( strcmp( wintext.ut_text, "Invisible" ) )
  422.             return( wd );
  423.         else
  424.             close( wd );
  425.         }
  426.     }
  427. }
  428.  
  429. showprocs()
  430.  
  431. /*    Find the process table; run through it, looking up the user areas and
  432.     printing out some data on each one on the fly.  Works like 'ps -ef'
  433.     command.    */
  434.  
  435. {
  436. struct proc    *ppt;
  437. static struct nlist    sym[3] = { { "tuhi", }, { "proc", }, { (char *)0, }, };
  438. struct tunable    tune;
  439. int    mem, kmem;
  440. FILE    *wp;
  441.  
  442. signal( SIGALRM, justgoon );
  443.  
  444. /*    Open up a window; also open the memory devices for reading.    */
  445.  
  446. wp = fopen( "/dev/window", "w" );
  447. mem = open( "/dev/mem", O_RDONLY );
  448. kmem = open( "/dev/kmem", O_RDONLY );
  449.  
  450. /*    Find the tunable parameters, to get the maximum number of processes; and
  451.     the process table.    */
  452.  
  453. nlist( "/unix", sym );
  454. dmemcopy( kmem, (char *)&tune, (long)(sym[0].n_value), (long)sizeof( tune ) );
  455. dmemcopy( kmem, (char *)&ppt, (long)(sym[1].n_value), (long)sizeof( ppt ) );
  456.  
  457. /*    Run through the process table.    */
  458.  
  459. fprintf( wp, "REAL UID EFECT ID  PID   PPID START TIME    TTY  COMMAND\n" );
  460. while ( tune.nproc-- )
  461.     printproc( wp, kmem, mem, ppt++ );
  462.  
  463. /*    Empty the output buffer, sleep to allow it to be read, and close up the
  464.     extra window and return.    */
  465.  
  466. fflush( wp );
  467. sleep( WHOSLEEP );
  468. fclose( wp );
  469. signal( SIGALRM, SIG_IGN );
  470. }
  471.  
  472. showutmp()
  473.  
  474. /*    Run through the utmp file.  Used to give an idea of system activity.    */
  475.  
  476. {
  477. struct    utmp *ut, *getutent();
  478. FILE    *wp;
  479.  
  480. signal( SIGALRM, justgoon );
  481. wp = fopen( "/dev/window", "w" );
  482.  
  483. /*    Go through utmp.  Show login and user processes.    */
  484.  
  485. while ( ( ut = getutent() ) != NULL ) {
  486.     switch ( (int) ut->ut_type ) {
  487.         case USER_PROCESS : 
  488.         case LOGIN_PROCESS : showutent( wp, ut );
  489.         default : break;
  490.         }
  491.     }
  492. endutent();
  493. fprintf( wp, "\n" );
  494. fflush( wp );
  495. sleep( WHOSLEEP );
  496. fclose( wp );
  497. signal( SIGALRM, SIG_IGN );
  498. }
  499.  
  500. showdir( dirname, mask )
  501. char    *dirname, *mask;
  502.  
  503. /*    Display the contents of a directory.  Use 'mask' to specify a file prefix
  504.     string to look for; useful for finding 'LCK..' files.    */
  505.  
  506. {
  507. DIR    *dirp;
  508. struct    directy *dir;
  509. FILE    *wp;
  510. int    mlen;
  511.  
  512. signal( SIGALRM, justgoon );
  513. wp = fopen( "/dev/window", "w" );
  514. dirp = opendir( dirname );
  515. mlen = strlen( mask );
  516. while ( ( dir = readdir( dirp ) ) != NULL ) {
  517.     if ( ( dir->d_name[0] != '.' ) && ( !strncmp( dir->d_name, mask, mlen ) ) ){
  518.         fprintf( wp, "\n%s%s", dirname, dir->d_name );
  519.         }
  520.     }
  521. closedir( dirp );
  522. fprintf( wp, "\n" );
  523. fflush( wp );
  524. sleep( WHOSLEEP );
  525. fclose( wp );
  526. signal( SIGALRM, SIG_IGN );
  527. }
  528.  
  529. showfsys()
  530.  
  531. /*    Show the root file system statistics.  Read the superblock in and get
  532.     free blocks, total blocks, and free inodes from it.  Also display the
  533.     freespace as a percentage.    */
  534.  
  535. {
  536. int rp;
  537. struct filsys fs;
  538.  
  539. signal( SIGALRM, justgoon );
  540. rp = open( "/dev/rfp002", O_RDONLY );
  541. lseek( rp, 512, 0 );
  542. read( rp, &fs, sizeof( fs ) );
  543. close( rp );
  544. printf( "\nRoot: %ldK free %ldK total %d%% free %d inodes", fs.s_tfree,
  545.     fs.s_fsize, (int)(100.0*(float)(fs.s_tfree)/(float)(fs.s_fsize)),
  546.     fs.s_tinode );
  547. fflush( stdout );
  548. sleep( 5 );
  549. printf( "\n" );
  550. signal( SIGALRM, SIG_IGN );
  551. }
  552.  
  553. help()
  554.  
  555. /*    Print a help message on key usage.    */
  556.  
  557. {
  558. FILE    *wp;
  559.  
  560. signal( SIGALRM, justgoon );
  561. wp = fopen( "/dev/window", "w" );
  562. fprintf( wp, "\t- Phone line states\n\t\to Idle - completely free line\n" );
  563. fprintf( wp, "\t\to Ansr - computer answering line (getty job)\n" );
  564. fprintf( wp, "\t\to Mach - incoming machine\n\t\to User - incoming user\n" );
  565. fprintf( wp, "\t\to Call - any outbound\n\t- Boot date and time\n" );
  566. fprintf( wp, "\t- Current run level\n\t- Current date and time\n\t- Number of users\n" );
  567. fprintf( wp, "\tWhen Suspend is hit, it makes the next window current; Resume\n" );
  568. fprintf( wp, "\tmakes the last window current.\n\n" );
  569. fprintf( wp, "\tThe following functions are mapped to the function keys:\n" );
  570. fprintf( wp, "\tShift-F1:  process display, similar to 'ps -ef'\n" );
  571. fprintf( wp, "\tShift-F2:  utmp display, like 'who'\n" );
  572. fprintf( wp, "\tShift-F3:  lists UUCP LCK.. files, if any\n" );
  573. fprintf( wp, "\tShift-F4:  directory of /usr/mail\n" );
  574. fprintf( wp, "\tShift-F5:  Root filesystem freespace and free inodes\n" );
  575. fprintf( wp, "\tShift-F6:  lists UUCP C. files, if any\n" );
  576. fprintf( wp, "\tShift-F7:  lists UUCP X. files, if any\n" );
  577. fprintf( wp, "\tShift-F8:  directory of /usr/spool/uucp\n" );
  578. fprintf( wp, "\tShift-Print:  display this list" );
  579. fflush( wp );
  580. sleep( WHOSLEEP );
  581. fclose( wp );
  582. signal( SIGALRM, SIG_IGN );
  583. }
  584.  
  585. dmemcopy( devmem, buf, memloc, nbytes )
  586. int    devmem;
  587. char    *buf;
  588. long    memloc, nbytes;
  589.  
  590. /*    Read from a memory device into a local buffer.    */
  591.  
  592. {
  593. lseek( devmem, memloc, 0 );
  594. read( devmem, buf, (unsigned)nbytes ); 
  595. }
  596.  
  597. printproc( wp, kmem, mem, ppt )
  598. FILE    *wp;
  599. int    kmem, mem;
  600. struct proc    *ppt;
  601.  
  602. /*    Get a process table entry and print some of the information from it.    */
  603.  
  604. {
  605. struct proc p;
  606. struct user    u;
  607. struct passwd    *pwd, *getpwuid();
  608. char    curtime[26], ptty[7], *getptty();
  609.  
  610. dmemcopy( kmem, (char *)&p, (long)ppt, (long)sizeof( struct proc ) );
  611. if ( p.p_flag & SLOAD ) {
  612.     dmemcopy( mem, (char *)&u, (long)( ctob( p.p_addr[0] )+U_OFFSET ),
  613.         (long)sizeof( struct user ) );
  614.     pwd = getpwuid( p.p_uid );
  615.     fprintf( wp, "%8s ", pwd->pw_name );
  616.     pwd = getpwuid( p.p_suid );
  617.     fprintf( wp, "%8s %5d %5d ", pwd->pw_name, p.p_pid, p.p_ppid );
  618.     strcpy( curtime, fmttime( localtime( &u.u_start ) ) );
  619.     strcpy( ptty, getptty( u.u_ttyd ) );
  620.     fprintf( wp, "%11s %6s %s\n", curtime, ptty, u.u_comm );
  621.     fflush( wp );
  622.     }
  623. }
  624.  
  625. char    *getptty( devno )
  626. dev_t    devno;
  627.  
  628. /*    Go from a device number to a device name.    */
  629.  
  630. {
  631. DIR    *dirp;
  632. struct directy    *dir;
  633. struct stat    sbuf;
  634. char    name[14];
  635. static char    tname[7];
  636.  
  637. strcpy( tname, "none" );
  638. if ( !devno )
  639.     return( tname );
  640. dirp = opendir( DEVDIR );
  641. while ( ( dir = readdir( dirp ) ) != NULL ) {
  642.     if ( dir->d_name[0] != '.' ) {
  643.         strcpy( name, DEVDIR );
  644.         strcat( name, dir->d_name );
  645.         stat( name, &sbuf );
  646.         if ( devno == sbuf.st_rdev ) {
  647.             closedir( dirp );
  648.             strcpy( tname, dir->d_name );
  649.             return( tname );
  650.             }
  651.         }
  652.     }
  653. closedir( dirp );
  654. return( tname );
  655. }
  656.  
  657. showutent( fp, u )
  658. FILE    *fp;
  659. struct    utmp *u;
  660.  
  661. /*    Print a formatted utmp entry.    */
  662.  
  663. {
  664. char    curtime[26], *fmttime();
  665.  
  666. strcpy( curtime, fmttime( localtime( &( u->ut_time ) ) ) );
  667. fprintf( fp, "%8s %14s %11s %5d\n", u->ut_user, u->ut_line, curtime, u->ut_pid );
  668. }
  669.  
  670.