系列入口:编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客
本文介绍执行后台命令的shell.asp的实现。
目录
一、概述
二、主体代码
三、详解
3.1 参数
一、概述
这个功能就相当于一个终端,不过只能执行一个命令。有什么好处看自己,可以加入自己喜欢的特性。
入口:
别的不说了,主体代码是doPageShell()。
二、主体代码
主体代码如下:
bool doPageShell()
{
#ifdef _MS_VC
return true;
#else
FILE * fp;
string changedir=m_request.GetParam("changedir");
string curdir=m_request.GetParam("curdir");
string cmd=m_request.GetParam("command");
bool noform=(m_request.GetParam("noform")=="true");
bool term=(m_request.GetParam("term")=="true");
long bufsize=1024*1024;
char * buf=new char[bufsize];
if(NULL==buf)
{
m_respond.AppendBody("<P><FONT color=RED>内存不足</FONT><P>");
return true;
}
//切换路径
if(0!=curdir.size())
{
if(0!=chdir(curdir.c_str()))
{
m_respond.AppendBody("<P><FONT color=RED>设置初始路径出错</FONT><P>"+curdir+"<P>");
return true;
}
}
if(0!=changedir.size())
{
if(0!=chdir(changedir.c_str()))
{
m_respond.AppendBody("<P><FONT color=RED>切换工作路径出错</FONT><P>"+changedir+"<P>");
return true;
}
}
//执行命令
if(0==cmd.size())
{
m_respond.AppendBody("<P>空命令<P>");
}
else
{
if(0!=setpgid(getpid(),getpid()))
{
m_respond.AppendBody("设置进程组ID出错<P>");
}
if(NULL==(fp=popen((cmd+" 2>&1").c_str(),"r")))
{
m_respond.AppendBody("<P><FONT color=RED>无法执行,原因:popen error</FONT><P>");
if(!m_respond.Flush(m_s))return true;
}
else
{
int fd=fileno(fp);
int flags;
fd_set fdset;
struct timeval tv;
tv.tv_sec=300;
tv.tv_usec=0;
flags = fcntl(fd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
char * tmpp;
m_respond.AppendBody("开始执行 ");
m_respond.AppendBody(CHtmlDoc::HTMLEncode(cmd));
m_respond.Flush(m_s);
m_respond.AppendBody("<HR></HR><CODE>");
while(true)
{
FD_ZERO(&fdset);
FD_SET(fd,&fdset);
#ifdef _HPOS
int selectret=select(fd+1,(int *)&fdset,NULL,NULL,&tv);
#else
int selectret=select(fd+1,&fdset,NULL,NULL,&tv);
#endif
if(selectret<0)
{
LOG<<"select error"<<ENDE;
}
else if(0==selectret)
{//超时没有数据
m_respond.AppendBody("注意,长时间没有收到输出.");
if(!m_respond.Flush(m_s))
{
LOG<<"发送失败,客户端已断开,直接退出"<<ENDI;
if(term)
{
if(0!=kill(0,SIGTERM))
{
LOG<<"发送停止信号出错,shell会持续执行到命令结束"<<ENDE;
}
}
return true;
}
continue;
}
else
{
}
bool fileend=false;
while(true)
{
tmpp=fgets(buf,int(bufsize-1),fp);
if(NULL==tmpp)
{
//正常结束
if(0!=feof(fp))
{
fileend=true;
break;
}
if(EWOULDBLOCK==errno || EAGAIN==errno)
{//无数据
break;
}
else
{//出错结束
m_respond.AppendBody("<P><FONT color=RED>执行出错,原因:read error</FONT><P>");
m_respond.AppendBody(strerror(errno));
m_respond.Flush(m_s);
fileend=true;
break;
}
}
else
{
m_respond.AppendBody(LogToHtml(buf,false,false));
m_respond.AppendBodyHtmlScroll();
if(!m_respond.Flush(m_s))
{
LOG<<"发送失败,客户端已断开,直接退出"<<ENDI;
if(term)
{
if(0!=kill(0,SIGTERM))
{
LOG<<"发送停止信号出错,shell会持续执行到命令结束"<<ENDE;
}
}
return true;
}
}
}
if(fileend)break;
}
m_respond.AppendBody("</CODE><HR></HR>");
int ret=pclose(fp);
if(0!=ret)
{
if(WIFEXITED(ret))
{
sprintf(buf,"<FONT color=RED>执行完毕,返回代码 %d 。</FONT><BR>",WEXITSTATUS(ret));//(0xFF00&ret)/256);
}
else if(WIFSIGNALED(ret))
{
sprintf(buf,"<FONT color=RED>被信号终止,信号 %d 。</FONT><BR>",WTERMSIG(ret));
}
else if(WCOREDUMP(ret))
{
sprintf(buf,"<FONT color=RED>执行失败,COREDUMP。</FONT><BR>");
}
else
{
sprintf(buf,"<FONT color=RED>未知的返回值:%d。</FONT><BR>",ret);
}
}
else sprintf(buf,"执行完毕,返回代码 %d 。<BR>",ret);
m_respond.AppendBody(buf);
}
}
if(!noform)
{
char cwd[1024];
if(NULL!=getcwd(cwd,1024))
{
sprintf(buf,
"<FORM ACTION=\"/shell.asp\" METHOD=\"GET\" >\n"
"当前路径:%s<BR>"
"<INPUT TYPE=\"hidden\" NAME=\"curdir\" VALUE=\"%s\" >\n"
"切换路径到:<INPUT TYPE=\"text\" SIZE=\"30\" NAME=\"changedir\" ><BR>\n"
"Shell命令: <INPUT TYPE=\"text\" SIZE=\"30\" NAME=\"command\" VALUE=\"%s\">\n"
"<INPUT TYPE=SUBMIT VALUE=\"执行\" >\n"
"</FORM>\n"
,cwd,cwd,cmd.c_str());
}
else
{
sprintf(buf,"获取当前工作路径出错");
}
m_respond.AppendBody(buf);
}
delete[] buf;
return true;
#endif
}
三、详解
3.1 参数
(我还没写完)