上犹电脑信息网我们一直在努力
您的位置:上犹电脑信息网 > 文件问题 > Thttpd源程序解析18 thttpd请求处理过程详解之CGI过程-cgi文件

Thttpd源程序解析18 thttpd请求处理过程详解之CGI过程-cgi文件

作者:上犹日期:

返回目录:文件问题

从之前讲述的really_start_request函数我们知道当设置的cgi_pattern不为空且文件具有其他用户可执行的权限且cgi_pattern与文件的扩展名称匹配时将调用CGI过程。

CGI过程根据用户的请求方式和请求参数调用存在于cgi_pattern文件目录中的可执行程序返回用户数据的过程。

程序流程

(1)判断已经使用的CGI的程序数量是否超过了设置的最大数量,如果是退出函数,反之继续执行。

(2)设置当前使用的CGI的程序数量加一。

(3)设置连接的文件描述符为阻塞模式。

(4)创建子进程,如果创建失败退出函数;如果创建成功子进程关闭服务器的文件描述符,调用cgi_child函数;父进程设置子进程超时处理函数,设置状态码等参数然后退出。

(5)设置连接的文件描述符的F_SETFD值为0。

(6)如果连接的文件描述符为标准输入输出或者是标准错误赋值连接的文件描述符为3。

(7)设置环境变量

(8)设置参数变量

(9)对于方式是POST方式,创建管道,创建失败返回500错误退出函数,创建成功将会创建子进程,创建子进程失败返回500错误退出函数,创建成功读取未读取完的数据写到管道的写数据端口,如果读管道的文件描述符不是标准输入复制为标准输入。对于不是POST方式设置连接的文件描述符的值为标准输入。

(10)复制连接的文件描述符到标准输入和标准错误

(10)设置子程序运行的优先级

(11)设置子程序的名称。

(12)调用子程序。

流程图

Thttpd源程序解析18 thttpd请求处理过程详解之CGI过程

源程序

/**cgi程序处理*/
static int cgi( httpd_conn* hc )
{
int r;
ClientData client_data;
/**判断是否是有效的CGI程序*/
if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit )
{
httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form,hc->encodedurl );
return -1;
}
++hc->hs->cgi_count;
/**设置为非阻塞模式*/
httpd_clear_ndelay( hc->conn_fd );
/**创建子进程*/
r = fork( );
if ( r < 0 )
{
syslog( LOG_ERR, "fork - %m" );
httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl );
return -1;
}
/**子进程处理函数*/
if ( r == 0 )
{
/* Child process. */
sub_process = 1;
httpd_unlisten( hc->hs );
cgi_child( hc );
}
/**父进程处理函数*/
/* Parent process. */
syslog( LOG_DEBUG, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename );
#ifdef CGI_TIMELIMIT
/* Schedule a kill for the child process, in case it runs too long */
client_data.i = r;
/**设置子进程程序运行的超时时间*/
if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
{
syslog( LOG_CRIT, "tmr_create(cgi_kill child) failed" );
exit( 1 );
}
#endif /* CGI_TIMELIMIT */
hc->status = 200;
hc->bytes_sent = CGI_BYTECOUNT;
hc->should_linger = 0;
return 0;
}
/**CGI处理函数*/
static void cgi_child( httpd_conn* hc )
{
int r;
char** argp;
char** envp;
char* binary;
char* directory;
/* Unset close-on-exec flag for this socket. This actually shouldn't
** be necessary, according to POSIX a dup()'d file descriptor does
** *not* inherit the close-on-exec flag, its flag is always clear.
** However, Linux messes this up and does copy the flag to the
** dup()'d descriptor, so we have to clear it. This could be
** ifdeffed for Linux only.
*/
/**设置这样的话将会保证打开的文件描述符在子进程中调用exec相关函数时保持打开 */
(void) fcntl( hc->conn_fd, F_SETFD, 0 );
/* Close the syslog descriptor so that the CGI program can't
** mess with it. All other open descriptors should be either
** the listen socket(s), sockets from accept(), or the file-logging
** fd, and all of those are set to close-on-exec, so we don't
** have to close anything else.
*/
closelog();
/* If the socket happens to be using one of the stdin/stdout/stderr
** descriptors, move it to another descriptor so that the dup2 calls
** below don't screw things up. We arbitrarily pick fd 3 - if there
** was already something on it, we clobber it, but that doesn't matter
** since at this point the only fd of interest is the connection.
** All others will be closed on exec.
*/
if ( hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO || hc->conn_fd == STDERR_FILENO )
{
int newfd = dup2( hc->conn_fd, STDERR_FILENO + 1 );
if ( newfd >= 0 )
{
hc->conn_fd = newfd;
}
/* If the dup2 fails, shrug. We'll just take our chances.
** Shouldn't happen though.
*/
}
/* Make the environment vector. */
/**设置环境变量*/
envp = make_envp( hc );
/* Make the argument vector. */
/**设置参数变量*/
argp = make_argp( hc );
/* Set up stdin. For POSTs we may have to set up a pipe from an
** interposer process, depending on if we've read some of the data
** into our buffer.
*/
/**对于POST方式且需要读取的数据大于已经读取的数据的处理*/
if ( hc->method == METHOD_POST && hc->read_idx > hc->checked_idx )
{
int p[2];
/**创建管道失败的处理*/
if ( pipe( p ) < 0 )
{
syslog( LOG_ERR, "pipe - %m" );
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
httpd_write_response( hc );
exit( 1 );
}
/**创建管道成功*/
r = fork( );
/**创建子进程失败处理*/
if ( r < 0 )
{
syslog( LOG_ERR, "fork - %m" );
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
httpd_write_response( hc );
exit( 1 );
}
/**创建子进程成功的处理*/
if ( r == 0 )
{
/* Interposer process. */
sub_process = 1;
/**关闭读管道*/
(void) close( p[0] );
/***/
cgi_interpose_input( hc, p[1] );
exit( 0 );
}
/* Need to schedule a kill for process r; but in the main process! */
(void) close( p[1] );
if ( p[0] != STDIN_FILENO )
{
(void) dup2( p[0], STDIN_FILENO );
(void) close( p[0] );
}
}
else
{
/* Otherwise, the request socket is stdin. */
/**对于连接的文件描述符不是标准输入文件描述符的处理
* 将当前的文件描述符的复制给标准输入文件描述符
*/
if ( hc->conn_fd != STDIN_FILENO )
{
(void) dup2( hc->conn_fd, STDIN_FILENO );
}
}
/* Set up stdout/stderr. If we're doing CGI header parsing,
** we need an output interposer too.
*/
if ( strncmp( argp[0], "nph-", 4 ) != 0 && hc->mime_flag )
{
int p[2];
if ( pipe( p ) < 0 )
{
syslog( LOG_ERR, "pipe - %m" );
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
httpd_write_response( hc );
exit( 1 );
}
r = fork( );
if ( r < 0 )
{
syslog( LOG_ERR, "fork - %m" );
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
httpd_write_response( hc );
exit( 1 );
}
if ( r == 0 )
{
/* Interposer process. */
sub_process = 1;
(void) close( p[1] );
cgi_interpose_output( hc, p[0] );
exit( 0 );
}
/* Need to schedule a kill for process r; but in the main process! */
(void) close( p[0] );
if ( p[1] != STDOUT_FILENO )
{
(void) dup2( p[1], STDOUT_FILENO );
}
if ( p[1] != STDERR_FILENO )
{
(void) dup2( p[1], STDERR_FILENO );
}
if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO )
{
(void) close( p[1] );
}
}
else
{
/* Otherwise, the request socket is stdout/stderr. */
if ( hc->conn_fd != STDOUT_FILENO )
{
(void) dup2( hc->conn_fd, STDOUT_FILENO );
}
if ( hc->conn_fd != STDERR_FILENO )
{
(void) dup2( hc->conn_fd, STDERR_FILENO );
}
}
/* At this point we would like to set close-on-exec again for hc->conn_fd
** (see previous comments on Linux's broken behavior re: close-on-exec
** and dup.) Unfortunately there seems to be another Linux problem, or
** perhaps a different aspect of the same problem - if we do this
** close-on-exec in Linux, the socket stays open but stderr gets
** closed - the last fd duped from the socket. What a mess. So we'll
** just leave the socket as is, which under other OSs means an extra
** file descriptor gets passed to the child process. Since the child
** probably already has that file open via stdin stdout and/or stderr,
** this is not a problem.
*/
/* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */
#ifdef CGI_NICE
/* Set priority. */
/**设置程序的优先级*/
(void) nice( CGI_NICE );
#endif /* CGI_NICE */
/* Split the program into directory and binary, so we can chdir()
** to the program's own directory. This isn't in the CGI 1.1
** spec, but it's what other HTTP servers do.
*/
directory = strdup( hc->expnfilename );
if ( directory == (char*) 0 )
{
binary = hc->expnfilename; /* ignore errors */
}
else
{
binary = strrchr( directory, '/' );
if ( binary == (char*) 0 )
{
binary = hc->expnfilename;
}
else
{
*binary++ = '\0';
(void) chdir( directory ); /* ignore errors */
}
}
/* Default behavior for SIGPIPE. */
#ifdef HAVE_SIGSET
(void) sigset( SIGPIPE, SIG_DFL );
#else /* HAVE_SIGSET */
(void) signal( SIGPIPE, SIG_DFL );
#endif /* HAVE_SIGSET */
/* Run the program. */
(void) execve( binary, argp, envp );
/* Something went wrong. */
syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename );
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
httpd_write_response( hc );
_exit( 1 );
}


相关阅读

关键词不能为空
极力推荐

电脑蓝屏_电脑怎么了_win7问题_win10问题_设置问题_文件问题_上犹电脑信息网

关于我们