2011年2月9日 星期三

[應用程式] Step by Step Setup of Boa with PHP

Environment:Ubuntu10.4 LTS

一、編譯Boa
1.安裝必要的library
Lex(A Lexical Analyzer Generator)和Yacc(Yet Another Compiler-Compiler)
$ sudo apt-get install bison
$ sudo apt-get install flex
如果沒有安裝bison,在編譯時將會出現錯誤訊息如
yacc  -d boa_grammar.y
make: yacc: Command not found
make: *** [y.tab.c] Error 127
如果沒有安裝flex,在編譯時將會出現錯誤訊息如
yacc  -d boa_grammar.y
gcc  -g -O2 -pipe -Wall -I.   -c -o y.tab.o y.tab.c
y.tab.c: In function 'yyparse':
y.tab.c:1295: warning: implicit declaration of function 『yylex'
lex  boa_lexer.l
make: lex: Command not found
make: *** [lex.yy.c] Error 127

2.下載Boa原始檔
$ cd ~
$ wget http://www.boa.org/boa-0.94.13.tar.gz
$ tar zxvf boa-0.94.13.tar.gz

3.先查看一下原始檔,注意原始檔的變化
$ cd boa-0.94.13/src
$ ls
acconfig.h     boa.h         check_struct_for.m4  configure.in  globals.h    Makefile.in   read.c      sublog.c
aclocal.m4     boa_lexer.l   compat.h             defines.h     hash.c       mmap_cache.c  request.c   timestamp.c
alias.c        buffer.c      config.c             escape.c      index_dir.c  parse.h       response.c  util.c
boa.c          cgi.c         config.h.in          escape.h      ip.c         pipe.c        select.c    webindex.pl
boa_grammar.y  cgi_header.c  configure            get.c         log.c        queue.c       signals.c

4.configure
$ ./configure

5.編譯,將會出現錯誤
$ make
...
gcc  -g -O2 -pipe -Wall -I.   -c -o util.o util.c
util.c:100:1: error: pasting "t" and "->" does not give a valid preprocessing token
make: *** [util.o] Error 1

6.修改Boa的原始檔
$ vim compat.h
120 #define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff
改成
120 #define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff

7.再次編譯,並檢查編譯完成後的檔案
在編譯的過程中,有些c的source和header會動態產生,例如config.h, lex.yy.c, y.tab.c, y.tab.h,而完成編譯後,也會產生boa和boa_indexer等兩個二進位執行檔
$ make
$ ls
acconfig.h     boa_lexer.l          compat.h       configure.in  hash.o       Makefile      read.c      signals.o    y.tab.h
aclocal.m4     boa.o                config.c       defines.h     index_dir.c  Makefile.in   read.o      sublog.c     y.tab.o
alias.c        buffer.c             config.cache   escape.c      index_dir.o  mmap_cache.c  request.c   sublog.o
alias.o        buffer.o             config.h       escape.h      ip.c         mmap_cache.o  request.o   timestamp.c
boa            cgi.c                config.h.in    escape.o      ip.o         parse.h       response.c  timestamp.o
boa.c          cgi_header.c         config.log     get.c         lex.yy.c     pipe.c        response.o  util.c
boa_grammar.y  cgi_header.o         config.o       get.o         lex.yy.o     pipe.o        select.c    util.o
boa.h          cgi.o                config.status  globals.h     log.c        queue.c       select.o    webindex.pl
boa_indexer    check_struct_for.m4  configure      hash.c        log.o        queue.o       signals.c   y.tab.c

8.編譯完成,啟動Boa
由於設定檔預設在/etc/boa目錄下的boa.conf,如果不存在,需手動複製過去,否則會出現錯誤訊息
$ sudo ./boa
Could not chdir to "/etc/boa": aborting
$ sudo mkdir /etc/boa
$ sudo ./boa
Could not open boa.conf for reading.
$ sudo cp ../boa.conf /etc/boa
$ sudo ./boa
由於Boa的log預設寫入到/var/log/boa下,若該目錄不存在會出現錯誤訊息
[10/Feb/2011:06:40:54 +0000] log.c:73 - unable to dup2 the error log: Bad file descriptor
$ sudo mkdir /var/log/boa

9.啟動Boa,檢查連線狀態(Boa預設綁在所有IP的80上)
$ sudo ./boa
$ netstat -nutlp
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -    

10.為了方便啟動/停止/重啟Boa,我寫了一個簡單的shell script幫助管理
$ vim ~/boa.sh
#!/bin/bash
BOA=~/boa-0.94.13/src/boa

start(){
    $BOA
}

stop(){
    PID=`pidof boa`
    kill $PID
}

restart(){
    stop
    start
}

case "$1" in
    stop)
        stop
        ;;
    start)
        start
        ;;
    restart)
        restart
        ;;
    *)  
        echo "{start|stop|restart}"
esac
$ chmod 755 ~/boa.sh
使用方式
$ sudo ~/boa.sh {start|stop|restart}


二、編譯PHP for Boa
1.下載PHP原始檔
$ cd ~
$ wget http://tw2.php.net/distributions/php-5.3.4.tar.gz
$ tar zxvf php-5.3.4.tar.gz

2.configure
在不確定PHP在Boa上能編譯進多少功能的情況下,先停用大多數的功能吧
$ cd php-5.3.4
$ ./configure --disable-all

3.編譯
由於PHP在Boa只能以CGI的方式執行,因此我們只要編出php-cgi就可以了
這意思是說將PHP編譯成CGI解譯器,如此一來每次有PHP Script被解譯時,網站伺服器就衍生出(spawn)一個PHP解譯器的實體,由它負責解譯該Script,但是這種方式會降低執行效能
$ make
$ ls -al sapi/cgi/php-cgi
-rwxr-xr-x 1 owen owen 8786495 2011-02-10 05:03 sapi/cgi/php-cgi


三、將PHP以CGI的方式執行
1.寫個顯示組態的PHP網頁
$ sudo vim /var/www/info.php
<?php
phpinfo();
?>

2.修改cgi-bin的虛擬路徑
$ sudo vim /etc/boa/boa.conf
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
改成
ScriptAlias /cgi-bin/ /var/www/cgi-bin/

3.將php-cgi複製到cgi-bin目錄下
$ sudo cp ~/php-5.3.4/sapi/cgi/php-cgi /var/www/cgi-bin

4.以瀏覽器檢視該網頁
如果url的路徑包括cgi-bin,那麼Boa就會以cgi的方式處理,並把它後面的參數變成cgi的命令參數去執行,因此瀏覽的網址為 http://your.host.ip/cgi-bin/php-cgi/info.php
502 Bad Gateway
The CGI was not CGI/1.1 compliant. 
檢查error_log
$ sudo cat /var/log/boa/error_log
[10/Feb/2011:16:24:37 +0000] cgi_header: unable to find LFLF
從這訊息看不出真正的錯誤原因,因此我們在/etc/boa/boa.conf裡多加上CGI的log
$ sudo vim /etc/boa/boa.conf
CgiLog /var/log/boa/cgi_log
重新啟動Boa
$ sudo ~/boa.sh restart
再用瀏覽器檢視該網頁,http://your.host.ip/cgi-bin/php-cgi/info.php,並檢查cgi_log
$ sudo cat /var/log/boa/cgi_log
<p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This
means that a page will only be served up if the REDIRECT_STATUS CGI variable is
set, e.g. via an Apache Action directive.</p>
<p>For more information as to <i>why</i> this behaviour exists, see the <a href="http://php.net/security.cgi-bin">manual page for CGI security</a>.</p>
<p>For more information about changing this behaviour or re-enabling this webserver,
consult the installation file that came with this distribution, or visit 
<a href="http://php.net/install.windows">the manual page</a>.</p>
這段錯誤主要的意思就是因為以CGI模式執行PHP程式有可能造成安全上的問題,因此預設編譯php-cgi是會開啟force-cgi-redirect的設定...
* 解決方式之一,是在configure時關閉此一設定(--disable-force-cgi-redirect),但是這招只在5.2以下的版本有用,5.3的configure就沒有此一選項
* 解決方式之二,就是在php.ini設定此一選項,預設php.ini的位置在/usr/local/lib下
$ sudo vim /usr/local/lib/php.ini
[PHP]
cgi.force_redirect = 0
在php.ini設定中的cgi.force_redirect,是因為有些網站伺服器(如Apache)在設定Action指令時,會將http://your.server.ip/protected/script.php這類請求重導至http://your.server.ip/cgi-bin/php/protected/script.php,其中的php就是PHP的CGI解譯器執行檔,前者會檢查protected目錄的存取行為,而後者如果本身有指定,它就不會去檢查這各被保護的目錄,這就可能發生安全性的問題,因此可利用--enable-force-cgi-redirect修正,並配合--enable-discard-path=/path/to/php-cgi(版本4.x以下有用),或是在php.ini設定cgi.redirect_status_env=/path/to/php-cgi

5.重新啟動Boa後以瀏覽器檢視該網頁將可看到PHP的組態設定

將PHP以CGI執行的方法可能還有一個,使用原生的cgi模式解析,雖然我沒成功完成,但是步驟大概如下
1.加入cgi的解析
$ sudo vim /etc/boa.boa.conf
AddType application/x-httpd-cgi .cgi

2.撰寫info.cgi
$ sudo vim /var/www/cgi-bin/info.cgi
#!/usr/local/bin/php-cgi
<?php
phpinfo();
?>

3.修改成可讀寫的權限
$ sudo chmod a+x /var/www/cgi-bin/info.cgi

references:
* The Lex & Yacc Page
* Linux系統編譯boa-0.94-13出錯信息問題
* 在嵌入式Linux架設Boa Webserver
* 嵌入式boa服務器移植
* boa with php

4 則留言:

♀蔚藍海域♂ 提到...

* 解決方式之二,就是在php.ini設定此一選項,預設php.ini的位置在/usr/local/lib下?,
/usr/local/lib/這個底下我並沒有看到??謝謝!

Owen.Hsu 提到...

用tarball沒有指定安裝路徑與php.ini搜尋路徑的話預設我記得會指到/usr/local/lib,但並不表示該處會有php.ini,通常做法會是將source下的php.ini-x複製到該處後再做修改
若要確定預設php.ini路徑可用/path/to/php -i|grep php.ini查詢

♀蔚藍海域♂ 提到...

不好意思,再請教你!
我的/etc/boa/boa.conf
CGIPath /bin:/www/cgi-bin:/www
ScriptAlias /cgi-bin/ /www/cgi-bin/
----------------------------------
開啟網頁http://192.168.3.51/cgi-bin/php-cgi/info.php
得到錯誤
502 Bad Gateway
The CGI was not CGI/1.1 compliant.
也有依照你的建議修改php.ini
不過還是一樣。
------------------------------
另外我如果再www建立php.cgi檔案
#!/www/usr-bin/php-cgi


瀏覽網頁也是一樣的錯誤訊息,不過我如果直接用 ./php.cgi執行的話,是可以顯示出一堆的htm碼,想請教是我設定不正確嗎?
另外我編譯的要放在嵌入式設備上,所以沒使用./configure --disable-all,改成CC=arm-linux-gcc ./configure --host=arm-linux --disable-all

Owen.Hsu 提到...

不好意思因為留言被過濾成垃圾剛剛才看到...
第一個是先確定你PHP的版本是5.3以上無誤?再來是要確定你的config是正確的,比如說在boa.conf的DocumentRoot是/www無誤? 再來是你有把php-cgi複製到/www/cgi-bin/的目錄下無誤? 然後你的info.php是放在/www無誤? 最後如果出現一樣的錯誤訊息, 看看err_log和cgi_log是什麼,再做進一步的判斷
第二個你編譯的方式是正確的,應該是可以跑無誤,因為我也是跑在嵌入式系統上,但是實測結果Boa+PHP不是很穩,有時候browser送request會timeout掉