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