统计用户在线时长
1. 获取用户登入与登出系统信息
在/var/log/
文件下有3个日志文件,记录了与用户登录有关的信息。
/var/run/utmp
: It contains information about the users who are currently logged onto the system. Who command is used to fetch the information from the file./var/log/wtmp
: It contains historical utmp. It keeps the users login and logout history. The last command uses this file to display the information./var/log/btmp
: It contains bad login attempts.
wtmp
和btmp
是可执行文件,使用命令last
和lastb
可以取取。
root@jmu-cs:/var/log# file -i wtmp
wtmp: application/x-dbt; charset=binary
root@jmu-cs:/var/log# file -i btmp
btmp: application/octet-stream; charset=binary
(1)utmp
utmp
记录了当前谁在使用系统,使用命令who
可以读取utmp
文件中的内容,举例:
# who
qiankun tty1 2022-04-21 00:17
root tty2 2022-04-21 00:23
root pts/0 2022-05-19 17:55 (172.20.11.16)
(2)wtmp
wtmp
记录了用户登入与登出的历史信息。使用命令last
可以读取wtmp
文件中的内容,举例:
last wtmp -Fw > last.dat
last -f wtmp -Fw > last.dat
其中,
-f
(--file
),指定wtmp文件,若无指定,默认为/var/log/wtmp
-F
(--fulltimes
),显示完整的登入与登出的日期与时间。没加-F
显示Mon May 16 15:19 - 15:55
,加了-F
显示Mon May 16 15:19:21 2022 - Mon May 16 15:55:26 2022
-w
(--fullnames
),显示完整用户名和域名
![Tip]
上完实验课应及时读取用户登入也登出信息(或者将
/var/log/wtmp
备份),否则随着系统运行,会覆盖之前的记录。
(3)btmp
btmp
记录了用户登录失败的信息,使用命令lastb
可以读取btmp
文件中的内容,举例:
# lastb -Fw
u20202121007 ssh:notty 172.21.4.49 Sat Jun 4 00:50:28 2022 - Sat Jun 4 00:50:28 2022 (00:00)
u20202121007 ssh:notty 172.21.4.49 Sat Jun 4 00:50:12 2022 - Sat Jun 4 00:50:12 2022 (00:00)
u20202121007 ssh:notty 172.21.4.49 Sat Jun 4 00:50:05 2022 - Sat Jun 4 00:50:05 2022 (00:00)
btmp begins Sat Jun 4 00:50:05 2022
2. 统计用户在线时长
除了统计用户在线时长,也统计用户自动登录次数,用于识别使用客户端设置自动登录的行为,核心代码如下:`
def get_username_duration_times(filename, start_dt, end_dt, d_user_name_info):
d_username_duration_times = AutoVivification()
# calculate the overlapping intervals
d_username_lists_overlapping = dict.fromkeys(d_user_name_info) # username : a list of [overlapping_start, overlapping_end]
with open(filename, 'r') as f:
for line in f.readlines()[-3::-1]: # skip the last two line, iterate in reverse order
#for line in f:
# stats ends util the empty line
'''
if not line.strip():
break
'''
l = line.split()
username = l[0]
if username not in d_user_name_info:
continue
# get the start time of period
str_dt = ' '.join(l[3:8]) # Tue Mar 16 11:44:57 2021
period_start = parse(str_dt)
# get the end time of period
if 'still logged in' in line:
period_end = end_dt
elif 'down' in line:
# root pts/1 36.249.132.196 Sun Mar 7 21:59:04 2021 - down (00:06)
duration = l[-1] # (00:06)
period_end = period_start + datetime.timedelta(hours=float(duration[1:3]), minutes=float(duration[4:6]))
else:
str_dt = ' '.join(l[9:14]) # Tue Mar 16 11:44:57 2021
period_end = parse(str_dt)
# not in the specified range
if period_end<=start_dt or period_start>=end_dt:
continue
# get the real interval with specified time range
if period_start < start_dt:
period_start = start_dt
if period_end > end_dt:
period_end = end_dt
# get the overlapping duration considering mupltiple login
if not d_username_lists_overlapping[username]: # init a loop
d_username_lists_overlapping[username] = [[period_start, period_end]]
else:
old_overlapping = d_username_lists_overlapping[username][-1]
if old_overlapping[1] < period_start:
d_username_lists_overlapping[username].append([period_start, period_end])
else:
d_username_lists_overlapping[username][-1] = [old_overlapping[0],
max(old_overlapping[1], period_end) ]
# detect autologin, stats
td = period_end - period_start
duration = td.total_seconds()
if (username in d_username_duration_times) and (duration in d_username_duration_times[username]):
d_username_duration_times[username][duration] += 1
else:
d_username_duration_times[username][duration] = 1
return d_username_lists_overlapping, d_username_duration_times