09
03

lighttpd+CGI+ipkg-遠端韌體更新的好幫手

寫這篇文章時,我突然想到周星馳演的一部電影,”國產007”,裡面有位”國家級研究人員”,研發了一種集10種不同殺人武器的兇器,好像叫”要你命3000”,結果這人間兇器一拿出來,居然是把什麼菜刀,電鋸,王水…等,通通用繩子綁在一起,哇靠,看到這樣的”武器”真想馬上飆髒話,不過這樣的武器倒也蠻搞笑的,能不能拿的動還是問題

話題回到這篇文章的主題”遠端韌體更新”,以後簡稱為”UI upgrade”,在一般網路裝置上,最常看到的,就是提供網頁的介面讓使用者更新網路裝置的韌體,而這樣的功能在實作上需要準備
(1)Web server:lighttpd or goahead web server(goahead已經不更新它的web server)
(2)可更新目標板軟體的工具:ipkg or busybox dpkg
(3) html和cgi:自己寫
實做時我選擇lighttpd+ipkg,而網頁的部份採用html+cgi,以下會分為三段介紹這三個實作

(1)lighttpd porting:
請至官網下載目前最新的版本1.4.19,並交叉編譯,交叉編譯(Cross-Compiling)這種有configure script的軟體有些小技巧,因為configure script需要使用者目前開發環境的所有設定,所以可以寫一個cross-compile shell script檔案先export開發環境的變數和設定configure的參數(輸入./configure –help可看到更多可設定的選項),這樣子做可以避免libtool地雷, cross-compile shell script的範例如下

  1. #! /bin/sh
  2.  
  3. export PATH=$PATH:/usr/local/eldk/usr/bin/
  4. export CPPFLAGS="-I/usr/local/eldk/arm/usr/include" 
  5. export LDFLAGS="-ldl" 
  6. export CFLAGS="" 
  7. export AR=arm-linux-ar 
  8. export AS=arm-linux-as 
  9. export LD=arm-linux-ld 
  10. export RANLIB=arm-linux-ranlib 
  11. export CC=arm-linux-gcc 
  12. export NM=arm-linux-nm 
  13. export ARCH=arm 
  14.  
  15. ./configure --target=arm-linux \
  16. --host=arm-linux \
  17. --build=arm-linux-gnu \
  18. --prefix=/usr/local/eldk/lighthttpd \
  19. --exec-prefix=/usr/local/eldk/lighthttpd \
  20. --with-pcre

所以編譯的步驟就是,先執行寫好的cross-compile shell script,然候執行make,最後make install,install的路徑就是上面cross-compile shell script中的—prefix和exec-prefix所指到的資料夾路徑

make install會把要安裝的binary和library都擺在我指定的資料夾/usr/local/eldk/lighthttpd下,把library都複制一份到目標板上,並且把lighttpd執行檔也傳到目標板的/bin資料夾下,接下來就是設定lighttpd.conf檔,設定時要特別注意以下幾段

[server.modules]

  1. server.modules = ( 
  2. # "mod_rewrite",
  3. # "mod_redirect",
  4. "mod_alias",
  5. "mod_access",
  6. # "mod_cml",
  7. # "mod_trigger_b4_dl",
  8. # "mod_auth",
  9. # "mod_status",
  10. # "mod_setenv",
  11. "mod_fastcgi",
  12. # "mod_proxy",
  13. # "mod_simple_vhost",
  14. # "mod_evhost",
  15. # "mod_userdir",
  16. "mod_cgi",
  17. # "mod_compress",
  18. # "mod_ssi",
  19. # "mod_usertrack",
  20. # "mod_expire",
  21. # "mod_secdownload",
  22. # "mod_rrdtool",
  23. "mod_accesslog" )

[server.document-root]

  1. server.document-root = "/www/"

把以下幾段註解掉,因為沒有support PCRE

  1. #url.access-deny = ( "~", ".inc" )
  2.  
  3. #$HTTP["url"] =~ "\.pdf$" { 
  4. # server.range-requests = "disable"
  5. #}

[CGI module]

  1. #### CGI module 
  2. cgi.assign = ( ".pl" => "/usr/bin/perl",
  3. ".cgi" => "" ) 
  4.  
  5. alias.url = ( "/cgi-bin" => "/www/cgi-bin" )

記得要把libdl相關的library傳到目標板的/lib資料夾下,執行lighttpd時所下的命令為lighttpd –f [lighttpd config file],用daemon方式執行於背景

(2)ipkg porting
ipkg cross compile的方式與lighttpd相同,但這邊要先修個bug,lighttpd執行cgi process時並沒有把目前linux環境變數複制到cgi執行環境,所以ipkg用getenv拿到的環境變數都是null,但ipkg沒有檢查指標是否為null,就逕行strdup,所以會讓程式crash

修改ipkg_cmd.c,找到ipkg_prep_intercepts這個function,並修改成如下範例

  1. ipkg_intercept_t ipkg_prep_intercepts(ipkg_conf_t *conf) 
  2. { 
  3. ipkg_intercept_t ctx;
  4. char *newpath;
  5. int gen;
  6.  
  7. ctx = malloc (sizeof (*ctx));
  8. //ctx->oldpath = strdup (getenv ("PATH"));//經典錯誤,用指標卻不檢查內容
  9. if (getenv ("PATH")==NULL) 
  10. ctx->oldpath=strdup("/sbin:/usr/sbin:/bin:/usr/bin");
  11. else ctx->oldpath = strdup (getenv ("PATH"));
  12. sprintf_alloc (&newpath, "%s/ipkg/intercept:%s", DATADIR, ctx->oldpath);
  13. setenv ("PATH", newpath, 1);

ipkg詳細的用法請參考ipkg-輕量級套件管理系統

(3) html和cgi

用c寫cgi的方法請參考用pure C寫CGI的輔助工具-CGIC library,底下是我firmware upgrade cgi的c原始碼

  1. #include <stdio.h> 
  2. #include "cgic.h" 
  3. #include <string.h> 
  4. #include <stdlib.h> 
  5.  
  6. #define SERVER_NAME cgiServerName
  7.  
  8. void File() 
  9. { 
  10. cgiFilePtr file;
  11. char name[1024];
  12. char contentType[1024];
  13. char buffer[1024];
  14. int size;
  15. int got;
  16. FILE *fp;
  17. char temp[256],fwop[1024];
  18. int realwrite;
  19. int status;
  20.  
  21. if (cgiFormFileName("file", name, sizeof(name)) != cgiFormSuccess) { 
  22. fprintf(cgiOut,"<p>No file was uploaded.<p>\n");
  23. return;
  24. } 
  25. fprintf(cgiOut, "The filename submitted was: ");
  26. cgiHtmlEscape(name);
  27. sprintf(temp,"touch /tmp/%s",name);
  28. system(temp);
  29. fprintf(cgiOut, "<p>\n");
  30. cgiFormFileSize("file", &size);
  31. fprintf(cgiOut, "The file size was: %d bytes<p>\n", size);
  32. cgiFormFileContentType("file", contentType, sizeof(contentType));
  33. //fprintf(cgiOut, "The alleged content type of the file was: ");
  34. //cgiHtmlEscape(contentType);
  35. fprintf(cgiOut, "<p>\n");
  36. if (cgiFormFileOpen("file", &file) != cgiFormSuccess) 
  37. { 
  38. fprintf(cgiOut, "Could not open the file.<p>\n");
  39. return;
  40. } 
  41. fprintf(cgiOut, "<pre>\n");
  42.  
  43. sprintf(temp,"/tmp/%s",name);
  44. fp=fopen(temp,"w");
  45.  
  46. while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) ==cgiFormSuccess) 
  47. { 
  48.  
  49. fwrite(buffer,1,got,fp);
  50. //cgiHtmlEscapeData(buffer, got);
  51. //cgiHtmlEscapeData(buffer, got);
  52. } 
  53. fclose(fp);
  54. fprintf(cgiOut, "</pre>\n");
  55. sprintf(fwop,"/bin/ipkg -V 99 -f /etc/ipkg.conf install %s >/dev/null",temp);
  56. system(fwop);
  57.  
  58. fprintf(cgiOut,"%s\n<p>",fwop);
  59. fprintf(cgiOut,"<p>Firmware upgrade finished\n");
  60. cgiFormFileClose(file);
  61. } 
  62. int cgiMain() 
  63. { 
  64. char name[81],mail[81],homepage[81],suggest[1024];
  65.  
  66. CookieSet();//set cookies before we read them
  67. /* Send the content type, letting the browser know this is HTML */ 
  68. cgiHeaderContentType("text/html");
  69. /* Top of the page */ 
  70. fprintf(cgiOut, "<HTML><HEAD>\n");
  71. fprintf(cgiOut, "<TITLE>Firmware upgrade result</TITLE></HEAD>\n");
  72. fprintf(cgiOut, "<BODY><H1>Responses</H1>\n");
  73.  
  74. if ((cgiFormSubmitClicked("send") == cgiFormSuccess)) 
  75. { 
  76. File();
  77. } 
  78. fprintf(cgiOut, "</BODY></HTML>\n");
  79. return 0;
  80. }

Firmware upgrade的html UI原始碼如下

  1. <head> 
  2. <body> 
  3. <title>Firmware upgrade</title> 
  4. <H1>QT2410 Firmware upgrade</H1> 
  5. <form method="post" enctype="multipart/form-data" action="http://192.168.15.1/cgi-bin/upgrade.cgi"> 
  6.  
  7. <p>Firmware Upload:<input type="file" name="file" value=""> (Select A Local File)<p> 
  8.  
  9. <input type="submit" name= "send" value="send"> <input type="reset" value="cancel"> 
  10.  
  11. </form> 
  12. </body> 
  13. </head>

Firmware upgrade執行畫面如下

標籤: embedded
評論: 11 | 引用: 0 | 閱讀: 15387
  • 1 
jserv [ 2008-09-06 12:35 網址 | 回覆 | 編輯 刪除 ]
opkg is well maintained than ipkg.
http://svn.openmoko.org/trunk/src/target/opkg/
Joey [ 2008-09-08 09:49 郵箱 | 回覆 | 編輯 刪除 ]
非常感謝您的建議,小弟會去試看看opkg
Charles [ 2009-05-21 18:29 | 回覆 | 編輯 刪除 ]
我嘗試編譯 lighttpd 為 statically linkd. 但不管如活測試編譯出來都是 dynamically linked.
不知要如何解決?

#file lighttpd
lighttpd: ELF 32-bit LSB executable, ARM, version 1, dynamically linked (uses shared libs), not stripped

configure :
./configure --host=arm-linux --target=arm-linux --prefix=/usr/local/lighttpd --with-cc=arm-linux-gcc --disable-ipv6 --without-bzip2 --enable-static CFLAGS="-static"

lighttp ver: 1.4.22

Thanks~
Joey [ 回復於2009-05-21 19:09 郵箱 | 編輯 刪除 ]
建議您用偷吃步的.
把link stage的最後一行拿出來
然後加入static option,應該就可以了
Charles [ 2009-05-25 14:31 | 回覆 | 編輯 刪除 ]
謝謝~~
^^ 我倒是沒想到這一步, 確實可行.

他本身的 Makefile 太複雜了, 我還沒找出真正原因是哪造成的.
丫刁 [ 2010-01-12 01:00 | 回覆 | 編輯 刪除 ]
請問您的firmware upgrade cgi這支C檔是放在lighttpd的哪個目錄下啊?lighttpd有form action和cgi的對應的一段code嗎?希望您能為我解答, 謝謝^^
Joey [ 回復於2010-02-04 19:04 郵箱 | 編輯 刪除 ]
put cgi in server's cgi-bin/ directory
all the fucntions mapping between cgi and lighttpd are automatically negotiated by STDIN and STDOUT
KK~~ [ 2011-07-04 20:33 | 回覆 | 編輯 刪除 ]
HI Joey
根據你的文章 想使用OPKG做管理
如果想UPDATE U-boot或LINUX部分
大概該怎麼寫CONFIG 現在目前是使用網路作讀寫
謝謝
另外想請問有無其他文章提到類似實作方法
Joey [ 回復於2011-08-02 21:46 郵箱 | 編輯 刪除 ]
OPKG seems no difference comparing with IPKG.
I have used OPKG in my latest project and it works fine. You just have to use OPKG-install script instead the one which is IPKG-install.
Kin [ 2011-09-06 20:15 | 回覆 | 編輯 刪除 ]
很棒的文,可以借分享嗎??
Joey [ 回復於2011-09-08 13:25 郵箱 | 編輯 刪除 ]
No problem.
Thanks for your comment.
  • 1 
發表評論
暱 稱: 密 碼:
網 址: E - mail:
驗證碼: 驗證碼圖片 選 項:
頭 像:
內 容: