#! /usr/bin/env python import os,string,g etopt,time,sys,struct """ usage : wwho [date] [-b secs (backwards)] [-p precision (sec/m/h/d)] [-l (last)] [-a (all last msgs)] [-f wtmp_file] [-d (debug)] DATE (2000 May 22 06:12:08) wfile : idem, except : [-a (access)] FILE ça marche. mais la recherche ds le wtmp pour construire _sessions_ est pas du tout optimale -> lent pour un gros wtmp. Améliorations à prévoir.. """ optlist, args = getopt.getopt(sys.argv[1:], "adlb:p:f:") date="" emul_last=0 all_last=0 debug=0 datesec=0 precision=2 utmpF="/var/log/wtmp" if len(args)>0 : date=args[0] print "cmd_line_date : %s" %date for i in optlist : if i[0] =='-l' : emul_last=1 if i[0] =='-a' : all_last=1 if i[0] =='-d' : debug=1 if i[0] =='-b' : datesec=time.time()-float(i[1]) if i[0] =='-p' : precision=float(i[1]) if i[0] =='-f' : utmpF=i[1] if debug : print "Option found : "+repr(i) if debug : print " argv[0] : %s" % sys.argv[0] if not datesec : if date : if string.lower(os.path.basename(sys.argv[0]))=="wfile.py" : datesec=os.path.getmtime(date) #modif time if all_last : #access time datesec=os.stat(date)[7] # datesec=os.path.getatime(date) #BUG ! donne mtime ! all_last=0 print " Chosen file Time : %s" \ %time.ctime(datesec) else : t=time.strptime(date,"%Y %b %d %H:%M:%S") #we still have to guess the DaylightSsavingTime flag -> -1 t2=[ t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],-1] datesec = time.mktime(t2) else : datesec =time.time() def unpackstr(s, i=0): for c in s: if c == '\000': break i = i + 1 return s[:i] def msectime(secs,usecs) : t=time.localtime(secs) s=time.strftime("%d %b %Y %T",t) s=s+ ( ".%06d"%usecs )[0:4] #s=s+ time.strftime(" %z %Z",time.localtime(secs)) return s if debug : print " Date : %s // datesec : "% date + repr(datesec) print "Finding who were here at %s, with precision %fs."\ % (msectime(datesec,0),precision) # the C struct declaration (on AIX 4.2.1) # char ut_user[8]; # char ut_id[14]; # char ut_line[12]; # short ut_type; # pid_t ut_pid; (int) # short ut_exit.e_termination; # short ut_exit.e_exit; # time_t ut_time; (long) # char ut_host[16]; # the format string to represent that structure """ cf utmp.h , ou man utmp : #define UT_LINESIZE 32 #define UT_NAMESIZE 32 #define UT_HOSTSIZE 256 /* The structure describing the status of a terminated process. This type is used in `struct utmp' below. */ struct exit_status { short int e_termination; /* Process termination status. */ short int e_exit; /* Process exit status. */ }; /* The structure describing an entry in the user accounting database. */ struct utmp { short int ut_type; /* Type of login. */ pid_t ut_pid; /* Process ID of login process. */ char ut_line[UT_LINESIZE=32]; /* Devicename. */ char ut_id[4]; /* Inittab ID. */ char ut_user[UT_NAMESIZE=32]; /* Username. */ char ut_host[UT_HOSTSIZE=256]; /* Hostname for remote login. */ struct exit_status ut_exit; /* Exit status of a process marked as DEAD_PROCESS. */ long int ut_session; /* Session ID, used for windowing. */ struct timeval ut_tv; /* Time entry was made. */ int32_t ut_addr_v6[4]; /* Internet address of remote host. */ char __unused[20]; /* Reserved for future use. */ }; """ fmt="hi32s4s32s256shhlii4i20s" #fmt="2s4s32s4s32s256s4s8s8s16s20s" """ constants (for ut_type): """ UT_EMPTY = 0 UT_RUN_LVL = 1 UT_BOOT_TIME = 2 UT_NEW_TIME = 3 UT_OLD_TIME = 4 UT_INIT_PROCESS = 5 UT_LOGIN_PROCESS = 6 UT_USER_PROCESS = 7 UT_DEAD_PROCESS = 8 UT_ACCOUNTING = 9 record_size = struct.calcsize(fmt) # read who is logged on to a UNIX system file = open(utmpF, 'rb') sessions={} suspects=[] def evdisp(event,zt,rev) : ( ut_t,ut_ut,zt_t,zt_ut,u,l,h,p,id,typ) =event if not zt : if zt_t: zt=msectime(zt_t,zt_ut) else : zt=" -stillNow-" if rev : t=zt; zt=msectime(ut_t,ut_ut) else : t=msectime(ut_t,ut_ut) return '%-8s %-8s %d/%-2d/%-6d %-4s [%-24s]=>[%-24s] (%s)' % \ (u, l, typ,1,p, id, t, zt, h) def rewind(sessions,n,criter,event) : i=n ( ut_time,ut_utime,zt_time,zt_utime,user,line,host,pid,sessid,type) =event i0=1 while i>i0 : i=i-1 closethis=0 if criter=='all' : closethis=1 if criter =='line': closethis=(sessions[i][5] == line) if closethis : i0=i if closethis : ( ut_t,ut_ut,zt_t,zt_ut,u,l,h,p,id,typ)=ev=sessions[i] if zt_t !=0 : if criter=='all' : continue if all_last : s="-ERR: " s=s+evdisp(ev,"",1) else : ev=sessions[i]=( ut_t,ut_ut,ut_time,ut_utime,u,l,h,p,id,typ) if all_last : s="-CLOS " s=s+evdisp(ev,"",1) if all_last : print s block = file.read(record_size) n=1 while block: ( type,pid,line,id,user,host,exit_1,exit_2,ut_session,ut_time,ut_utime,\ ip1,ip2,ip3,ip4,unused) = \ struct.unpack(fmt, block) user = unpackstr(user) # remove the null characters line = unpackstr(line) host = unpackstr(host) id = unpackstr(id) event = ( ut_time,ut_utime,0,0,user,line,host,pid,id,type) if type and all_last : s='USER: ' s=s+evdisp(event," -n/a-",0) print s if user in ['LOGIN','reboot','shutdown'] : "Crash & such : everybody logs off.." rewind(sessions,n,'all',event) if type == 7 and user : sessions[n]=event n=n+1 if type == 8 : rewind(sessions,n,'line',event) block=file.read(record_size) file.close() print "Number of entries : %d" %n evs=sessions.values() evs.sort(lambda x, y: cmp(y[0]+y[1]*1e-7,x[0]+x[1]*1e-7) ) i=0 for ev in evs: i=i+1 (ut_t,ut_ut,zt_t,zt_ut,u,l,h,p,id,typ)=ev if emul_last : print evdisp(ev,"",0) if datesec <360000 : # secret feature : giving a near-epoch date does that : if precision >0 and zt_t-ut_t < precision : continue if precision <0 and zt_t-ut_t > -precision : continue else : if datesec + precision < ut_t : continue if zt_t !=0 and zt_t + precision