这儿说的“后门”并不是教你做坏事,而是让你做好事,建立自己的调试东西更好地进行调试开发。咱们都知道,当程序发生反常过错时,咱们需求定位到过错,有时咱们还想,咱们在不修正程序的前提下,就能经过log来定位过错呢?有人会说,我在我的程序里加多点打印就好了,程序每做一步我就加一行打印,届时一查log就知道程序在哪一步死掉的了。这个办法在小程序里或许会行得通,可是,在一个大型体系,每秒的log到达几百条,那时咱们怎样能在这繁复的log里找出咱们想要的那条的log的?这工作量大得夸大。工程中的处理办法便是给自己的程序开个后门专门给开发人员来调试程序。
当然咱们经过后门能做的不只是是这些,具体来说,后门便是咱们程序眼和跑着的程序沟通的一道门。
建立这么一个程序后门主要有这么几个要害点:
在进程里开一个线程用于充任debug center
该线程经过fifo接纳开发人员传给它的指令
解析这些指令
用脚本建立简略的指令行界面
一、创立debug center线程
这个就没什么好说了,我运用了上篇文章《Linux编程之自界说音讯行列》所建立的音讯行列结构,将其间的msg_sender1改造为debug_center线程,作为咱们的程序后门,咱们跟程序交互便是从这儿开端的。
if(pthread_create(&debug_thread_id, NULL, (void*)debug_center, NULL)){ MY_LOG(FATAL,”create debug center fail!\n”); return -1;}
二、创立FIFO
为什么要创立FIFO(有名管道)?由于咱们需求跟咱们的程序进行通讯,咱们需求把咱们的指令告知程序,那就需求一个通讯途径,FIFO便是一个很好的挑选。咱们把咱们的指令写进管道,程序将指令从管道出,然后履行该指令,这姿态咱们程序后门的通讯模型就出来了。why处理了,是时分处理how了。
关于管道的操作,我是这么做的:
system(“rm /vob/ljsdpoenew3/exercise/debug_log”); //每次进入debug center咱们都将本来的fifo文件删去,防止影响后边操作rc = mkfifo(“/vob/ljsdpoenew3/exercise/debug_log”, 0666); //创立fifoif(rc < 0){ MY_LOG(DEBUG, "make fifo fail!\n"); pthread_exit(0);} fp = fopen("/vob/ljsdpoenew3/exercise/debug_log", "r"); //翻开fifo,读取指令if(fp == NULL){ MY_LOG(DEBUG, "open debug_log fail!\n"); pthread_exit(0);}
读fifo咱们处理了,那怎样将咱们的指令写进fifo呢?这儿我计划运用shell的read指令,文章后边会解说怎样完成。
三、解析指令
解析指令又能够分为两个过程:
将从fifo获得数据进行格局解析,比方我界说了d d的意思是display debug,即显现现在的debug等级,那么咱们程序就得首要对原始数据进行格局处理。
将指令进行指令解析,履行相应操作。
格局处理:
static int get_args(FILE *inputFile){ char tmpBuffer[100]; char *line = tmpBuffer; char separator[] = ” ,\n\t”; char *token; int i; char eof; int num = 0; eof = !fgets(line, sizeof(tmpBuffer), inputFile); if (eof) return num; token = strtok(line, separator); while (num < MAX_NUM_ARGS && token) { strcpy(args[num], token); num++; token = strtok(NULL, separator); } for (i = num; i < MAX_NUM_ARGS; i++) args[i][0] = 0; return num;}
指令解析:
switch(args[0][0]) //解析指令,看每个指令对应哪些意思 { case 'd': //display switch(args[1][0]) { case 'q': //display queue show_MQ(fd); break; case 'd': //display debug show_debug_level(fd); break; default: help_manual(fd); break; } break; case 's': //set switch(args[1][0]) { case 'd': //set debug level n = atoi(args[2]); //将字符串转化为整数 fprintf(fd,” debug level change from %d to %d”,global.debug_level,n); global.debug_level = n; //更改log等级 break; default: help_manual(fd); break; } break; default: help_manual(fd); break; }
四、建立debug界面
先上界面图:
咱们敲的每个指令都是经过该界面传递到程序那头的,比方“d d”就表明展现出现在体系运转时的log等级,而“s d”便是设置咱们想要看的log等级,这样咱们就能够完成经过程序后门动态修正程序走向了。
那该怎样完成这个看似简略的交互界面呢?完成该交互界面,有几个要害点:
咱们需将程序的打印输出重定向到一个文件里
运用shell脚本读出文件的内容
咱们输入的指令需写入到fifo中
以上三点便是做成界面的最重要的技术问题。
重定向输出
一开端我是计划将规范输出、规范过错都重定向到文件里的额,可是想了想,仍是想直接把打印内容直接输出到文件就好了。比方这样:
fd = fopen(“/vob/ljsdpoenew3/exercise/debug_log2”, “w+”); if(fd == NULL) { MY_LOG(DEBUG, “open debug_log2 fail!\n”); pthread_exit(0); }fprintf(fd,” debug level change from %d to %d”,global.debug_level,n);
这样咱们在debug center发生的打印都输出到文件debug_log2上了。
2.运用脚本读出内容且将内容写入fifo
要做到这点需求shell编程的简略常识,咱们怎样将咱们的输入放到fifo呢?咱们怎样把log文件的内容读出来显现在显现屏呢?我想到首要是再写个client,进行进程间通讯嘛,将指令写到fifo,一起也读出log文件的内容。可是,我发现运用shell就能够做到以上的事了,并且只需花费几行代码。
#!/bin/bash my_inp_fifo=/vob/ljsdpoenew3/exercise/debug_log # 指定fifo文件stty erase ^H while [ true ];do read inp #循环读入用户输入 if [ “$inp” == “quit” ];then #quit代表完毕该界面 exit 0 fi echo $inp > $my_inp_fifo #写入fifo cat debug_log2.txt #显现log的内容done
那看看这个指令行界面跑起来是怎样的吧!
首要咱们运转server进程
sever进程不断发生音讯并处理音讯。
咱们翻开另一个窗口,并履行脚本./test.sh,进入界面
咱们运用了d d指令看到了程序此刻的debug等级,用d q看出了程序音讯行列的状况。
咱们运用了s d指令将debug level设置为0,此刻屏幕没有任何打印输出,当咱们在运用s d指令将level设置为-1(行将一切方位一),此刻一切打印等级都翻开了,屏幕又开端张狂打印了。也就说,咱们经过后门控制了程序,这儿咱们只是只是修正了程序的log等级,当然咱们还能够做更多的事,只需按照这个结构往里面加指令,以及对应的处理操作,就能够完成了。
五、总结
所谓后门,便是一个能够控制程序的接口,这个接口只是用于开发者调试开发,不会开放给客户。所以这个后门的效果十分巨大,所以是开发者调试程序的一大利器。有人会想,我想用socket来替代fifo进行进程通讯能够不,这样就能够做到长途主机控制程序了。我觉得是能够的,可是感觉运用telnet到意图主机再运转脚本操作比较安全。
最终给出源代码结构
1 #include 2 #include