树莓派4B中Nginx+fastcgi_wrap环境下C CGI无法将QUERY_STRING解析到argv[]
C CGI程序在Nginx+fastcgi_wrap环境下QUERY_STRING无法传入argv[]的问题
我是树莓派4B上的C CGI开发新手,用Nginx + fastcgi_wrap + spawn-cgi做练习,跟着《C Programming in Linux》第16页的内容操作。命令行直接运行程序时,QUERY_STRING能正常传到main函数的argv[]里,输出符合预期;但通过浏览器或curl访问时,QUERY_STRING没法传到argv[],只能用getenv获取到值。而在Apache环境下运行这个程序,QUERY_STRING能正常传入argv[]。下面是我的C代码、Nginx配置及相关运行输出,求帮忙排查。
命令行运行输出
$ ./hello3 my first Cproc Content-type:text/plain Hello, you are still learning C!! Number of arguments to the main function: 4 arguments number 0 is ./hello3 arguments number 1 is my arguments number 2 is first arguments number 3 is Cproc QUERY_STRING got by getenv: (null); env[0]=SHELL=/bin/bash env[1]=LANGUAGE=en_US.UTF-8 env[2]=NO_AT_BRIDGE=1 env[3]=PWD=/var/www/html/cgi-bin env[4]=LOGNAME=pi env[5]=XDG_SESSION_TYPE=tty env[6]=HOME=/home/pi env[7]=LANG=en_US.UTF-8 env[8]=LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: env[9]=SSH_CONNECTION=192.168.1.62 2295 192.168.1.118 22 env[10]=XDG_SESSION_CLASS=user env[11]=TERM=xterm env[12]=USER=pi env[13]=DISPLAY=localhost:10.0 env[14]=SHLVL=1 env[15]=XDG_SESSION_ID=4 env[16]=XDG_RUNTIME_DIR=/run/user/1000 env[17]=SSH_CLIENT=192.168.1.62 2295 22 env[18]=LC_ALL=en_US.UTF-8 env[19]=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games env[20]=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus env[21]=MAIL=/var/mail/pi env[22]=SSH_TTY=/dev/pts/0 env[23]=OLDPWD=/home/pi env[24]=TEXTDOMAIN=Linux-PAM env[25]=_=./hello3
curl访问输出
$ curl -i http://192.168.1.118/cgi-bin/hello3?my+first+Cproc HTTP/1.1 200 OK Server: nginx/1.14.2 Date: Thu, 28 Jul 2022 03:33:15 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive Hello, you are still learning C!! Number of arguments to the main function: 1 arguments number 0 is /var/www/html/cgi-bin/hello3 QUERY_STRING got by getenv: my+first+Cproc; env[0]=FCGI_ROLE=RESPONDER env[1]=SCRIPT_FILENAME=/var/www/html/cgi-bin/hello3 env[2]=QUERY_STRING=my+first+Cproc env[3]=REQUEST_METHOD=GET env[4]=CONTENT_TYPE= env[5]=CONTENT_LENGTH= env[6]=SCRIPT_NAME=/cgi-bin/hello3 env[7]=REQUEST_URI=/cgi-bin/hello3?my+first+Cproc env[8]=DOCUMENT_URI=/cgi-bin/hello3 env[9]=DOCUMENT_ROOT=/var/www/html env[10]=SERVER_PROTOCOL=HTTP/1.1 env[11]=REQUEST_SCHEME=http env[12]=GATEWAY_INTERFACE=CGI/1.1 env[13]=SERVER_SOFTWARE=nginx/1.14.2 env[14]=REMOTE_ADDR=192.168.1.118 env[15]=REMOTE_PORT=56758 env[16]=SERVER_ADDR=192.168.1.118 env[17]=SERVER_PORT=80 env[18]=SERVER_NAME=_ env[19]=REDIRECT_STATUS=200 env[20]=HTTP_HOST=192.168.1.118 env[21]=HTTP_USER_AGENT=curl/7.64.0 env[22]=HTTP_ACCEPT=*/* env[23]=LANG=en_US.UTF-8 env[24]=LANGUAGE=en_US.UTF-8 env[25]=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin env[26]=HOME=/var/www env[27]=LOGNAME=www-data env[28]=USER=www-data env[29]=INVOCATION_ID=99694992abd040ac81d526dddaa66988 env[30]=JOURNAL_STREAM=9:27356 env[31]=DAEMON_OPTS=-f
C代码
/*C code in …www/cgi-bin/*/ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[], char *env[]) { int i=0; printf("Content-type:text/plain\n\n"); printf("Hello, you are still learning C!!\n"); printf("Number of arguments to the main function: %d\n", argc); for(i=0; i<argc; i++) { printf("arguments number %d is %s\n", i, argv[i]); } printf("QUERY_STRING got by getenv: %s;\n\n", getenv("QUERY_STRING")); i=0; while(env[i]) { printf("env[%d]=%s\n",i,env[i]); i++; } return 0; }
Nginx配置(server段)
server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; index index.html index.htm index.php index.nginx-debian.html; server_name _; location / { try_files $uri $uri/ =404; } location ~ \.php$ { fastcgi_pass unix:/run/php/php7.3-fpm.sock; #fastcgi_pass 127.0.0.1:9000 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location /cgi-bin { gzip off; fastcgi_pass unix:/run/fcgiwrap.socket; include /etc/nginx/fastcgi.conf; #fastcgi.conf already includes SCRIPT_FILENAME; it's different from fastcgi_param; try_files $uri $uri/ /=404; } }
Apache环境运行输出
Hello, you are still learing C! Number of arguments to the main function:4 argument num 0 is /home/pi/cgidir/hello argument num 1 is my argument num 2 is first argument num 3 is Cproc Environmental variables: env[0] = HTTP_HOST=192.168.1.64 env[1] = HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0 env[2] = HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 env[3] = HTTP_ACCEPT_LANGUAGE=en-US,en;q=0.5 env[4] = HTTP_ACCEPT_ENCODING=gzip, deflate env[5] = HTTP_DNT=1 env[6] = HTTP_CONNECTION=keep-alive env[7] = HTTP_UPGRADE_INSECURE_REQUESTS=1 env[8] = PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin env[9] = SERVER_SIGNATURE=<address>Apache/2.4.54 (Debian) Server at 192.168.1.64 Port 80</address> env[10] = SERVER_SOFTWARE=Apache/2.4.54 (Debian) env[11] = SERVER_NAME=192.168.1.64 env[12] = SERVER_ADDR=192.168.1.64 env[13] = SERVER_PORT=80 env[14] = REMOTE_ADDR=192.168.1.62 env[15] = DOCUMENT_ROOT=/var/www/html env[16] = REQUEST_SCHEME=http env[17] = CONTEXT_PREFIX=/cgi-bin/ env[18] = CONTEXT_DOCUMENT_ROOT=/home/pi/cgidir/ env[19] = SERVER_ADMIN=webmaster@localhost env[20] = SCRIPT_FILENAME=/home/pi/cgidir/hello env[21] = REMOTE_PORT=4835 env[22] = GATEWAY_INTERFACE=CGI/1.1 env[23] = SERVER_PROTOCOL=HTTP/1.1 env[24] = REQUEST_METHOD=GET env[25] = QUERY_STRING=my+first+Cproc env[26] = REQUEST_URI=/cgi-bin/hello?my+first+Cproc env[27] = SCRIPT_NAME=/cgi-bin/hello
这是因为Apache和fastcgi_wrap对CGI程序的参数传递方式不同:
原因:
- Apache的CGI实现会自动将
QUERY_STRING按空格分割后传入程序的argv[]; - 而fastcgi_wrap(配合Nginx使用时)仅会把
QUERY_STRING作为环境变量传递,不会解析分割后传入argv[],这是它的默认行为。
- Apache的CGI实现会自动将
解决方案:
- 如果你希望和Apache环境下的行为一致,需要自己在代码中解析
QUERY_STRING环境变量,分割成参数后处理。示例代码如下:#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[], char *env[]) { int i=0; char *query_str = getenv("QUERY_STRING"); // 自定义参数列表,用于存储解析后的QUERY_STRING参数 char *custom_argv[100]; int custom_argc = 0; printf("Content-type:text/plain\n\n"); printf("Hello, you are still learning C!!\n"); // 解析QUERY_STRING(URL中空格会编码为+,按+分割) if(query_str != NULL) { char *token = strtok(query_str, "+"); while(token != NULL) { custom_argv[custom_argc++] = token; token = strtok(NULL, "+"); } } // 输出原始argv信息 printf("Number of arguments to the main function: %d\n", argc); for(i=0; i<argc; i++) { printf("arguments number %d is %s\n", i, argv[i]); } // 输出解析后的QUERY_STRING参数 printf("\nParsed QUERY_STRING arguments: %d\n", custom_argc);
- 如果你希望和Apache环境下的行为一致,需要自己在代码中解析
相关产品推荐
相关产品推荐

