背景
由于平时工作中用到很多机器,机器的位置在各个楼层,有些机器还是和其他同事公用的。每台机器安装的系统、内核,甚至CPU的硬件架构都不尽相同。
很多时候我们都是通过ssh远程连接到目标机器上进行工作。但是每次登录后都要先查看系统版本,CPU架构,是否有其他人使用,等等。因此想着做一个ssh欢迎界面 ,显示这些信息。
motd(message of the day,每日信息),每次ssh成功登录后都会显示motd的信息。因此想基于此进行实现。
先上效果:
motd介绍
每次系统登陆时,通过终端展示一些消息给登陆用户,如向用户提示欢迎信息,或提示使用系统的注意事项等等信息像公告板一样为所有用户提示信息。这就是motd。
写这篇文章时,笔者使用最多的操作系统是银河麒麟V10,部分架构的服务器版本是R系(Redhat系),桌面操作系统均为U系(Debian系)。motd在两种系统上是不一样的。U系采用动态motd,配置个性化的动态motd比较简单,但R系采用的是静态motd,不能实现每次ssh登录时动态显示系统信息。这需要用点”特殊办法“。
motd内容
系统的状态信息用脚本进行收集,美化格式后输出。这里给出一个正在使用的脚本,包含CPU架构,内核版本,开机时间,运行时间,登录用户数量(不准确),内存信息,挂载硬盘的使用情况。
#!/bin/sh
#----------------------
# Colors
c0="\e[m"
blod="\e[01m"
b_black="\e[40m"
f_black="\e[30m"
f_red="\e[31m"
f_green="\e[32m"
f_yellow="\e[33m"
f_blue="\e[34m"
f_magenta="\e[35m"
f_cyan="\e[36m"
f_white="\e[37m"
color_t1="${c0}${blod}${f_blue}"
color_t2="${c0}${blod}${f_white}"
#----------------------
# Kernel information
kernel="$(uname -sr)"
# Uptime
uptime="$(uptime -p | sed 's/up //;s/,//g')"
Up_runtime=`cat /proc/uptime| awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second =$1 % 60;printf("%d天%d时%d分%d秒",run_days,run_hour,run_minute,run_second)}'`
# Last boot time
Up_lastime=`date -d "$(awk -F. '{print $1}' /proc/uptime) second ago" +"%Y-%m-%d %H:%M:%S"`
# Current login users
Login_user=`last -a | grep "logged in" | wc -l`
# Archive
arch_info=`arch`
# CPU
cpu=`cat /proc/cpuinfo | grep "name" | uniq | cut -d ":" -f2 | sed 's/ //;s/(TM)//g;s/(R)//g;s/CPU//;s/ / /'`
## Memory
mem_used=0
while IFS=":" read -r a b; do
case $a in
"MemTotal")
mem_used=$((mem_used + ${b%kB}))
mem_total="${b%kB}"
;;
"Shmem")
mem_used=$((mem_used + ${b%kB}))
;;
"MemFree" | "Buffers" | "Cached" | "SReclaimable")
mem_used=$((mem_used - ${b%kB}))
;;
"MemAvailable")
mem_avail=${b%kB}
;;
esac
done < /proc/meminfo
if [ $mem_avail ]; then
mem_used="$(((mem_total - mem_avail) / 1024))"
else
mem_used="$((mem_used / 1024))"
fi
mem_total=$((mem_total / 1024))
mem_perc=$((mem_used * 100 / mem_total))
mem_used=`echo $mem_used | awk '{printf "%.2f", $1/1024}'`
mem_total=`echo $mem_total | awk '{printf "%.2f", $1/1024}'`
mem_label=GiB
memory="${f_yellow}${mem_used}${mem_label:-MiB} ${f_white}/ ${f_blue}${mem_total}${mem_label:-MiB} ${f_white}${mem_perc:+(${mem_perc}%)}"
# Mountpoints
mount_list=`df -h |grep "^/dev" |awk '{print $1}'`
str_line="-----------------------------------------------------------------"
BAR_LEN=25
color_bar () {
temp1="${1%?}"
temp2=$(($temp1*$BAR_LEN/100))
str="${b_black}["
# Add a color to the output
if [ $temp1 -gt 85 ] ; then
str= "${str}${f_red}"
elif [ $temp1 -gt 40 ] ; then
str="${str}${f_yellow}"
elif [ $temp1 -gt 25 ] ; then
str="${str}${f_green}"
else
#str+="${f_white}"
str="${str}${f_green}"
fi
# Fill with '='
for i in $(seq 1 $temp2)
do
str="${str}="
done
# reset the color
str="${str}${f_white}"
# Fill with '-'
for i in $(seq $temp2 $((BAR_LEN-1)))
do
str="${str}-"
done
str="${str}]"
echo "$str${reset}"
}
create_str () {
#param 1: str,
#param 2: max_len,
#param 3: left(1) or right(0) align
str="${1}"
max_len="${2}"
align="${3}"
s=""
if [ ${#str} -gt ${max_len} ]
then
#for i in $(seq 0 $((max_len-1)))
#do
s=$(echo ${str} | awk '{print substr($0,0,10)}')
s="${s}... "
#done
else
s="${str}"
if [ $align -eq 1 ]
then
for i in $(seq ${#str} $((max_len-1)))
do
s="${s} "
done
else
for i in $(seq ${#str} $((max_len-1)))
do
s=" ${s}"
done
fi
fi
echo "${s}"
}
# mount point line,
mpl_str () {
# target 14,size 8,avail 8,pcent 6
tmp1="${1}"
echo "${tmp1}" | while read -r a b c d; do
stra="$(create_str "${a}" 14 1)"
strb="$(create_str "${b}" 8 0)"
strc="$(create_str "${c}" 8 0)"
strd="$(create_str "${d}" 6 0)"
echo "${stra}${strb}${strc}${strd} $(color_bar ${d})"
done
}
#################################
# #
# Print Info #
# #
#################################
# \e[2J\e[0;0H 是后加的,用于清屏并将光标放在最上面
echo "\e[2J\e[0;0H${b_black}${blod}${f_white}${str_line}${c0}"
echo "${b_black}${blod}${f_blue} CPU架构: ${f_green}$(create_str "${arch_info}" 52 1)${c0}"
echo "${b_black}${blod}${f_blue} 内核版本: ${f_white}$(create_str "${kernel}" 51 1)${c0}"
echo "${b_black}${blod}${f_blue} 开机时间: ${f_white}$(create_str "${Up_lastime}" 51 1)${c0}"
echo "${b_black}${blod}${f_blue} 运行时间: ${f_white}$(create_str "${Up_runtime}" 55 1)${c0}"
echo "${b_black}${blod}${f_blue} Users: ${f_white}$(create_str "${Login_user}" 54 1)${c0}"
echo "${b_black}${blod}${f_blue} 内存: $(create_str "${memory}" 79 1)${c0}"
echo "${b_black}${blod} $(color_bar "${mem_perc}%")$(create_str "" 29 1)${c0}"
echo "${b_black}${blod}${f_white}${str_line}${c0}"
echo "${b_black}${blod}${f_white}$(create_str "挂载点 总量 可用 已用" 74 1)${c0}"
for i in ${mount_list}
do
#read a b c d<<< "$(df -h --output=avail,size,target,pcent $i |tail -1)"
disk_all=$(df -h --output=target,size,avail,pcent $i |tail -1)
s="${b_black}${blod}${f_white}$(mpl_str "${disk_all}")"
echo "${s}${c0}"
done
echo "${b_black}${blod}${f_white}${str_line}${c0}"
U系配置动态motd
直接使用脚本配置即可,比如上面的这个脚本,放入/etc/update-motd.d/目录下, 并chmod加上执行权限即可,其他原装的脚本,chmod a-x去掉执行权限就能屏蔽。
水平有限,脚本写的比较烂,稍微复杂了点,效果如下:
无需重启系统或sshd服务之类的,再次ssh远程登录就能生效。
【重点】R系配置动态motd
通过网上查资料,发现大多数人都使用/etc/profile文件实现R系OS的动态欢迎词,即,将上面的脚本放在/etc/profile文件的最后执行以下,并将输出重定向到/etc/motd文件。试了一下,可以实现。
这里给出我想到的另一个方案,即使用pam的pam_exec模块。
ssh登录会调用pam进行认证。刚开始准备在/etc/pam.d/login里面加上动态生成motd的规则,但是没有效果,所以改成了/etc/pam.d/sshd文件中,用于每次ssh登录后显示。
使用pam_exec.so很简单,加上下图这一行即可。pam_exec.so后面加上要运行的命令或者脚本即可。脚本内容就是将上面设置motd动态信息输出重定向到/etc/motd中。
吐糟
1.脚本中没有使用echo -e是因为U系中似乎只能用sh执行脚本,而且默认好像就是加了-e选项的,再加输出就不对了。但是R系里面又要加上-e选项。。。
2.银河麒麟V10,终端默认白色背景,但有时候又使用黑色背景的终端,所以脚本统一定义了黑色背景,就显得很。。。。复杂。
3.吐糟自己,R系上实现动态motd,明明用/etc/profile更简单,但就是非要搞点儿自己的想法。pam_exec模块能用,但是好像又没啥用。。。。