在一般網路裝置上,最常看到的,就是提供網頁的介面讓使用者更新網路裝置的韌體,而這樣的功能在實作上需要準備
(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的範例如下

#! /bin/sh
 
export PATH=$PATH:/usr/local/eldk/usr/bin/
export CPPFLAGS="-I/usr/local/eldk/arm/usr/include" 
export LDFLAGS="-ldl" 
export CFLAGS="" 
export AR=arm-linux-ar 
export AS=arm-linux-as 
export LD=arm-linux-ld 
export RANLIB=arm-linux-ranlib 
export CC=arm-linux-gcc 
export NM=arm-linux-nm 
export ARCH=arm 
 
./configure --target=arm-linux \
--host=arm-linux \
--build=arm-linux-gnu \
--prefix=/usr/local/eldk/lighthttpd \
--exec-prefix=/usr/local/eldk/lighthttpd \
--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]

server.modules = ( 
# "mod_rewrite",
# "mod_redirect",
"mod_alias",
"mod_access",
# "mod_cml",
# "mod_trigger_b4_dl",
# "mod_auth",
# "mod_status",
# "mod_setenv",
"mod_fastcgi",
# "mod_proxy",
# "mod_simple_vhost",
# "mod_evhost",
# "mod_userdir",
"mod_cgi",
# "mod_compress",
# "mod_ssi",
# "mod_usertrack",
# "mod_expire",
# "mod_secdownload",
# "mod_rrdtool",
"mod_accesslog" )

[server.document-root]

server.document-root = "/www/"

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

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

[CGI module]

#### CGI module 
cgi.assign = ( ".pl" => "/usr/bin/perl",
".cgi" => "" ) 
 
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,並修改成如下範例

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

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

(3) html和cgi

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

#include <stdio.h> 
#include "cgic.h" 
#include <string.h> 
#include <stdlib.h> 
 
#define SERVER_NAME cgiServerName
 
void File() 
{ 
cgiFilePtr file;
char name[1024];
char contentType[1024];
char buffer[1024];
int size;
int got;
FILE *fp;
char temp[256],fwop[1024];
int realwrite;
int status;
 
if (cgiFormFileName("file", name, sizeof(name)) != cgiFormSuccess) { 
fprintf(cgiOut,"<p>No file was uploaded.<p>\n");
return;
} 
fprintf(cgiOut, "The filename submitted was: ");
cgiHtmlEscape(name);
sprintf(temp,"touch /tmp/%s",name);
system(temp);
fprintf(cgiOut, "<p>\n");
cgiFormFileSize("file", &size);
fprintf(cgiOut, "The file size was: %d bytes<p>\n", size);
cgiFormFileContentType("file", contentType, sizeof(contentType));
//fprintf(cgiOut, "The alleged content type of the file was: ");
//cgiHtmlEscape(contentType);
fprintf(cgiOut, "<p>\n");
if (cgiFormFileOpen("file", &file) != cgiFormSuccess) 
{ 
fprintf(cgiOut, "Could not open the file.<p>\n");
return;
} 
fprintf(cgiOut, "<pre>\n");
 
sprintf(temp,"/tmp/%s",name);
fp=fopen(temp,"w");
 
while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) ==cgiFormSuccess) 
{ 
 
fwrite(buffer,1,got,fp);
//cgiHtmlEscapeData(buffer, got);
//cgiHtmlEscapeData(buffer, got);
} 
fclose(fp);
fprintf(cgiOut, "</pre>\n");
sprintf(fwop,"/bin/ipkg -V 99 -f /etc/ipkg.conf install %s >/dev/null",temp);
system(fwop);
 
fprintf(cgiOut,"%s\n<p>",fwop);
fprintf(cgiOut,"<p>Firmware upgrade finished\n");
cgiFormFileClose(file);
} 
int cgiMain() 
{ 
char name[81],mail[81],homepage[81],suggest[1024];
 
CookieSet();//set cookies before we read them
/* Send the content type, letting the browser know this is HTML */ 
cgiHeaderContentType("text/html");
/* Top of the page */ 
fprintf(cgiOut, "<HTML><HEAD>\n");
fprintf(cgiOut, "<TITLE>Firmware upgrade result</TITLE></HEAD>\n");
fprintf(cgiOut, "<BODY><H1>Responses</H1>\n");
 
if ((cgiFormSubmitClicked("send") == cgiFormSuccess)) 
{ 
File();
} 
fprintf(cgiOut, "</BODY></HTML>\n");
return 0;
}

Firmware upgrade的html UI原始碼如下

<head> 
<body> 
<title>Firmware upgrade</title> 
<H1>QT2410 Firmware upgrade</H1> 
<form method="post" enctype="multipart/form-data" action="http://192.168.15.1/cgi-bin/upgrade.cgi"> 
 
<p>Firmware Upload:<input type="file" name="file" value=""> (Select A Local File)<p> 
 
<input type="submit" name= "send" value="send"> <input type="reset" value="cancel"> 
 
</form> 
</body> 
</head>

最後修改日期: 3 6 月, 2022

作者

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。