RCS_ID_C="$Id: getutent.c,v 4.1 1994/05/16 14:09:07 ppessi Exp $";
/*
 * Login database functions
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * This file is part of the AmiTCP/IP usergroup.library.
 *
 * Copyright © 1993 AmiTCP/IP Group, <AmiTCP-Group@hut.fi>
 *                  Helsinki University of Technology, Finland.
 *
 * Created      : Sun Nov 28 17:45:55 1993 ppessi
 * Last modified: Mon May 16 12:57:05 1994 ppessi
 *
 * $Log: getutent.c,v $
 * Revision 4.1  1994/05/16  14:09:07  ppessi
 * Fixed some outdated typecasts
 *
 * Revision 2.4  1994/02/27  19:15:26  ppessi
 * Removed getutsid() as obsoilete
 *
 * Revision 2.3  1994/02/22  03:01:12  ppessi
 * Removed tty from logging information
 *
 * Revision 2.2  1994/02/17  12:11:55  ppessi
 * Release 2
 *
 * Revision 2.1  1994/02/17  02:21:06  ppessi
 * Saving before downgrading library
 *
 */

/****** usergroup.library/getutent *******************************************

    NAME
        getutsid, getutent, setutent, endutent - utmp database operations

    SYNOPSIS
        #include <utmp.h>

        utmpent = getutsid(sid)
           D0              D0
        struct utmp *getutsid(long);

        utmpent = getutent()
           D0
        struct utmp *getutent(void);

        setutent()
        void setutent(void);

        endutent()
        void endutent(void);

    FUNCTION
        These functions operate on the utmp database.  There is an utmp
        entry for each active session.  A session is started with login
        command and finished with logout command.

        The entry returned by each reading function is defined by the
        structure utmp found in the include file <utmp.h>:

            struct utmp {
              long    ut_time;              \* the login time *\
              long    ut_sid;               \* session ID *\
              char    ut_name[UT_NAMESIZE]; \* the login name *\
              char    ut_line[UT_LINESIZE]; \* the name of login device *\
              char    ut_host[UT_HOSTSIZE]; \* where the login originated *\
            };

        The getutsid() function search the utmp database for the given
        session id, returning the first one encountered.  The getutent()
        function sequentially reads the utmp database.  Both functions also
        open the utmp database, if necessary.

        The setutent() function opens the utmp database.  The
        endutent() function closes the utmp database.  It is
        recommended to call endutent() if the program won't access
        utmp database any more.

    RESULTS
        The functions getutsid() and getutent() return a pointer to the utmp
        entry if successful; if the end of database is reached or an error
        occurs a null pointer is returned.  The functions endutent() and
        setutent() have no return value.

    ERRORS
        [ENOENT] -- no utmp entries were available.

    SEE ALSO

    BUGS
        The getutent() and getutsid() function leave their result in an
        internal static object and return a pointer to that object.
        Subsequent calls to the same function will modify the same object.

        Current implementation allows only one user to be logged in
        concurrently.

*****************************************************************************
*
*/

#include "base.h"
#include "libfunc.h"
#include "credential.h"

#include <utmp.h>
#include <string.h>
#include <exec/memory.h>
#include <dos/dos.h>

#include <sys/time.h>

#define SINGLE_USER

ASM int R_gettimeofday(REG(a0) struct timeval *tvp, 
		       REG(a1) struct timezone *tzp);

#ifdef SINGLE_USER
/*      
 * These are simple routines for single-user environment
 */
void CleanupUTMP(void)
{
}

static short already_read = 0;

SAVEDS ASM void R_endutent(void)
{
  already_read = 0;
}
SAVEDS ASM void R_setutent(void)
{
  already_read = 0;
}

SAVEDS ASM struct utmp *R_getutent(void)
{
  if (!already_read) {
    static struct utmp utentbuf[1];

    already_read = 1;
    *utentbuf = *CredentialBase->r_utmp;
    return utentbuf;
  }

  SetErrno(ENOENT);
  return NULL;
}
#else
static void lock_for_writing(void)
{
}

static void unlock_for_writing(void)
{
}

#define UTENTS 2
static short utent_left;
static struct utmp *utentbuf = NULL, *utent = NULL;
static BPTR utfile = NULL;
static LONG uttell;

void CleanupUTMP(void)
{
  R_endutent();

  if (utentbuf != NULL) {
    FreeVec(utentbuf), utentbuf = NULL;
  }
}

SAVEDS ASM void R_endutent(void)
{
  if (utfile != NULL) {
    Close(utfile), utfile = NULL;
  }
}

SAVEDS ASM void R_setutent(void)
{
  if (utentbuf == NULL)
    utentbuf = AllocVec(sizeof(*utent) * UTENTS, MEMF_CLEAR | MEMF_PUBLIC);
  if (utentbuf != NULL) {
    utent_left = 0;
    if (utfile == NULL) {
      utfile = Open(_PATH_UTMP, MODE_READWRITE);
    } else {
      Seek(utfile, 0, OFFSET_BEGINNING);
    }
  }
}

SAVEDS ASM struct utmp *R_getutent(void)
{
  if (utentbuf == NULL) {
    R_setutent();
  }

  if (utfile) {
    for (;;) {
      if (utent_left == 0) {
	LONG read;
	
	uttell = Seek(utfile, 0, OFFSET_CURRENT);
	read = Read(utfile, utentbuf, sizeof(*utent) * UTENTS);

	utent = utentbuf;
	if (read >= sizeof(*utent)) {
	  utent_left = read / sizeof(*utent);
	} else {
	  break;
	}
      } else {
	uttell += sizeof(*utent);
      }

      utent_left--;
      if (utent->ut_time != 0)
	return utent++;
      utent++;
    }

    SetErrno(ENOENT);
  } else {
    if (utentbuf)
      SetErrno(ENOENT);
    else
      SetErrno(ENOMEM);
  }

  return NULL;
}

SAVEDS ASM struct utmp *R_getutsid(REG(d0) pid_t sid)
{
  struct utmp *ut;

  for (R_setutent(); ut = R_getutent();) {
    if (ut->ut_sid == sid)
      return ut;
  }

  return NULL;
}
#endif

/****** usergroup.library/getlastlog *******************************************

    NAME
        getlastlog - get lastlog database entry

    SYNOPSIS
        #include <utmp.h>

        lastlog = getlastlog(uid)
           D0                D0
        struct lastlog *getlastlog(uid_t);

    FUNCTION

        The getlastlog() function search the lastlog database for the given
        user id. There should be an lastlog entry for each user.  A lastlog
        entry with ll_time being zero means that user has never logged in
        this system.

        The entry returned by getlastlog is defined by the structure lastlog
        found in the include file <utmp.h>:

	      struct lastlog {                                              
	        long  ll_time;              \* the login time *\            
	        uid_t ll_uid;               \* user ID *\                   
	        char  ll_name[UT_NAMESIZE]; \* the login name *\            
	        char  ll_line[UT_LINESIZE]; \* the name of login device *\  
	        char  ll_host[UT_HOSTSIZE]; \* where the login originated *\
              };

    RESULTS
        The function getlastlog() returns a pointer to the lastlog entry if
        successful; if an error occurs a null pointer is returned.

    ERRORS
        [ENOENT] -- no lastlog entry was found
        [EINVAL] -- the user ID was illegal

    SEE ALSO
        setlastlog()

    BUGS
        The getlastlog() function leaves its result in an internal static
        object and return a pointer to that object.  Subsequent calls to the
        same function will modify the same object.

        Current implementation stores only the lastlog data of the latest
        user logged in.

*****************************************************************************
*
*/

#ifdef SINGLE_USER

SAVEDS ASM struct lastlog *R_getlastlog(REG(d0) uid_t uid)
{
  static struct lastlog ll[1];

  if (uid == CredentialBase->r_lastlog->ll_uid) {
    *ll = *CredentialBase->r_lastlog;
  } else {
    bzero(ll, sizeof(*ll));
    ll->ll_uid = uid;
  }

  return ll;
}
#else
static LONG lltell = 0; 

SAVEDS ASM struct lastlog *R_getlastlog(REG(d0) uid_t uid)
{
  static struct lastlog ll[1];
  BPTR llfile = Open(_PATH_LASTLOG, MODE_OLDFILE);
  int never = 1;

  lltell = 0;
  
  if (llfile) {
    while (Read(llfile, ll, sizeof(*ll)) == sizeof(*ll)) {
      lltell += sizeof(*ll);
      if (ll->ll_uid == uid) {
	never = 0;
	break;
      }
    }
    Close(llfile);
  }

  if (never) {
    ll->ll_time = 0;
    ll->ll_uid = uid;
    ll->ll_name[0] = '\0';
    ll->ll_line[0] = '\0';
    ll->ll_host[0] = '\0';
    lltell = -1;
  }

  return ll;
}
#endif

/****** usergroup.library/setlastlog ***************************************

    NAME
        setlastlog - login an user

    SYNOPSIS
        #include <utmp.h>

        lastlogin = setlastlog(uid, name, console, host)

        struct lastlog setlastlog(uid_t, char *, char *, char *);

    FUNCTION
        The setlastlog function is used to register user logging in.  Each
        time a user is logging in, the function setlastlog() should be
        called to register that event.

    INPUTS
        uid     -- the uid of user logging in
	name    -- the user login name
	console -- the console handler name (from ug_GetConsole())
	host    -- the host which the user is logging in from

    RESULTS
        The setlastlog() function returns an success indicator, 0 if the
        call was successful, -1 otherwise.  The error code is set if an
        error occurs.

    ERRORS
        The setlastlog() can have following error codes:
	[EFAULT] -- the utmp entry cannot be accessed
	[ENOMEM] -- the memory has been exhausted
	[ENOENT] -- cannot access utmp database

    BUGS
        Current implementation stores only the lastlog data of the latest
        user logged in.

    FILES

    SEE ALSO
        getutent(), getlastlog()

*****************************************************************************
*/

#ifdef SINGLE_USER
/*
 * This is somewhat simplified version
 */
SAVEDS ASM int
R_setlastlog(REG(d0) uid_t uid, 
	     REG(a0) char *name, REG(a1) char *host)
{
  short error = 0;
  struct proc *p = procfind(NULL);
  pid_t sid = p->p_session->s_leader;

  if (name == NULL) {
    error = EFAULT;
  } else {
    struct utmp utmp[1];
    struct timeval now[1]; 

    bzero((void *)utmp, sizeof(*utmp));

    R_gettimeofday(now, NULL);
    utmp->ut_time = now->tv_sec;
    utmp->ut_sid = (long)sid;

    strncpy(utmp->ut_name, name, sizeof(utmp->ut_name));
    if (host)
      strncpy(utmp->ut_host, host, sizeof(utmp->ut_host));
      
    *CredentialBase->r_utmp = *utmp;
    
    /*
     * This trick requires that 
     * lastlog and utmp are essentially identical..
     */
    utmp->ut_sid = (long) uid;
    *CredentialBase->r_lastlog = *(struct lastlog *)utmp;
    return 0;
  }

  SetErrno(error);
  return -1;
}
#else /* SINGLE_USER */
SAVEDS ASM int
R_setlastlog(REG(d0) uid_t uid, 
	     REG(a0) char *name, REG(a1) char *host)
{
  short error = 0;
  struct proc *p = procfind(NULL);
  pid_t sid = p->p_session->s_leader;

  if (name == NULL || con == NULL) {
    error = EFAULT;
  } else {
    struct utmp *oldut, utmp[1];
    struct timeval now[1]; 

    bzero((void *)utmp, sizeof(*utmp));
    R_gettimeofday(now, NULL);
    utmp->ut_time = now->tv_sec;
    utmp->ut_sid = sid;
    strncpy(utmp->ut_name, name, sizeof(utmp->ut_name));
    if (sid != 0) 
      strncpy(utmp->ut_line, con, sizeof(utmp->ut_line));
    else
      strcpy(utmp->ut_line, "ALL");
    if (host)
      strncpy(utmp->ut_host, host, sizeof(utmp->ut_host));

    R_setutent();

    /* Writing lock */
    lock_for_writing();

    if (utentbuf == NULL) {
      error = ENOMEM;
    } else if (utfile == NULL) {
      error = ENOENT;
    } else {
      LONG utslot = -1;

      while (oldut = R_getutent()) {
	if (oldut->ut_sid == sid) {
	  utslot = uttell;
	  break;
	}
	if (oldut->ut_time == 0 && utslot == -1) {
	  utslot = uttell;
	}
      }
      if (oldut == NULL)
	utslot = uttell;

      Seek(utfile, uttell, OFFSET_BEGINNING);
      Write(utfile, utmp, sizeof(*utmp));
    }

    R_endutent();

    {
      BPTR llfile = Open(_PATH_LASTLOG, MODE_READWRITE);
      
      utmp->ut_sid = (long)uid;

      if (llfile) {
	if (lltell > 0) {
	  Seek(llfile, lltell, OFFSET_BEGINNING);
	} else {
	  LONG tell;
	  Seek(llfile, 0, OFFSET_END);
	  tell = Seek(llfile, 0, OFFSET_CURRENT);
	  tell = (tell / sizeof(*utmp)) * sizeof(*utmp);
	  Seek(llfile, tell, OFFSET_BEGINNING);
	}
	Write(llfile, utmp, sizeof(*utmp));
	Close(llfile);
      }
    }
    unlock_for_writing();
  }

  if (error != 0) {
    SetErrno(error);
    return NULL;
  } else {
    return ll;
  }
}
#endif
