/*************************************************************************/
/* user.c - Displays all interactive users on the system.                */
/*          This was something available from my old DEC days that I     */
/*          thought I would try to do on *nix.                           */
/* --------------------------------------------------------------------- */
/* Written by Dean P. Junk (dpjunk@mmm.com)                              */
/*************************************************************************/
/*  This program is free software; you can redistribute it and/or modify */
/*  it under the terms of the GNU General Public License as published by */
/*  the Free Software Foundation; either version 2 of the License, or    */
/*  (at your option) any later version.                                  */
/*                                                                       */
/*  This program is distributed in the hope that it will be useful,      */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of       */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
/*  GNU General Public License for more details.                         */
/*                                                                       */
/*  You should have received a copy of the GNU General Public License    */
/*  along with this program; if not, write to the Free Software          */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            */
/*************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <utmp.h>
#include <pwd.h>

char  ident[] = "@(#)user 09-Jun-1993 V2.00 by Dean P. Junk.";

struct utmp    *utmpp;        /* pointer to utmp file entry     */
struct utmp    *getutent();   /* returns next utmp file entry   */
struct utsname utsbuf;        /* returns machine info - uname   */
struct passwd  *pwdd;         /* returns passwd entry for user  */
struct passwd  *getpwnam();   /* returns next passwd file entry */

typedef struct 
{
     char UserName[10];       /* OS user id            */
     char RealName[20];       /* Real name of the user */
     char Location[16];       /* Location of user      */
     char Phone[16];          /* Phone number of user  */
     int  TermCount;          /* # of logins for user  */
} SU;                         /* Hold unique usernames */

SU *Users;                    /* pointer to the structure */


main ()
{
     SumUsers ();             /* Process all entries */
}


int compare (a, b)
SU *a;
SU *b;
{
     return (strcmp(a->UserName , b->UserName));
}



SumUsers()
{
    /* Declare some local variables */ 
    char newname[10]; 
    char msg[100]; 
    char *curtime; 
    int  count; 
    int  totalcount;
    int  nseconds; 
    int  usercount; 
    int  i;

    nseconds = time(NULL);         /* Get the current time */
    curtime  = ctime(&nseconds);

    for (i = 0; curtime[i] != '\0'; i++)
    {
         if (iscntrl(curtime[i]))         /* Strip control codes from time */
              curtime[i] = ' ';
    }

    if (uname(&utsbuf) == -1)             /* Get the machine info */
         ExitNow ("Unable to get machine info!", -1);

    totalcount = 0;
    count      = 0;
    usercount  = 0; 

  
    while ((utmpp = getutent()) != NULL)       /* Get count for allocation */
    {                                          /* of memory                */
         if (utmpp->ut_type = USER_PROCESS)
              count++;
    }

    if (!(Users = calloc (count, sizeof(SU))))     /* Allocate the memory */
         ExitNow ("Unable to allocate memory.", -1);

    setutent ();     /* Reset the pointer to the beginning of file */
 
    do
    {
        utmpp = getutent ();  /* Get utmp record */
      
        if (utmpp == NULL || utmpp->ut_type != USER_PROCESS)
             continue;

        strncpy(newname, utmpp->ut_user, 8);
        newname[8] = '\0';

        for (i = 0; i < usercount; i++)  /* See if username is in the arrary */
        {
             if (!strcmp(Users[i].UserName, newname))
                  break;
        }

        if (i < usercount)      /* If it is, just increment terminal count */
             Users[i].TermCount++;
        else                    /* Add it to the array */
        {
             if (usercount > count)
                  ExitNow ("Too many users for memory allocated.", -1);

             /* Get passwd information for the user */
             if ((pwdd = getpwnam(newname)) == NULL)
             {
                  sprintf (msg, "Username <%s> not found in passwd!\n", 
                           newname);
                  ExitNow (msg, -1);
             }

             /* Parse out the full name, location, and phone */
             sscanf (pwdd->pw_gecos, "%[^,]%*c%[^,]%*c%[^,]%*c",
                     Users[usercount].RealName, Users[usercount].Location, 
                     Users[usercount].Phone);

             strcpy(Users[usercount].UserName, newname);
             Users[usercount].TermCount++;
             usercount ++;
        }

    } while (utmpp != NULL);

    /* Sort the array by OS username */
    qsort (Users, usercount, sizeof(SU), compare );

    /* Print the header for the display */ 
    printf ("\n\nUsers on node %s %s %s @ %s:\n\n",
            utsbuf.nodename, utsbuf.sysname, utsbuf.machine, curtime);
    printf ("%-9.9s  %-5.5s  %-20.20s  %-16.16s  %-16.16s\n",
            "User Name", "Count", "Real Name", "Location", "Phone");
    printf ("%-9.9s  %-5.5s  %-20.20s  %-16.16s  %-16.16s\n",
            "---------", "-----", "--------------------", 
            "----------------", "----------------");
         
    for (i = 0; i < usercount; i++)    /* Now circle through the array */
    {

        /* Print the user information */
        printf ("%-9.9s  %5.0d  %-20.20s  %-16.16s  %-16.16s\n", 
                Users[i].UserName, Users[i].TermCount, Users[i].RealName, 
                Users[i].Location, Users[i].Phone);

        totalcount = totalcount + Users[i].TermCount;

    } 

    printf ("\nTotal number of users are %d.\n", totalcount);

    cfree (Users);   /* Deallocate the memory */

}

  
ExitNow (message, status)
char *message;
int status;
{
     printf ("user *error* : %s, status: %d\n", message, status);
     exit (status);
}
