這篇文章的主題”遠端韌體更新”,以後簡稱為”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的範例如下
- #! /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 portingipkg 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>
沒有留言:
張貼留言