http://melkor.dnp.fmph.uniba.sk/~garabik/python-utmp
garabik@melkor.dnp.fmph.uniba.sk
====================================================

python-utmp consists of three modules, providing access to utmp records.
It is quite difficult to access utmp record portably, because every UNIX
has different structure of utmp files. Currently, python-utmp works on
platforms which provide getutent, getutid, getutline,  pututline,
setutent, endutent and utmpname functions (such as GNU systems
(Linux and hurd) and System V unices) and on BSD systems using
simple utmp structure.

python-utmp is known to work on
linux with glibc2.0, glibc2.1, glibc2.2; SunOS 5.6; HP-UX B.10.20;
NetBSD 1.4.2; OpenBSD 2.9
     
Usually, system provides functions for accessing utmp, but on *BSD
systems the only way (IMHO) is to read utmp file as consisting of
C-structures. It is difficult to unite both worlds. 
To make things worse, some OS'es (hint: SunOS :-)) use utmpx 
in parallel to utmp, utmpx containing more information.

python-utmp provides getutent() & comp. as an emulation layer if native
system is lacking these functions, providing ideal glibc-like utmp
structure, emulating fields present in this structure but missing at the
specific platform.

Missing member:    Default used instead:
ut_type                 USER_PROCESS
                        DEAD_PROCESS if ut_name and ut_host are empty
                        EMPTY if ut_name, ut_host and ut_line are empty
                        BOOT_TIME if ut_line is "~"
ut_pid                  0
ut_id                   ut_line
ut_host                 ''
ut_exit                 (0, 0)
ut_tv.tv_sec            ut_time
ut_tv.tv_usec           0
ut_addr_v6              4*[0]
ut_session              0


CAVEAT: 
If native platform misses ut_type, it distinguishes between user
processes and pseudo-entries by ut_line. python-utmp tries to guess the
appropriate ut_type in this case. It might not guess correctly.
Moreover, I have seen some quirks with ut_type==USER_PROCESS  for date
entries with linux. Therefore, date entries (with ut_line=="{" or
ut_line=="|") are considered to be user processes for the time being.

Similarly, if you write into utmp and your ut_type
is not USER_PROCESS, corresponding ut_host and ut_user are cleared (=filled
with zeros). Depending on your system, this may or may not be the
right thing to do. It is correct at least at NetBSD.
Reading from utmp and wtmp should be safe and bulletproof.

utmpaccess.py provides following functions
(see getutent(3) and utmp(5) for more information):

ut_addr_v6 is a tuple of 4 integers,
ut_exit is tuple of (e_termination, e_exit)
ut_tv is tuple of (tv_sec, tv_usec)

getutent():
    returns None if there is an error (e.g. going past the end of utmp file)
    otherwise returns tuple of:
    (ut_type, ut_pid, ut_line, ut_id, ut_user, ut_host,
     ut_exit, ut_session, ut_tv, ut_addr_v6)

getutid(ut_type[, ut_id]):
    returns None if there is an error (e.g. not found), otherwise returns
    tuple of:
    (ut_type, ut_pid, ut_line, ut_id, ut_user, ut_host,
     ut_exit, ut_session, ut_tv, ut_addr_v6)

getutline(ut_line):
    returns None if there is an error (e.g. not found), otherwise returns
    tuple of:
    (ut_type, ut_pid, ut_line, ut_id, ut_user, ut_host,
     ut_exit, ut_session, ut_tv, ut_addr_v6)

pututline(ut_type, ut_pid, ut_line, ut_id, ut_user, ut_host,
          ut_e_termination, ut_exit, ut_session,
          ut_tv, ut_addr_v6):
    returns None

setutent():
    returns None

endutent()
    returns None

utmpname(fname):
    returns None


UTMPCONST.py provides convenient constants for work with utmpaccess.py,
taken from system constants, or some sensibly made-up values if
system does not provide them.
(see UTMPCONST.py source)
These constants are provided:

EMPTY RUN_LVL BOOT_TIME NEW_TIME OLD_TIME INIT_PROCESS LOGIN_PROCESS
USER_PROCESS DEAD_PROCESS ACCOUNTING UT_UNKNOWN

UT_LINESIZE UT_NAMESIZE UT_HOSTSIZE UT_IDSIZE

UTMP_FILE WTMP_FILE 

UT_IDSIZE if the size of ut_id (usually 4).
If your system does not have ut_id, ut_id is simulated by ut_line,
and UT_IDSIZE==UT_LINESIZE


utmp.py provides class UtmpRecord with following methods:

getutent()
getutent_dict()
    like getutent(), but returns a dictionary instead of tuple.
    returns {} if there was an error.
getutid(ut_type[, ut_id]):
getutid_dict(ut_type[, ut_id]):
    like getutid(), but returns a dictionary instead of tuple.
    returns {} if there was an error.
getutline(ut_line)
getutline_dict(ut_line)
    like getutline(), but returns a dictionary instead of tuple.
    returns {} if there was an error.
pututline(ut_type, ut_pid, ut_line, ut_id, ut_user, ut_host,
          ut_e_termination, ut_exit, ut_session,
          ut_tv, ut_addr_vut_line6)
or pututline(t) where t is tuple as returned by getutent.
pututline_dict(d)
    like pututline(), but d is a dictionary as returned by getutent_dict()
setutent()
endutent()

You pass filename for utmpname() when creating instance of the class, 
e.g. a=Utmp.UtmpRecord("/var/log/wtmp")
