有一段 Python CGI 脚本,用于检查一个进程是否正在运行,如果该进程未找到,则启动它。该进程本身是一个基于 web.py 的 Web 服务器。确保该进程正在运行后,尝试向其发出 URL 请求。这个想法是从 CGI 脚本的请求者重定向此请求的结果,基本上是想将查询重定向到监听不同端口的本地 Web 服务器。
这段代码如果在 shell 中先启动服务器,然后从非 CGI 请求中调用,可以正常工作。但是,如果尝试通过下面的 CGI 脚本启动进程,在调用 urllib2.urlopen 时就会抛出异常,表明连接被拒绝。
2、解决方案
问题在于 CGI 脚本和 Web 服务器进程运行在不同的用户帐户下。CGI 脚本通常由 Web 服务器用户运行,而 Web 服务器进程通常由另一个用户(例如 www-data)运行。因此,由 CGI 脚本启动的 Web 服务器进程无法访问 CGI 脚本尝试连接的端口。
要解决这个问题,需要确保 CGI 脚本和 Web 服务器进程都运行在同一个用户帐户下。这可以通过使用 suexec 配置 Apache 来实现。
1、在 Apache 配置文件中添加以下行:
SuexecUserGroup www-data www-data
2、将 CGI 脚本的权限更改为由 www-data 用户拥有:
chown www-data /path/to/cgi-script
3、重新启动 Apache。
现在,CGI 脚本和 Web 服务器进程都将运行在同一个用户帐户下,因此 CGI 脚本可以成功连接到由 Web 服务器进程监听的端口。
以下是经过修改后的代码,可以正常工作:
#!/usr/bin/python
import cgi, cgitb
cgitb.enable()
import os, re
import sys
import subprocess
import urllib2
urlbase = "http://localhost:8080/getimage"
imgserver = "/home/martin/public_html/cgi-bin/stlimgservermirror.py" # this is based on web.py
def findImgServerProcess():
r = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0]
return re.match(".*%s" % os.path.split(imgserver)[-1], r, re.DOTALL)
def ensureImgServerProcess():
if findImgServerProcess():
return True
os.environ['LD_LIBRARY_PATH'] = '/home/martin/lib'
fout = open("/dev/null", "w")
ferr = fout
subprocess.Popen([sys.executable, imgserver], stdout=fout, stderr=subprocess.STDOUT)
# use 'sleep' to ensure that the process will run after 'ensureImgServerProcess()' return
import time
time.sleep(1)
# now try and find the process
return findImgServerProcess() != None
def main():
if not ensureImgServerProcess():
print "Content-type: text/plain\n"
print "image server down!"
return
form = cgi.FieldStorage()
if form.has_key("debug"):
print "Content-type: text/plain\n"
print os.environ['QUERY_STRING']
else:
try:
img = urllib2.urlopen("%s?%s" % (urlbase, os.environ['QUERY_STRING'])).read()
except Exception, e:
print "Content-type: text/plain\n"
print e
sys.exit()
print "Content-type: image/png\n"
print img
if __name__ == "__main__":
main()