常年混论坛的经验告诉我们:标题要醒目
我们所有台服务器,可能所里有些同学都不知道这台服务器的存在。我一直很好奇,究竟谁在用这台服务器,所以,我每次登陆都会下意识的输入”who”,来瞅一眼,呵呵。但是不断输入who,实在是强迫症的表现,早就想写这样一个东西来达到实时监测的效果。趁着这几天没事儿,就写了个简单的脚本。
首先说下原理:
所以,我的实现方法是:每隔一个适当短的时间(这个时间可以自己设定)用ps打印一次当前进程,在这个时间段内,只根据这一页ps page,提取用户的进程信息,这样就避免了很多不确定的情况。从这个“processes snapshot”里,提取用户名和必要的进程状态字段,比较前后进程状态的改变,判断被监测用户:(1)是否处于活动状态;(2)如果处于活动状态,他/她干了什么。
【8月9号补充:为了弥补无法捕捉短时间持续操作的不足,我又使用了cpu时间精度更高的top命令来改写了函数“pick_ps”的实现,所以在此脚本中,ps命令实际上被top命令所替代了】
然后,我们看一下监控效果:
下面是今晚做的一景监控,监控的时间间隔是1秒,总监控时长设置为100秒
Start monitoring at 2013-08-07 21:38:04 Wednesday CST
lanched by zhb.
The output directory /home/zhb/test/ has been made
Output dir is /home/zhb/test/
Monitor time is 100s
Converted to second is 100s
Time Interval is 1s
All users’ full names are listed below:
cxl: Chen Xuelong
DBH: Ding Baohong
… … … … … … …
(这一段是所有注册用户的完整姓名,略去)
All valid users’ full names have been obtained
Main work loop starts at 2013-08-07 21:38:04
Expected ending time is 2013-08-07 21:39:44
Lan Cuo log on
Created monitor file: /home/zhb/test/lancuo.monitor
Zhang Hongbo log on
Created monitor file: /home/zhb/test/zhb.monitor
zhb is doing “vim monitor.sh” at 21:38:04
Zhu Fuxin log on
Created monitor file: /home/zhb/test/zhufx.monitor
zhufx is doing “vim flow1.scr” at 21:38:04
zhufx is doing “vim map.scr” at 21:38:04
warning: Consumed cpu time exceeded the time interval!
lancuo is doing “vim log.YellowRiver.2013-07-24-1503″ at 21:38:51
lancuo is doing “vim log.YellowRiver.2013-07-24-1503″ at 21:38:52
lancuo is doing “vim update_daily.c” at 21:39:37
lancuo is doing “vim update_daily.c” at 21:39:38
Monitor task is finished at 2013-08-07 21:39:44 Wednesday CST
从输出的监控信息中可以看到,一开始有三个人登录在服务器中,至于这仨人是谁,认识的都知道
初始时刻(21:38:04)下的动作并不表示ta在那一时刻真的在做那件事,只是简单的把初始时刻中感兴趣的进程提出来而已。
而下面的兰措老师的活动,则真的表示她当时正在做这些。不过由于我提取信息的局限性,我无法捕捉兰措老师短时间持续的操作,
只能捕捉兰措老师的首次“同进程”操作,所以,一个很大的影响是,像echo、cat这种属于bash内置命令的情况,它们不会fork新的进程
,所以这个脚本是捕捉不到的,只是,本来就是写着玩儿的,这种方法本身就有一些局限性,求全责备就没劲了。
然后,我自己又做了下测试,我运行测试脚本之后,尝试做一些操作后,查看监控的反应,如下:
Main work loop starts at 2013-08-07 22:52:37
Expected ending time is 2013-08-07 22:57:37
Lan Cuo log on at 22:52:37
Zhang Hongbo log on at 22:52:37
zhb is doing “vim monitor.sh” at 22:52:37
zhb is doing “vim 1.txt” at 22:52:37
Zhu Fuxin log on at 22:52:37
zhufx is doing “vim flow1.scr” at 22:52:37
zhufx is doing “vim map.scr” at 22:52:37
warning: Consumed cpu time exceeded the time interval!
zhb is doing “vim 2.sh” at 22:54:00
zhb is doing “vim 2.sh” at 22:54:01
warning: Consumed cpu time exceeded the time interval!
warning: Consumed cpu time exceeded the time interval!
zhb is doing “vim zhb.monitor” at 22:55:13
zhb is doing “vim zhb.monitor” at 22:55:14
warning: Consumed cpu time exceeded the time interval!
Monitor task is finished at Wed Aug 7 14:57:37 UTC 2013
发现,确实可以捕捉首次操作,但是持续的操作很难捕捉到,这是因为实现方法的局限,可以参看我后面的源码
【8月9号补充:使用top命令替代ps命令之后,此情况得到很大改善,虽然没有完全解决此问题,但是效果好了很多】
可以想见,如果我把总监控时长设置为1天,那我基本上就可以获得在这一天中谁登陆了服务器以及他们大体都做了什么。
每个被监控者都会有一个监控文件,记录了ta的活动记录,比如这位同学的
Start monitoring zhufx at 2013-08-07 15:00:29 Wednesday UTC
0 2013-08-07,23:00:29 3 log on
– – – vim flow1.scr
– – – vim map.scr
61 2013-08-07,23:01:32 1 active
64 2013-08-07,23:01:35 1 active
265 2013-08-07,23:05:02 1 active
第一列表示监控循环周期,第二列是时间,第三列是该时刻操作数,第四列是ta的活动
“-”只是占位符,“active”表示在该时刻ta是处于活动状态的,但是做了什么不是很清楚。
需要说明一下,我做这个没有什么不良企图哈,纯粹是好奇害死猫,为了表明这一点,我把源码如数列出,其实实现上没有什么难度,就是需要时间。任何人都可以自由修改使用此脚本。而且,里面有很多实现,我知道肯定有欠妥的地方,欢迎批评指正。
说明:此脚本完全是用Bash编写(awk肯定也要用一些了),仓促完成,测试有限,bug在所难免,运行后果本人概不负责。
#!/bin/bash –
#===============================================================================
# FILE: monitor.sh
# USAGE: ./monitor.sh
# DESCRIPTION:
# AUTHOR: HONGBO ZHANG (zhb), zhanghongbo@itpcas.ac.cn
# ORGANIZATION: ITPCAS
# CREATED: 08/04/2013 17:26
# REVISION: 08/06/2013 20:59
#===============================================================================
MonitorTime=”300s” # unit: d, h, m or s, the unit is needed
TimeInterval=”1″ # unit: s, the unit is unnecessary
DIR=”$HOME/monitor/” # directory for output files, it’ll be created
#+ if not exist
#+ seperated with space
logfile=”${DIR}log.txt” # trace the flow of this program
MYPID=”$$” # temp file identifier, may not be used
SUFFIX=”monitor” # every monitored user has a record file, and this is the
#+ suffix of that file’s name
dbg=0 # turn on test mode
# NAME: log
# DESCRIPTION: print messages and write them to logfile
# PARAMETERS: $1- messages
# RETURNS: void
#——————————————————————————-
log ()
{
echo “$1″
echo “$1″ >> $logfile
} # ———- end of function log ———-
# NAME: timediff
# DESCRIPTION: compare two datetime
# PARAMETERS: $1- later time, $2- earlier time
# RETURNS: seconds between specified times
#——————————————————————————-
timediff ()
{
echo “$(($(date -d “$1″ +%s) – $(date -d “$2″ +%s)))”
return
} # ———- end of function timediff ———-
# NAME: get_fullname
# DESCRIPTION: get full name of each one in staff list(array)
# from /etc/passwd
# PARAMETERS: none
# RETURNS: full name list/array
#——————————————————————————-
get_fullname ()
{
local shipsym=”%” # the symbol to replace whitespace in a field
namelist=($(cat /etc/passwd | sort | awk -F”:” ‘
{
if ($4 >= 10000 && $4 < 20000) {
gsub(/[ ]/, “‘”${shipsym}”‘”, $5)
print $1″,”$5
}
}’))
local count=0;
local ind
local full
for item in ${namelist[@]}
do
# echo $item
ind=`expr index “$item” “,”`
names[$count]=${item:0:$((ind-1))}
full=${item:ind}
fullnamelist[$count]=${full//$shipsym/” “}
log “${names[$count]}: ${fullnamelist[${count}]}”
count=$((count + 1))
done
} # ———- end of function fullname ———-
{
for (( i=0; i<${#names[*]}; i++ ))
do
if [ “${names[i]}” == “$1″ ]
then
echo “${fullnamelist[$i]}”
return
fi
done
echo “”
} # ———- end of function fullnameof ———-
{
local name=$1
for (( i=0; i<${#names[*]}; i++ ))
do
if [ “${names[i]}” == “$1″ ]
then
echo “${i}”
return
fi
done
echo “”
} # ———- end of function nameindex ———-
# NAME: make_array
# DESCRIPTION: split passed multiline string into an array
# NOTE: a special manipulation is that the last blank line will
# be discarded. But if $1 is single balnk line, return “”
# PARAMETERS: $1- string containing “\n” to be splitted
# RETURNS: golbal var “Array” serves as the return-val reciever
#——————————————————————————-
make_array ()
{
Array=()
local str=$1; local i=0
local linefeed=”
”
local ind=`expr index “${str}” “${linefeed}”`
while [ $ind -ne 0 ]
do
Array[${i}]=${str:0:(${ind}-1)}
str=${str:${ind}}
let ‘i++’
ind=`expr index “${str}” “${linefeed}”`
done
if [ ! -z “${str}” ]; then
Array[${i}]=${str}
fi
} # ———- end of function make_array ———-
# NAME: pick_ps
# DESCRIPTION: only print procs belonging to valid users
# NOTE: to avoid spaces in “comm” field, all spaces among each
# real field are replaced with ‘\t’
# PARAMETERS: none
# RETURNS: valid procs
#——————————————————————————-
pick_ps ()
{
top -n 1 cb | awk ‘NR > 7 { print }’ | sort -k 2 | awk ‘
BEGIN {
namelist = “‘”${names[*]}”‘”
len = split(namelist, names, ” “)
}
for (i=1; i<=len; i++)
if (names[i] == $2) {
out = $2″\t”$1″\t”$8″\t”$11″\t”$12
for (j=13; j<=NF; j++)
out = out” “$j
print out
next
}
}
‘
} # ———- end of function pick_ps ———-
# NAME: procs_fieldindex
# DESCRIPTION: set and find specifid field’s index
# NOTE: str’s content depends on the implementation
# of function “pick_ps”. Modify it if “pick_ps”
# changed. The index should be 1-based for it is
# commonly used by awk
# PARAMETERS: $1- field
# RETURNS: index
#——————————————————————————-
procs_fieldindex ()
{
INDEX_ERROR=250
local str=(“USER” “PID” “STAT” “TIME” “COMM”)
local i=1
for item in “${str[@]}”
do
if [ “$item” == “$1″ ]; then
echo ${i}
return
fi
let ‘i++’
done
echo $INDEX_ERROR
} # ———- end of function procs_fieldindex ———-
# NAME: get_who
# DESCRIPTION: get current users from ps
# PARAMETERS: ps lines
# RETURNS: user array
#——————————————————————————-
get_who ()
{
local ind=$(procs_fieldindex “USER”)
echo “$Procs” | awk ‘
BEGIN {
puser=””
}
cuser = $”‘$ind'”
if (cuser != puser)
print cuser
puser = cuser
}
‘
} # ———- end of function get_who ———-
# NAME: get_diff
# DESCRIPTION: get diffrent items from $1 array compared with $2
# PARAMETERS: $1- output is from this; $2- compared with this
# RETURNS: multiline string
#——————————————————————————-
get_diff ()
{
make_array “$1″
local base=(“${Array[@]}”)
make_array “$2″
local ref=(“${Array[@]}”)
local eq=0
local i; local j
for ((i=0; i<${#base[@]}; i++)); do
eq=0
for ((j=0; j<${#ref[@]}; j++)); do
if [ “${base[${i}]}” == “${ref[${j}]}” ]; then
unset ref[${j}]
ref=(“${ref[@]}”)
eq=1
break
fi
done
if [ ${eq} -eq 0 ]; then
echo “${base[${i}]}”
fi
done
} # ———- end of function get_diff ———-
# NAME: psof
# DESCRIPTION: get procs of specified user
# PARAMETERS: $1- user name; $2-optional field index, 0 is whole line
# 1-5 depends on the implementation of func “pick_ps”
# RETURNS: procs’ info array
#——————————————————————————-
psof ()
{
local ind=${2-0}
echo “$Procs” | awk -F”\t” ‘
{
if ($1 == “‘$1′”) {
print $”‘$ind'”
}
}
‘
} # ———- end of function psof ———-
# NAME: get_textfile
# DESCRIPTION: get the editing text file from command arguments
# PARAMETERS: $1- comm
# RETURNS: file list separated by “\t” or just return “”
#——————————————————————————-
get_textfile ()
{
echo “$1″ | awk -F” ” ‘
BEGIN {
key=”vi vim nedit gedit cat”
}
function findtxt() {
if (NF < 2)
print “”
else {
j = 0
for (i=2; i<=NF; i++) {
if ($i !~ /^-/) {
sel[j] = $i
j++
}
}
if (sel[0] == “”) {
print “”
next
}
else
out = sel[0]
for (i=1; i<j; i++)
out = out”\t”sel[i]
print out
next
}
}
{
num = split(key, keys, ” “)
if ($1 ~ /\//) {
for (i=1; i<=num; i++)
if ($1 ~ “^[0-9a-zA-Z./]*/”keys[i])
findtxt()
}
else {
for (i=1; i<=num; i++)
if ($1 ~ “^[^0-9a-zA-Z_]*”keys[i])
findtxt()
}
}
‘
} # ———- end of function get_textfile ———-
# NAME: get_comm
# DESCRIPTION: analyse command field
# PARAMETERS: $1- the full command
# RETURNS: valid commands excluding login-relative commands
#——————————————————————————-
get_comm ()
{
echo “$1″ | awk -F” ” ‘
BEGIN {
key=”bash sshd sftp-server”
}
{
num = split(key, keys, ” “)
if ($1 ~ /\//) {
for (i=1; i<=num; i++)
if ($1 ~ “^[0-9a-zA-Z./]*/”keys[i]) {
print “”
next
}
}
else {
for (i=1; i<=num; i++)
if ($1 ~ “^[^0-9a-zA-Z_]*”keys[i]) {
print “”
next
}
}
}
‘
} # ———- end of function get_comm ———-
# NAME: comm_cute
# DESCRIPTION: filter out interesting commands
# PARAMETERS: $1- full comm
# RETURNS: comm
#——————————————————————————-
comm_cute ()
{
echo “$1″ | awk -F” ” ‘
BEGIN {
key=”vi vim nedit gedit awk cc gdb ddd gcc ifort man info”
}
{
num = split(key, keys, ” “)
if ($1 ~ /\//) {
for (i=1; i<=num; i++)
if ($1 ~ “^[0-9a-zA-Z./]*/”keys[i]) {
next
}
}
else {
for (i=1; i<=num; i++)
if ($1 ~ “^[^0-9a-zA-Z_]*”keys[i]) {
next
}
}
print “”
}
‘
} # ———- end of function comm_cute ———-
# NAME: procs_whochanged
# DESCRIPTION: update four global Arrays- “Cprocs”(Created processes),
# “Sprocs”(Sustaining processes), Kprocs”(Killed processes),
# “Aprocs”(Active processes)
# PARAMETERS: $1- cur procs; $2- pre procs
# RETURNS: none
#——————————————————————————-
procs_whochanged ()
{
local cp; local cn=0
local pp; local pn=0
local i=0; local k=0
make_array “$1″
cp=(“${Array[@]}”)
cn=${#cp[*]}
make_array “$2″
pp=(“${Array[@]}”)
pn=${#pp[*]}
local cpid; local ppid
local created=(); local killed=(); local sustained=(); local actived=()
local Cn=0; local Kn=0; local Sn=0; local An=0
local si=0; local ai=0
local ind2; local delc
local cstat; local pstat
local ctime; local ptime
local ind
ind=$(procs_fieldindex “PID”)
local i; local k
for ((i=0; i<${#cp[@]}; i++))
do
cpid=$(echo “${cp[${i}]}” | awk -F”\t” ‘{ print $”‘${ind}'” }’)
for ((k=0; k<${#pp[@]}; k++))
do
ppid=$(echo “${pp[$k]}” | awk -F”\t” ‘{ print $”‘${ind}'” }’)
if [ $cpid -eq $ppid ]
then
sustained[${si}]=”${cp[${i}]}”
let ‘si++’
ind2=$(procs_fieldindex “TIME”)
ctime=$(echo “${cp[${i}]}” | awk -F”\t” ‘{ print $”‘${ind2}'” }’)
ptime=$(echo “${pp[${k}]}” | awk -F”\t” ‘{ print $”‘${ind2}'” }’)
if [ “$ctime” != “$ptime” ]; then
actived[${ai}]=”${cp[${i}]}”
let ‘ai++’
else
ind2=$(procs_fieldindex “STAT”)
cstat=$(echo “${cp[${i}]}” | awk -F”\t” ‘{ print $”‘${ind2}'” }’)
pstat=$(echo “${pp[${k}]}” | awk -F”\t” ‘{ print $”‘${ind2}'” }’)
if [ “$cstat” != “$pstat” -o “${cstat:0:1}” == “R” ]; then
actived[${ai}]=”${cp[${i}]}”
let ‘ai++’
fi
fi
unset pp[${k}]
let ‘pn–‘
[ $pn -gt 0 ] && pp=(“${pp[@]}”) || Kn=0
delc=( ${delc[@]} “${i}” )
break
fi
done
done
killed=(“${pp[@]}”)
for item in “${delc[@]}”; do
[ ! -z “$item” ] && unset cp[${item}] && let ‘cn–‘
done
created=(“${cp[@]}”)
Cprocs=(“${created[@]}”)
Kprocs=(“${killed[@]}”)
Sprocs=(“${sustained[@]}”)
Aprocs=(“${actived[@]}”)
return
} # ———- end of function procs_whochanged ———-
{
local i
for ((i=0; i<${#Array[*]}; i++))
do
echo “${Array[$i]}”
done
} # ———- end of function array_tostring ———-
{
local uname=”$1″
local fname=”${DIR}${uname}.${SUFFIX}”
if [ -e $fname ]; then
return
else
log “Created monitor file: $fname”
echo “Start monitoring ${uname} at `date -u +’%F %T %A %Z’`” > $fname
fi
} # ———- end of function rec_create ———-
{
local uname=”$1″
local fname=”${DIR}${uname}.${SUFFIX}”
local mess_num=”${#Messages[@]}”
echo -e “$Cycle\t$Cdatetime\t$mess_num\t${Messages[0]}” >> $fname
local i
for ((i=1; i<$mess_num; i++))
do echo -e “-\t-\t-\t${Messages[${i}]}” >> $fname
done
} # ———- end of function rec_update ———-
{
local i
for ((i=0; i<${#Array[@]}; i++))
do
echo “$i: ${Array[$i]}”
done
} # ———- end of function printa ———-
# Start of main procedure
#——————————————————————————-
welcome=”Start monitoring at `date +’%F %T %A %Z’`
lanched by $USER.”
log “$welcome”
# Check user input
#——————————————————————————-
[ -d $DIR ] || mkdir -p $DIR && log “The output directory $DIR has been made”
log “Output dir is $DIR”
case “$MonitorTime” in
*[0-9]s|*[0-9]S )
MonitorTime=${MonitorTime%[s|S]}
MonitorTime=$(($MonitorTime * 1))
;;
*[0-9]m|*[0-9]M )
MonitorTime=${MonitorTime%[m|M]}
MonitorTime=$(($MonitorTime * 60))
;;
*[0-9]h|*[0-9]H )
MonitorTime=${MonitorTime%[h|H]}
MonitorTime=$(($MonitorTime * 60 * 60))
;;
*[0-9]d|*[0-9]D )
MonitorTime=${MonitorTime%[d|D]}
MonitorTime=$(($MonitorTime * 60 * 60 * 24))
;;
*)
log “MonitorTime input is wrong”
log “Program is exiting…”
exit 1
;;
esac || exit 1
log “Converted to second is ${MonitorTime}s”
*[0-9])
TimeInterval=$(($TimeInterval * 1))
;;
*[0-9]s | *[0-9]S)
TimeInterval=${TimeInterval%[s|S]}
TimeInterval=$(($TimeInterval * 1))
;;
*)
log “TimeInterval input is wrong”
log “Program is exiting…”
;;
esac || exit 1
log “Time Interval is ${TimeInterval}s”
# Main work flow
#——————————————————————————-
Messages=() # messages to output
Array=() # global array to pass array made from echo
Cprocs=() # Created procs
Kprocs=() # Killed procs
Sprocs=() # Sustaing procs
Aprocs=() # Active procs
Cycle=0 # global loop counter
Ctime=”” # current loop time(second)
Cdatetime=”” # current loop date time
Procs=”” # current processes collection, a multiline string
Userps=() # all users’ processes array at last loop, actually
#+ this is a 2-dim array whose each item is a string
#+ containing the processes of the user at the index
cur_users=()
get_fullname
log “All valid users’ full names have been obtained”
log “Main work loop starts at `date +%Y-%m-%d\ %H:%M:%S -d “1970-01-01 UTC $StartTime seconds”`”
EndTime=$(( StartTime + MonitorTime ))
log “Expected ending time is `date +%Y-%m-%d\ %H:%M:%S -d “1970-01-01 UTC $EndTime seconds”`”
while [ 1 ]; do
Ctime=$(date +%s)
Cdatetime=$(date +%Y-%m-%d,%H:%M:%S -d “1970-01-01 UTC $Ctime seconds”)
Cshorttime=$(date +%H:%M:%S -d “1970-01-01 UTC $Ctime seconds”)
if [ $Ctime -ge $EndTime ]; then
break
fi
if [ $dbg -eq 1 ]; then
if [ $Cycle -eq 0 ]; then
Procs=”zhb 1110 s 11:00 13:00 vi monitor.sh
zhufx 1112 s 11:00 13:00 gedit zhu1.sh
zhufx 1116 s 11:00 12:00 vi map.scr
zll 1113 s 11:00 12:00 nedit glacier.scr
”
fi
if [ $Cycle -eq 1 ]; then
Procs=”zhb 1111 s 11:00 12:00 vi monitor.sh
lancuo 1118 s 11:00 13:00 gdb
lancuo 1119 s 11:00 12:00 vi run.sh
zhufx 1116 s 11:00 12:00 vi map.scr
yangk 1115 s 11:00 12:00 ./yang
”
fi
if [ $Cycle -eq 2 ]; then
Procs=”zhb 1111 s 11:00 12:00 vi monitor.sh
lancuo 1118 s 11:00 13:00 gdb
lancuo 1119 s 11:00 12:00 vi run.sh
zhufx 1116 s 11:00 12:00 vi map.scr
yangk 1115 s 11:00 12:00 ./yang
”
fi
fi
cur_users_bak=”$cur_users”
bolters=”$(get_diff “$pre_users” “$cur_users”)”
if [ ! -z “$bolters” ]
then
make_array “$bolters”
bolters=(“${Array[@]}”)
for ((i=0; i<${#bolters[@]}; i++))
do
Messages=()
Messages[0]=”log out”
log “$(fullnameof ${bolters[$i]}) logged out at $Cshorttime”
rec_update ${bolters[$i]}
done
fi
make_array “$cur_users”
cur_users=(“${Array[@]}”)
make_array “$pre_users”
pre_users=(“${Array[@]}”)
for ((i=0; i<${#cur_users[*]}; i++))
do
Messages=()
mi=0
sname=${cur_users[$i]}
do
[ “$suser” == “$sname” ] && continue 2
done
log_flag=0
for ((k=0; k<${#pre_users[*]}; k++))
do
[ “$sname” == “${pre_users[${k}]}” ] && let “log_flag++” && break
done
if [ $log_flag -eq 0 ]; then
log “$fname log on at $Cshorttime”
Messages[$mi]=”log on”
let ‘mi++’
rec_create $sname
fi
ind=$(nameindex $sname)
# echo “$Cycle: $sname $ind
#uprocs:
#$uprocs
#Userps:
#${Userps[$ind]}”
procs_whochanged “$uprocs” “${Userps[$ind]}”
kn=${#Kprocs[*]}
an=${#Aprocs[*]}
[ $cn -gt 0 -o $kn -gt 0 -o $an -gt 0 ] || continue
combined=(“${Cprocs[@]}” “${Aprocs[@]}”)
for ((j=0; j<${#combined[*]}; j++))
do
comm=$(echo “${combined[${j}]}” | awk -F”\t” ‘{ print $”‘${ind}'” }’)
comm=”$(get_comm “$comm”)”
if [ ! -z “$comm” ]
then
Messages[$mi]=”$comm”
let “mi++”
comm=”$(comm_cute “$comm”)”
[ -z “$comm” ] || log “$sname is doing \”$comm\” at $Cshorttime”
fi
done
[ $mi -eq 0 ] && Messages[0]=”active”
rec_update $sname
done
pre_users=”$cur_users_bak”
#——– reset previous processes of users ———————————
num=${#Userps[*]}
for ((i=0; i<num; i++))
do
save=0
for ((j=0; j<${#cur_users[*]}; j++))
do
ind=$(nameindex “${cur_users[$j]}”)
[ $ind -eq $i ] && let ‘save++’ && break
done
[ $save -eq 0 ] && unset Userps[$i]
done
delta_t=$(( time2 – Ctime))
[ $(( TimeInterval – delta_t )) -ge 0 ] && sleep $(( TimeInterval – delta_t )) || echo “warning: Consumed cpu time exceeded the time interval!”
let ‘Cycle++’
done