It took me a while to figure out the file format for /var/log/lastlog on *NIX type machines. Some of the sysadmins out there may find it usefull to be able to determine the last time of login for an account without using any other apps.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #! /usr/bin/env python
import struct
def getrecord(file,uid, preserve = False):
"""Returns [int(unix_time),string(device),string(host)] from the lastlog formated file object, set preserve = True to preserve your position within the file"""
position = file.tell()
recordsize = struct.calcsize('L32s256s')
file.seek(recordsize*uid)
data = file.read(recordsize)
if preserve:
file.seek(position)
try:
returnlist = list(struct.unpack('L32s256s',data))
returnlist[1] = returnlist[1].replace('\x00','')
returnlist[2] = returnlist[2].replace('\x00','')
return returnlist
except:
return False
if __name__ == '__main__':
import sys
import pwd
import time
try:
llfile = open("/var/log/lastlog",'r')
except:
print "Unable to open /var/log/lastlog"
sys.exit(1)
for user in pwd.getpwall():
record = getrecord(llfile,user[2])
if record and record[0] > 0:
print '%16s\t\t%s\t%s' % (user[0],time.ctime(record[0]),record[2])
elif record:
print '%16s\t\tNever logged in' % (user[0],)
else:
pass
llfile.close()
|
It took me a while to figure out the file format for /var/log/lastlog on NIX type machines. This file contains recordsizelargest_uid records, each record is accessed by seeking to the uid*recordsize and reading recordsize bytes. Each record consists of a long unsigned integer (important to note that on some machines this is the same size as an unsigned integer but may differ), up to a 16 character string denoting the device the connection was made with (i.e. tty0,:0,pts1), and a 256 character string denoting the host from which the connection was made. I tried to make this implementation as simple and flexible as possible and stick to the standard library. I welcome any suggestions for making this more efficient, usefull, elegant, etc.
Small change for x86_64. I can't imagine having access to the lastlog file, but not the lastlog program. As you say, the file format is system dependent and the lastlog program has been compiled with the system header files that define what that is. So something like os.popen('lastlog') is probably a better way to get this information from within Python.
Anyway, to get your program to work on my x86_64 machine, I had to add an initial = to the struct format. That is, I added the line
and then replaced both occurrences of your format string with lastlogformat.
I overlooked the byteorder, that is a very good suggestion.
This most likely will never be of use to you, but I have clusters where I need to aggregate several lastlog files. I also have 40000 users and would like to avoid the extra over head of pipes, file opens for every query, and nonsquential access if I step through all of the uid's.
On further examination '=' prefix works for x86 and x86_64, but not for ia64. I am not sure what the best way to make this work on all arches.
Those should be C-style null terminated strings in the file, so instead of .replace('\x00',''), wouldn't the following work better?