背景介绍
最近支持现场产品部署,由于系统并没有UI界面,开机只有一个命令行,IP地址不可用的时候,web配置管理也没办法使用,此时让用户来改一个IP地址的话,就得需要一定的专业知识了,这可不够人性啊。
所以特意研究了一下shell下能够用的界面,ncurses和dialog。
设计的软件流程如下
就是一个状态机,切换不同状态下的界面。
ncurses
这是一个开源的c语言库,用来创建用户交互的接口。整个流程的思路就是创建了一个窗口,然后开始在上半部分输出结果,最后一行为输入。
效果
库安装
CentOS下需要安装ncurses-devel这个库
yum install ncurses-devel
主函数
int main(int argc, char *argv[])
{
system_ver = check_system();
if (init_win() != 0)
{
printf("init_win fail!\n");
destroy_win();
return -1;
}
if(has_colors() == TRUE)
{
init_pair(1, COLOR_WHITE, COLOR_MAGENTA);
attron(COLOR_PAIR(1));
}
proc_user_cmd();
if(has_colors() == TRUE)
{
attroff(COLOR_PAIR(1));
}
stop();
return 0;
}
这里的has_colors() 是为了判断系统支持不支持彩色,支持的话就会设置前景色和背景色。
初始化函数
static int init_win()
{
initscr();
start_color();
raw();
keypad(stdscr, TRUE);
refresh();
memset(&s_win, 0, sizeof(s_win));
s_win.resp_win = newwin(BODY_HEIGHT, COLS, 0, 0);
if(s_win.resp_win == NULL) {
return -1;
}
leaveok(s_win.resp_win, TRUE);
scrollok(s_win.resp_win, TRUE);
wsetscrreg(s_win.resp_win, 0, resp_win_lines());
return 0;
}
创建了一个窗口,初始化并配置大小。
主状态机
static int proc_user_cmd()
{
bool run=true;
while(run)
{
if(g_sta_now == _wait_sel_ifname)
{
waite_sel_ifname();
}
else if(g_sta_now == _wait_sel_setpart)
{
waite_sel_setpart();
}
else if(g_sta_now == _wait_input_ip)
{
wait_input_ip();
}
else if(g_sta_now == _wait_input_netmask)
{
wait_input_netmask();
}
else if(g_sta_now == _wait_input_gateway)
{
wait_input_gateway();
}
else if(g_sta_now == _wait_input_dns1)
{
wait_input_dns1();
}
else if(g_sta_now == _wait_input_dns2)
{
wait_input_dns2();
}
else if(g_sta_now == _wait_save_ifname)
{
wait_save_ifname();
}
else
{
run=false;
}
}
return 0;
}
举一个选择接口的例子
static int waite_sel_ifname()
{
bool runnow=true;
clean_all_paras();
while(runnow)
{
clean_resp_win();
show_info_msg("Select interface");
get_all_ifnames();
wait_input_msg();
if (s_win.cmd_len > FLAG_LEN)
{
if ((strcasecmp(cmd_str(), "exit") != 0)&&(strcasecmp(cmd_str(), "q") != 0))
{
int num=atoi(cmd_str());
if((num>0)&&(num<=all_if))
{
g_sta_now = _wait_sel_setpart;
use_if=num;
alog("select ifname:");
alog(ifnames[use_if].ifname);
strcpy(save_ifname,ifnames[use_if].ifname);
strcpy(save_if_path,ifnames[use_if].cfg_file);
}
}
else
{
g_sta_now = _exit_code;
}
}
exit_input();
runnow =false;
}
return 0;
}
显示标题的函数如下
static void show_info_msg(char* infomsg)
{
s_win.cmd_line = 1;
s_win.cmd_y = 0;
s_win.cmd_x = FLAG_LEN;
s_win.cmd_len = FLAG_LEN;
memset(s_win.cmd, 0, sizeof(s_win.cmd));
strcpy(s_win.cmd, infomsg);
move(s_win.cmd_y, 0);
clrtoeol();
refresh();
mvprintw(s_win.cmd_y++, 0, "%s", s_win.cmd);
}
然后列出所有接口并记录。
等待输入的函数如下
static void wait_input_msg(void)
{
int cmd_count = 0;
s_win.cmd_line = 1;
s_win.cmd_y = MAX_Y;
s_win.cmd_x = FLAG_LEN;
s_win.cmd_len = FLAG_LEN;
memset(s_win.cmd, 0, sizeof(s_win.cmd));
strcpy(s_win.cmd, CMD_FLAG);
move(s_win.cmd_y, 0);
clrtoeol();
refresh();
mvprintw(s_win.cmd_y, 0, "%s", s_win.cmd);
refresh();
int ch;
while((ch=getch()) != KEY_Enter)
{
switch (ch)
{
case KEY_CTRL_C:
g_sta_now = _exit_code;
return ;
case KEY_UP:
if (cmd_count < RECORD_SUM)
{
//show_cmd_record(++cmd_count);
}
break;
case KEY_DOWN:
if (cmd_count > 0) {
//show_cmd_record(--cmd_count);
} else {
mvprintw(MAX_Y, 0, CMD_FLAG);
clrtoeol();
}
break;
case KEY_LEFT:
on_key_left();
break;
case KEY_RIGHT:
on_key_right();
break;
case KEY_TAB:
on_key_tab(cmd_str());
move(MAX_Y, s_win.cmd_x);
break;
case KEY_BackSpace:
on_key_backspace();
break;
case KEY_DC:
on_key_delete();
break;
default:
if (s_win.cmd_len < CMD_MAX)
{
on_key_normal(ch);
}
break;
}
refresh();
}
}
里面有很多操作,例如按下左右设置光标,退格键删除等。
完整代码下载路径,内部包含了代码和ncurses中文说明手册。
《基于ncurses开发配置网络图形化界面》
这个库的功能其实远比我写的这个强大,甚至可以制作游戏。
Dialog
俗话说有时候shell一句话,C语言可能要写100行代码 ,也不知道是谁说的。
所以这里找到了一个shell的模块,用来生成一系列对话框,这就是dialog命令
关于各种参数和用法,可以参考这个文章
《shell之dialog详解》
看一下效果
这个看上去是不是就很银杏啊。
首先要安装dialog命令,CentOS7下
yum install dialog
整个脚本就100多行,主体依旧是一个状态机。基本每一步就是将对话的结果保存为文件,最终确认保存的时候,将文本内容依次写入配置文件,并重启网络
while(( $run == 1 ))
do
case $Q_STATUS in
0)
dialog --menu "which interface do you want to set?" 15 30 8 $ALL_INF_LIST 2> _ifname.txt
Q_STATUS=1
Q_IFNAME=$(cat _ifname.txt)
;;
1)
dialog --menu "which part do you want to set?" 15 30 8 1 "ipaddr" 2 "netmask" 3 "gateway" 4 "dns1" 5 "dns2" 6 "save config" 7 "exit" 2> _setpart.txt
Q_SETPART=$(cat _setpart.txt)
if [ $Q_SETPART = "1" ];then
Q_STATUS=2
elif [ $Q_SETPART = "2" ]; then
Q_STATUS=3
elif [ $Q_SETPART = "3" ]; then
Q_STATUS=4
elif [ $Q_SETPART = "4" ]; then
Q_STATUS=5
elif [ $Q_SETPART = "5" ]; then
Q_STATUS=6
elif [ $Q_SETPART = "6" ]; then
Q_STATUS=7
elif [ $Q_SETPART = "7" ]; then
dialog --clear # “-clear”作用为清屏。
exit 0
else
echo "没有符合的条件"
fi
;;
2)
dialog --title "IP" --inputbox "Please enter ip" 9 30 2>_ipaddr.txt
Q_STATUS=1
;;
3)
dialog --title "Netmask" --inputbox "Please enter netmask" 9 30 2>_netmask.txt
Q_STATUS=1
;;
4)
dialog --title "Gateway" --inputbox "Please enter gateway" 9 30 2>_gateway.txt
Q_STATUS=1
;;
5)
dialog --title "DNS1" --inputbox "Please enter dns1" 9 30 2>_dns1.txt
Q_STATUS=1
;;
6)
dialog --title "DNS2" --inputbox "Please enter dns2" 9 30 2>_dns2.txt
Q_STATUS=1
;;
7)
dialog --title "Confirm" --yesno "Are you willing to save config?\n" 9 18
if [ $? !=0 ]; then # yes的返回码为0,“$?”为上一个命令的退出状态。
dialog --infobox "Thank you anyway!" 5 20
sleep 2
dialog --clear # “-clear”作用为清屏。
exit 0
else
if [ -f "./_ipaddr.txt" ]; then
Q_IPADDR=$(cat _ipaddr.txt)
sed -i '/IPADDR=/d' /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
echo IPADDR=$Q_IPADDR >> /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
fi
if [ -f "./_netmask.txt" ]; then
Q_NETMASK=$(cat _netmask.txt)
sed -i '/PREFIX=/d' /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
sed -i '/NETMASK=/d' /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
echo NETMASK=$Q_NETMASK >> /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
fi
if [ -f "./_gateway.txt" ]; then
Q_GATEWAY=$(cat _gateway.txt)
sed -i '/GATEWAY=/d' /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
echo GATEWAY=$Q_GATEWAY >> /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
fi
if [ -f "./_dns1.txt" ]; then
Q_DNS1=$(cat _dns1.txt)
sed -i '/DNS1=/d' /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
echo DNS1=$Q_DNS1 >> /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
fi
if [ -f "./_dns2.txt" ]; then
Q_DNS2=$(cat _dns2.txt)
sed -i '/DNS2=/d' /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
echo DNS2=$Q_DNS2 >> /etc/sysconfig/network-scripts/ifcfg-$Q_IFNAME
fi
##CentOS7
systemctl restart network
##CentOS8
##ifdown $Q_IFNAME;ifup $Q_IFNAME
dialog --infobox "Save config success!" 5 20
sleep 2
dialog --clear # “-clear”作用为清屏。
exit 0
fi
Q_STATUS=1
;;
*)
;;
esac
done
完整代码下载
《基于dialog命令开发的网络配置工具》
结束语
最近总是感觉睡不醒,脑子里倒是总有想法,就是时间不太够用。“内在有激情,但是还要从容不迫。”
今天就简单的讲一个小的现象,就是为什么动物的腿都是双数。
是因为动物从海里进化到陆地上的时候,进化为腿的是鱼类的鳍,腹部的鳍都是成对存在的,所以爬上来进化出来的腿,也是成对的。
简单而言,成对的腿有什么好处呢,就是为了不跑偏。