// MAC address found in shipped EEPROM
#define MY_MAC "\x00\x06\x98\x01\x0E\xBA"
// Unique IP address of the Ethernut Board.
// All following defines ignored if DHCP is used.
#define MY_IPADDR "192.168.192.35"
// IP network mask of the Ethernut Board.
#define MY_IPMASK "255.255.255.0"
// Gateway IP address for the Ethernut Board.
#define MY_IPGATE "192.168.192.1"
/* Wether we should use DHCP. */
#define USE_DHCP
/* Wether we should run a discovery responder. */
#define USE_DISCOVERY
/* Wether to use PHAT file system. */
#define MY_FSDEV devUrom
#include <cfg/os.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <dev/nvmem.h> // NutNvMemSave()
#include <dev/board.h>
#include <dev/urom.h>
//#include <dev/nplmmc.h>
//#include <dev/sbimmc.h>
//#include <fs/phatfs.h>
#include <sys/version.h>
#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/heap.h>
#include <sys/confnet.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <pro/httpd.h>
#include <pro/dhcp.h>
//#include <pro/ssi.h>
//#include <pro/asp.h>
#include <pro/discover.h>
#ifdef NUTDEBUG
#include <sys/osdebug.h>
#include <net/netdebug.h>
#endif
#include "hardware.h"
// Gemeinsam genutzter Kopfteil
static void PutHeader(FILE *f, REQUEST *req, prog_char *title) {
static char *html_mt = "text/html";
static prog_char head[]=
"<HTML><HEAD>\n"
"<META http-equiv=content-type content=\"text/html; charset=utf-8\">\n"
"<LINK rel=stylesheet href=\"/screen.css\">\n"
"<LINK rel=\"shortcut icon\" href=\"/boot.ico\">\n"
"<TITLE>%P</TITLE></HEAD>\n"
"<BODY><H1>%P</H1>\n";
/* Send HTTP response. */
NutHttpSendHeaderTop(f, req, 200, "Ok");
NutHttpSendHeaderBot(f, html_mt, -1);
fprintf_P(f,head,title,title);
}
// Tabellenkopf ausgeben
static void PutTableHead(FILE *f, prog_char *tablehead) {
fputs_P(PSTR("<TABLE border cellpadding=4 cellspacing=0>\n<TR>"),f);
fputs_P(tablehead,f);
}
// Gemeinsam genutzter Fußteil
static void PutFoot(FILE *f, prog_char *endtext) {
fputs_P(endtext,f);
fputs_P(PSTR("</BODY></HTML>\n"),f);
fflush(f);
}
/*******************************************
* CGI-Prozeduren für Ethernut-Information *
*******************************************/
/* CGI Sample: Show list of threads.
* is called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/threads.cgi'.
*/
static int ShowThreads(FILE * stream, REQUEST * req) {
static prog_char thead[] =
"<TH>Handle<TH>Name<TH>Priorität<TH>Zustand"
"<TH>Ereignis-<BR>warteschlange<TH>Timer<TH>Stack-<BR>pointer"
"<TH>Freier<BR>Stack\n<TH>Status\n";
static prog_char tfmt[] =
"<TR><TD>%04X<TD>%s<TD>%u<TD>%P<TD>%04X<TD>%04X<TD>%04X<TD>%u<TD>%P\n";
static prog_char tstate0[]="TRM";
static prog_char tstate1[]="<FONT COLOR=#CC0000>RUN";
static prog_char tstate2[]="<FONT COLOR=#339966>RDY";
static prog_char tstate3[]="SLP";
static prog_char *thread_states[]={tstate0,tstate1,tstate2,tstate3};
NUTTHREADINFO *tdp = nutThreadList;
PutHeader(stream,req,PSTR("Thread-Liste"));
PutTableHead(stream,thead);
for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
fprintf_P(stream, tfmt,
(uptr_t)tdp, tdp->td_name, tdp->td_priority,
thread_states[tdp->td_state], (uptr_t)tdp->td_queue, (uptr_t)tdp->td_timer,
(uptr_t)tdp->td_sp, (uptr_t)tdp->td_sp - (uptr_t)tdp->td_memory,
*((u_long *) tdp->td_memory) != DEADBEEF ? PSTR("Stacküberlauf") : PSTR("OK"));
}
PutFoot(stream,PSTR("</TABLE>"));
return 0;
}
/* CGI Sample: Show list of timers.
* is called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/timers.cgi'.
*/
static int ShowTimers(FILE * stream, REQUEST * req) {
static prog_char thead[] =
"<TH>Handle<TH>Countdown<TH>Tick Reload"
"<TH>Rückruf-<BR>adresse<TH>Rückruf-<BR>Argument\n";
static prog_char tfmt[] =
"<TR><TD>%04X<TD>%lu<TD>%lu<TD>%04X<TD>%04X\n";
NUTTIMERINFO *tnp;
u_long ticks_left;
PutHeader(stream,req,PSTR("Timer-Liste"));
PutTableHead(stream,thead);
tnp = nutTimerList;
ticks_left = 0;
while (tnp) {
ticks_left += tnp->tn_ticks_left;
fprintf_P(stream, tfmt,
(uptr_t)tnp,ticks_left,tnp->tn_ticks,
(uptr_t)tnp->tn_callback,(uptr_t)tnp->tn_arg);
tnp = tnp->tn_next;
}
PutFoot(stream,PSTR("</TABLE>"));
return 0;
}
/* CGI Sample: Show list of sockets.
* is called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/sockets.cgi'.
*/
static int ShowSockets(FILE * stream, REQUEST * req) {
static prog_char thead[]=
"<TH>Handle<TH>Typ<TH>Lokal<TH>Remote<TH>Status\n";
static prog_char tfmt[]=
"<TR><TD>%04X<TD>TCP<TD>%s:%u<TD>%s:%u<TD>%P\n";
prog_char *st_P;
extern TCPSOCKET *tcpSocketList;
TCPSOCKET *ts;
PutHeader(stream,req,PSTR("Socket-Liste"));
PutTableHead(stream,thead);
for (ts = tcpSocketList; ts; ts = ts->so_next) {
switch (ts->so_state) {
case TCPS_LISTEN: st_P = PSTR("LISTEN"); break;
case TCPS_SYN_SENT: st_P = PSTR("SYNSENT"); break;
case TCPS_SYN_RECEIVED: st_P = PSTR("SYNRCVD"); break;
case TCPS_ESTABLISHED: st_P = PSTR("<FONT COLOR=#CC0000>ESTABL"); break;
case TCPS_FIN_WAIT_1: st_P = PSTR("FINWAIT1"); break;
case TCPS_FIN_WAIT_2: st_P = PSTR("FINWAIT2"); break;
case TCPS_CLOSE_WAIT: st_P = PSTR("CLOSEWAIT"); break;
case TCPS_CLOSING: st_P = PSTR("CLOSING"); break;
case TCPS_LAST_ACK: st_P = PSTR("LASTACK"); break;
case TCPS_TIME_WAIT: st_P = PSTR("TIMEWAIT"); break;
case TCPS_CLOSED: st_P = PSTR("CLOSED"); break;
default: st_P = PSTR("UNKNOWN"); break;
}
//Fixed a bug reported by Zhao Weigang.
fprintf_P(stream, tfmt,
(uptr_t)ts, inet_ntoa(ts->so_local_addr), ntohs(ts->so_local_port),
inet_ntoa(ts->so_remote_addr), ntohs(ts->so_remote_port), st_P);
}
PutFoot(stream,PSTR("</TABLE>"));
return 0;
}
/*! \fn Service(void *arg)
* \brief HTTP service thread.
*
* The endless loop in this thread waits for a client connect,
* processes the HTTP request and disconnects. Nut/Net doesn't
* support a server backlog. If one client has established a
* connection, further connect attempts will be rejected.
* Typically browsers open more than one connection in order
* to load images concurrently. So we run this routine by
* several threads.
*
*/
THREAD(Service, arg)
{
TCPSOCKET *sock;
FILE *stream;
u_char id = (u_char) ((uptr_t) arg);
/*
* Now loop endless for connections.
*/
for (;;) {
/*
* Create a socket.
*/
if ((sock = NutTcpCreateSocket()) == 0) {
printf_P(PSTR("[%u] Creating socket failed\n"), id);
NutSleep(5000);
continue;
}
/*
* Listen on port 80. This call will block until we get a connection
* from a client.
*/
NutTcpAccept(sock, 80);
printf_P(PSTR("[%u] Connected, %u bytes free\n"), id, NutHeapAvailable());
/*
* Wait until at least 8 kByte of free RAM is available. This will
* keep the client connected in low memory situations.
*/
while (NutHeapAvailable() < 8192) {
printf_P(PSTR("[%u] Low mem\n"), id);
NutSleep(1000);
}
/*
* Associate a stream with the socket so we can use standard I/O calls.
*/
if ((stream = _fdopen((int) ((uptr_t) sock), "r+b")) == 0) {
printf_P(PSTR("[%u] Creating stream device failed\n"), id);
} else {
/*
* This API call saves us a lot of work. It will parse the
* client's HTTP request, send any requested file from the
* registered file system or handle CGI requests by calling
* our registered CGI routine.
*/
NutHttpProcessRequest(stream);
/*
* Destroy the virtual stream device.
*/
fclose(stream);
}
/*
* Close our socket.
*/
NutTcpCloseSocket(sock);
printf_P(PSTR("[%u] Disconnected\n"), id);
}
}
/***********************
* CGI-Hilfsfunktionen *
***********************/
enum {false, true};
// Schreibt nur die veränderten Bytes (Geschwindigkeit!)
// Liefert die Anzahl der geänderten Bytes (Änderungskontrolle!)
static size_t EeWriteChanged(unsigned EeAdr, u_char*MemAdr, size_t Len) {
u_char b;
size_t ret=0;
for (;Len; EeAdr++,MemAdr++,Len--) {
NutNvMemLoad(EeAdr,&b,1);
if (b!=*MemAdr) {
NutNvMemSave(EeAdr,MemAdr,1);
ret++;
}
}
return ret;
}
// Schreibt eine (einzeilige) Nachricht in (nur Mozilla: abgerundeter) Box
static void PutMessage(FILE*f,const prog_char*format,...) {
va_list va; // Variable Argumentliste
fputs_P(PSTR("<P class=notice>"),f);
va_start(va,format);
vfprintf_P(f,format,va);
va_end(va);
fputs_P(PSTR("</P>\n"),f);
}
// Wie strlcpy, aber bei "ganzen" UTF-8-Zeichen kürzen
static void strlcpy_utf8(char *d, const char*s, size_t bufsize) {
u_char c; // Zu betrachtendes Führungsbyte
u_char l; // Anzahl Bytes eines UTF-8-kodierten Zeichens
if (!bufsize) return;
c = *s++; // Zeichen oder Führungsbyte laden
for (;;) {
if (!c) break;
l = 1;
if (c >= 0xF0) l++;
if (c >= 0xE0) l++;
if (c >= 0xC0) l++;
if (l >= bufsize) break; // Der "="-Fall bewahrt Platz für '\0'
bufsize -= l; // verbleibender Platz
do{
*d++ = c; // Ganzes Multibyte-Zeichen kopieren
c = *s++;
}while (--l);
}
*d = 0;
}
/*********************************
* CGI-Prozedur zur Boot-Auswahl *
*********************************/
#define NUMPORTS 16 // Dieses Gerät hat 16 Ports (Buchsen)
// (Eine ähnliche Software könnte mal massiv-viele Geräte steuern)
#define MAXGROUPS 16 // hier: maximal ist jede Buchse eine Gruppe
#define STRMAXLEN 15 // Maximale Länge für alle Strings
// Boot-Gruppen-Eigenschaften (so auch im EEPROM, max. 16 Gruppen)
typedef char string_t[STRMAXLEN+1];
typedef union{
struct{
string_t ports; // Ports, bspw. "1-10" oder "7,9"
string_t groupname; // Gruppen-Name, bspw. "Cave"
string_t bootname[4]; // Betriebssystem-Namen, bspw. "XP SP2", "Suse 7.8"...
};
string_t names[6]; // alle Namen sind 16 Zeichen lang, das ist alles!
}bootgroup_t;
#define BOOTGROUP_EE_OFFSET 0x90
// Prüft die übergebene Liste auf Korrektheit, liefert Maske <>0 wenn OK
static unsigned GroupCheck(const char*ports) {
unsigned ret=0;
char c=*ports;
while (c) {
u_char e,a=strtoul(ports,(char**)&ports,10); // Anfang lesen
if (a==0 || a>NUMPORTS) return 0; // Nicht mehr als Anzahl Ports
e=a;
c=*ports++;
switch (c) {
case '-': {
e=strtoul(ports,(char**)&ports,10); // Ende lesen
if (e<=a || e>NUMPORTS) return 0; // Ende muss größer als Anfang sein
c=*ports++;
}break;
case ',': // Komma als Trenner erlaubt
case ' ': // Leerzeichen ebenfalls erlaubt
case 0: break;
default: return 0; // jedes andere Zeichen = Fehler
}
a--;
do ret|=1U<<a; while (++a<e);
}
return ret;
}
// Setzt die Ports einer Gruppe; die Port-Liste wird als korrekt angenommen
static void GroupSetBootsel(const char *ports, u_char sel) {
unsigned m, mask;
u_char i;
if (sel>4) return;
mask = GroupCheck(ports);
for (i=1,m=1; i<=16; i++,m<<=1) if (mask&m) OutSetBootsel(i,sel);
}
// Liefert den Schaltzustand einer Gruppe,
// liefert 0xFF wenn <ports> nicht in Ordnung
// liefert 0xFE wenn verschiedene Zustände
static u_char GroupGetBootsel(const char *ports) {
unsigned m, mask;
u_char i, ret = 0xFF;
mask = GroupCheck(ports);
for (i=1,m=1; i<=16; i++,m<<=1) if (mask&m) {
if (ret==0xFF) ret=OutGetBootsel(i); // beim ersten Auffinden laden
else if (ret!=OutGetBootsel(i)) { // beim weiteren Auffinden vergleichen
ret=0xFE;
break;
}
}
return ret;
}
// Liefert " checked" wenn column==state, sonst ""
static const prog_char* GetCheck(u_char column, u_char state) {
static prog_char checked[] = " checked";
static prog_char unchecked[] = "";
return column==state ? checked : unchecked;
}
// CGI-Handler
static int EditBootsel(FILE*f, REQUEST*req) {
static prog_char thead[] =
"<TH> <TH>Buchse<small><br>(Buchsengruppe)"
"<TH>Rechnername<small><br>(Gruppenname)"
"<TH colspan=5>Boot-Vorauswahl\n";
static prog_char same[] = ": <INPUT type=radio name=";
static prog_char tfmt[] =
" <TR><TH>%u:<TH>%s\n"
" <TH>%s\n"
" <TD>0%P%u%P value=0>keine</INPUT>\n"
" <TD bgcolor=#FFC0C0>1%P%u%P value=1>%s</INPUT>\n"
" <TD bgcolor=#FFFFC0>2%P%u%P value=2>%s</INPUT>\n"
" <TD bgcolor=#C0FFC0>3%P%u%P value=3>%s</INPUT>\n"
" <TD bgcolor=#FFE0C0>4%P%u%P value=4>%s</INPUT>\n";
bootgroup_t *Groups;
u_char i, values[MAXGROUPS];
PutHeader(f,req,PSTR("Boot-Auswahl"));
Groups = NutHeapAlloc(sizeof(bootgroup_t)*MAXGROUPS);
if (Groups) {
NutNvMemLoad(BOOTGROUP_EE_OFFSET,Groups,sizeof(bootgroup_t)*MAXGROUPS);
// Schaltzustände lesen
for (i=0; i<MAXGROUPS; i++) {
if ((u_char)Groups[i].ports[0]==0xFF) break;
values[i]=GroupGetBootsel(Groups[i].ports);
}
// CGI-Daten auswerten und ggf. Message einfügen
if (req->req_query) {
u_char i;
u_char key, val;
char *pk, *pv, **args = req->req_qptrs;
#if 0
PutMessage(f,PSTR("CGI-Daten sind da"));
fprintf_P(f,PSTR("<BR>num=%u<P>\n"),req->req_numqptrs);
{int i;
char **args = req->req_qptrs;
for (i=0; i<req->req_numqptrs*2; i++) {
fprintf_P(f,PSTR("%s<br>\n"),*args++);
}
}
#endif
for (i=0; i<req->req_numqptrs; i++) {
key = strtoul(*args++,&pk,10)-1; // Gruppen-Zeilennummer (war 1-basiert)
val = strtoul(*args++,&pv,10); // Radiobuttons 0..4
if (*pk || *pv || key>=MAXGROUPS
|| (u_char)Groups[key].ports[0]==0xFF || val>4) {
PutMessage(f,PSTR("Ungültiger Formularaufruf!"));
goto raus;
}
values[key]=val;
}
for (i=0; i<MAXGROUPS; i++) {
if ((u_char)Groups[i].ports[0]==0xFF) break;
GroupSetBootsel(Groups[i].ports,values[i]);
}
PutMessage(f,PSTR("Boot-Auswahl gesetzt"));
}
raus:
// Webseite mit Istwerten (ggf. indifferenten Werten) aufbauen
if ((u_char)Groups[0].ports[0]==0xFF) {
PutFoot(f,PSTR("Keine Gruppen definiert!\n"
"Gehen Sie zur <A href=\"bootsgl.cgi\">Boot-Auswahl (Einzelport)</A>\n"
"oder zur <A href=\"groups.cgi\">Gruppen-Festlegung</A>!\n"));
}else{
fputs_P(PSTR("<FORM action=\"bootsel.cgi\">\n"),f);
PutTableHead(f,thead);
for (i=0; i<MAXGROUPS; i++) {
if ((u_char)Groups[i].ports[0] == 0xFF) break;
fprintf_P(f,tfmt, // Tabellenzeile ausgeben
i+1,
Groups[i].ports,
Groups[i].groupname,
same,i+1,GetCheck(0,values[i]),
same,i+1,GetCheck(1,values[i]),Groups[i].bootname[0],
same,i+1,GetCheck(2,values[i]),Groups[i].bootname[1],
same,i+1,GetCheck(3,values[i]),Groups[i].bootname[2],
same,i+1,GetCheck(4,values[i]),Groups[i].bootname[3]);
}
PutFoot(f,PSTR(
"</TABLE>\n"
"<P><INPUT type=submit value=\"Setzen\"> \n"
"<INPUT type=button value=\"Aktualisieren\" onclick=\"window.location.href='bootsel.cgi'\"></P>\n"
"</FORM>\n"));
}
NutHeapFree(Groups);
}else PutFoot(f,PSTR("Zu wenig RAM für Verarbeitung!"));
return 0;
}
// CGI-Handler für einzelportweise Ausgabe
static int EditBootsgl(FILE*f, REQUEST*req) {
static prog_char thead[] =
"<TH>Buchse"
"<TH>Rechnername<small><br>(Gruppenname)"
"<TH colspan=5>Boot-Vorauswahl\n";
static prog_char same[] = ": <INPUT type=radio name=";
static prog_char tfmt[] =
" <TR><TH>%u:\n"
" <TH>%s\n"
" <TD>0%P%u%P value=0>keine</INPUT>\n"
" <TD bgcolor=#FFC0C0>1%P%u%P value=1>%s</INPUT>\n"
" <TD bgcolor=#FFFFC0>2%P%u%P value=2>%s</INPUT>\n"
" <TD bgcolor=#C0FFC0>3%P%u%P value=3>%s</INPUT>\n"
" <TD bgcolor=#FFE0C0>4%P%u%P value=4>%s</INPUT>\n";
bootgroup_t *Groups;
u_char i, j, members[NUMPORTS], values[NUMPORTS];
PutHeader(f,req,PSTR("Boot-Auswahl"));
Groups = NutHeapAlloc(sizeof(bootgroup_t)*MAXGROUPS);
if (Groups) {
NutNvMemLoad(BOOTGROUP_EE_OFFSET,Groups,sizeof(bootgroup_t)*MAXGROUPS);
// Zugehörigkeit Port->Gruppe finden
memset(members,0xFF,sizeof(members));
memset(values,0xFF,sizeof(values));
for (i=0; i<MAXGROUPS; i++) {
unsigned m, mask;
if ((u_char)Groups[i].ports[0] == 0xFF) break;
mask = GroupCheck(Groups[i].ports);
for (j=0,m=1; j<NUMPORTS; j++,m<<=1) if (mask&m) members[j]=i;
}
// CGI-Daten auswerten und ggf. Message einfügen
if (req->req_query) {
u_char i;
u_char key, val;
char *pk, *pv, **args = req->req_qptrs;
for (i=0; i<req->req_numqptrs; i++) {
key = strtoul(*args++,&pk,10)-1; // Portnummer (war 1-basiert)
val = strtoul(*args++,&pv,10); // Radiobuttons 0..4
if (*pk || *pv || key>=NUMPORTS || val>4) {
PutMessage(f,PSTR("Ungültiger Formularaufruf!"));
goto raus;
}
values[key]=val;
}
for (i=0; i<NUMPORTS; i++) {
if (values[i]<=4) OutSetBootsel(i+1,values[i]);
}
PutMessage(f,PSTR("Boot-Auswahl gesetzt"));
}
raus:
// Webseite mit Istwerten aufbauen
fputs_P(PSTR("<FORM action=\"bootsgl.cgi\">\n"),f);
PutTableHead(f,thead);
for (i=0; i<NUMPORTS; i++) {
bootgroup_t *member = NULL;
if (members[i]<MAXGROUPS) member = Groups+members[i];
j = OutGetBootsel(i+1);
fprintf_P(f,tfmt, // Tabellenzeile ausgeben
i+1,
member?member->groupname:"–",
same,i+1,GetCheck(0,j),
same,i+1,GetCheck(1,j),member?member->bootname[0]:"??",
same,i+1,GetCheck(2,j),member?member->bootname[1]:"??",
same,i+1,GetCheck(3,j),member?member->bootname[2]:"??",
same,i+1,GetCheck(4,j),member?member->bootname[3]:"??");
}
PutFoot(f,PSTR(
"</TABLE>\n"
"<P><INPUT type=submit value=\"Setzen\"> \n"
"<INPUT type=button value=\"Aktualisieren\" onclick=\"window.location.href='bootsgl.cgi'\"></P>\n"
"</FORM>\n"));
NutHeapFree(Groups);
}else PutFoot(f,PSTR("Zu wenig RAM für Verarbeitung!"));
return 0;
}
/********************************************
* CGI-Prozedur zur Boot-Gruppen-Verwaltung *
********************************************/
// Auswertung der CGI-Daten, füllt <Groups> und schreibt EEPROM wenn OK
// Rückkehr-Kodes: 0 = keine Daten verändert
// positiv = Anzahl Bytes im EEPROM verändert
// -1 = Syntaxfehler (*el = Zeilennummer)
// -2 = überlappende Ports (*el = Zeilennummer)
// -3 = zu viele Gruppen
static int ProcessGroupsCgi(REQUEST *req, u_char *el, bootgroup_t *Groups) {
char *key, *val, **args = req->req_qptrs;
// bootgroup_t *CurGroup=NutHeapAlloc(sizeof(bootgroup_t));
unsigned Ports, PortsUsed = 0;
u_char i,j;
u_char NumGroups = 0, LineFilled = false;
int ret=0;
memset(Groups,0xFF,sizeof(bootgroup_t)*MAXGROUPS);
for (j = 0; j < req->req_numqptrs; j++) {
key = *args++;
val = *args++;
switch (i = *key++) {
case 'P':
case 'p': { // Port-Liste: neue Tabellenzeile beginnt
if (NumGroups == MAXGROUPS) {
ret = -3; // Fehler-Kode: Zu viele Gruppen
goto exi;
}
if (*val) { // gefüllte Zeile
Ports=GroupCheck(val);
if (!Ports && !ret) {
ret = -1; // Fehler-Kode: Syntaxfehler
*el = NumGroups+1;
}
if (PortsUsed&Ports && !ret) {
ret = -2; // Fehler-Kode: Überlappende Gruppe
*el = NumGroups+1;
}
PortsUsed|=Ports; // Verwendete Ports vermerken
LineFilled++;
i=0; goto copy;
}
}break;
case 'N':
case 'n': { // Gruppen-Name
i=1; goto copy;
}break;
case '1': // Vier Boot-Namen
case '2':
case '3':
case '4': {
i += -'1' + 2;
copy:
if (LineFilled) {
strlcpy_utf8(Groups[NumGroups].names[i],val,16);
if (i == 5) { // Letzter Boot-Name?
NumGroups++;
LineFilled = false;
}
}
}
}
}
if (NumGroups<MAXGROUPS) NumGroups++;
if (!ret) ret=EeWriteChanged(BOOTGROUP_EE_OFFSET,
(void*)Groups,sizeof(bootgroup_t)*NumGroups);
exi:
return ret;
}
// CGI-Handler
static int EditGroups(FILE*f, REQUEST*req) {
static prog_char thead[] =
"<TH> <TH>Buchse<small><br>(Buchsengruppe)"
"<TH>Rechnername<small><br>(Gruppenname)"
"<TH colspan=4>Betriebssystem-Namen\n";
static prog_char editsize[] = " size=16 maxlen=15 value=\"";
static prog_char tfmt[] =
" <TR><TH>%u:<TD><INPUT name=p%P%s\">\n"
" <TD><INPUT name=n%P%s\">\n"
" <TD bgcolor=#FFC0C0>1: <INPUT name=1%P%s\">\n"
" <TD bgcolor=#FFFFC0>2: <INPUT name=2%P%s\">\n"
" <TD bgcolor=#C0FFC0>3: <INPUT name=3%P%s\">\n"
" <TD bgcolor=#FFE0C0>4: <INPUT name=4%P%s\">\n";
bootgroup_t *Groups;
u_char i;
PutHeader(f,req,PSTR("Gruppen-Festlegung"));
Groups = NutHeapAlloc(sizeof(bootgroup_t)*MAXGROUPS);
if (Groups) {
// CGI-Daten auswerten und ggf. Message einfügen
if (req->req_method == METHOD_POST) NutHttpProcessPostQuery(f,req);
if (req->req_query) {
u_char el=0; // el = Error-Location
int ec=ProcessGroupsCgi(req,&el,Groups); // ec = Error-Code
switch (ec) {
case -1: PutMessage(f,PSTR("Syntaxfehler bei Buchsenbezeichnung in Zeile %u!"),el); break;
case -2: PutMessage(f,PSTR("Überlappende Buchsenbezeichnungen in Zeile %u!"),el); break;
case -3: PutMessage(f,PSTR("Zu viele Gruppen!")); break;
case 0: break; // keine Message
default: PutMessage(f,PSTR("Geänderte Daten permanent gepeichert, %u Bytes"),ec);
}
}else{ // Daten aus EEPROM lesen
NutNvMemLoad(BOOTGROUP_EE_OFFSET,Groups,sizeof(bootgroup_t)*MAXGROUPS);
}
// Webseite mit Istwerten oder eingegebenen (falschen) Werten aufbauen
fputs_P(PSTR("<FORM action=\"groups.cgi\" method=post>\n"),f);
PutTableHead(f,thead);
for (i=0; i<MAXGROUPS; i++) {
if ((u_char)Groups[i].ports[0] == 0xFF) break;
fprintf_P(f,tfmt, // Tabellenzeile ausgeben
i+1,
editsize,Groups[i].ports,
editsize,Groups[i].groupname,
editsize,Groups[i].bootname[0],
editsize,Groups[i].bootname[1],
editsize,Groups[i].bootname[2],
editsize,Groups[i].bootname[3]);
}
if (i<MAXGROUPS) {
fprintf_P(f,tfmt, // leere Tabellenzeile am Ende ausgeben
i+1,
editsize,"",
editsize,"",
editsize,"",
editsize,"",
editsize,"",
editsize,"");
}
PutFoot(f,PSTR(
"</TABLE>\n"
"<P><INPUT type=submit value=\"Setzen\"></P>\n"
"</FORM>\n"
"Buchsen-Nummern: 1..16<BR>\n"
"Buchsengruppen wie bei <code>psselect</code>.<BR>\n"
"Leere Feld bei „Buchse“ = Zeile löschen<P>\n"
"Bei „Rechnername“ sind beliebige Namen einzutragen.\n"
"Diese sieht der Benutzer bei „Boot-Auswahl“\n"));
NutHeapFree(Groups);
}else PutFoot(f,PSTR("Zu wenig RAM für Verarbeitung!"));
return 0;
}
/**************************************
* CGI-Prozedur zur Zugriffskontrolle *
**************************************/
#define PASSWORD_EE_OFFSET BOOTGROUP_EE_OFFSET + MAXGROUPS*sizeof(bootgroup_t)
typedef struct {
string_t password;
// später noch: IP-Adressbereich
}accesscontrol_t;
static void RegisterAuth(void) {
char catbuf[5+STRMAXLEN] = "root:ethernut";
u_char b;
NutNvMemLoad(PASSWORD_EE_OFFSET,&b,1);
if (b != 0xFF) {
NutNvMemLoad(PASSWORD_EE_OFFSET,catbuf+5,STRMAXLEN+1);
}
if (b) NutRegisterAuth("cgi-bin", catbuf);
}
// CGI: Passwort-Eingabe
static int EditPassword(FILE *f, REQUEST *req) {
static prog_char editsize[] = " type=password size=16 maxlen=15 value=\"";
accesscontrol_t *AC;
char *pass1 = NULL, *pass2 = NULL;
PutHeader(f,req,PSTR("Zugriffskontrolle"));
// CGI-Daten auswerten und ggf. Message einfügen
AC = NutHeapAlloc(sizeof(accesscontrol_t));
if (AC) {
if (req->req_query) {
int i;
char *key, *val, **args = req->req_qptrs;
for (i=0; i<req->req_numqptrs; i++) {
key = *args++;
val = *args++;
if (!strcasecmp_P(key,PSTR("pass1"))) pass1 = val;
else if (!strcasecmp_P(key,PSTR("pass2"))) pass2 = val;
}
if (!pass1 || !pass2) PutMessage(f,PSTR("Ungültiger Formularaufruf!"));
else if (strcmp(pass1,pass2)) PutMessage(f,PSTR("Passwörter unterschiedlich!"));
else{
u_char len = strlen(pass1);
if (len>STRMAXLEN) PutMessage(f,PSTR("Passwort zu lang (%u Bytes)!"),len);
else{
EeWriteChanged(PASSWORD_EE_OFFSET,(u_char*)pass1,len+1);
NutClearAuth();
RegisterAuth();
PutMessage(f,PSTR("Passwort gesetzt"));
}
}
}else{
NutNvMemLoad(PASSWORD_EE_OFFSET,AC,sizeof(accesscontrol_t));
if ((u_char)AC->password[0] == 0xFF) strcpy_P(AC->password,PSTR("ethernut"));
pass1 = pass2 = AC->password;
}
// Webseite mit Istwerten oder eingegebenen (falschen) Werten aufbauen
fputs_P(PSTR("<FORM action=\"password.cgi\">\n"),f);
PutTableHead(f,PSTR("<TD>login:<TD><INPUT value=root size=16 disabled>\n"));
fprintf_P(f,PSTR(
"<TR><TD rowspan=2>Password<TD><INPUT name=pass1%P%s\">\n"
"<TR><TD><INPUT name=pass2%P%s\">\n"),
editsize,pass1,
editsize,pass2);
PutFoot(f,PSTR(
"</TABLE>\n"
"<P><INPUT type=submit value=\"Setzen\"></P>\n"
"</FORM>\n"));
NutHeapFree(AC);
}else PutFoot(f,PSTR("Zu wenig RAM für Verarbeitung!"));
return 0;
}
/********************
* Telnet-Interface *
********************/
// Liefert Eingabe-Abbruch-Zeichen
static char EnterLine(FILE *f, const prog_char *prompt, char *buf, size_t bufsize) {
int c;
char *p = buf, *e = buf+bufsize-1;
if (prompt) {
fputs_P(prompt,f);
fputc(' ',f);
fflush(f);
}
for (;;) {
c = fgetc(f);
if (c == EOF) break;
switch ((char)c) {
case '\r': continue;
case '\n':
case 3: goto exi;
default: {
fflush(f);
if (p!=e) *p++=c;
}
}
}
exi:
fputs_P(PSTR("\r"),f);
*p = 0;
return (char)c;
}
// Telnet-Behandlung (mit Login und Passwort?)
static void ProcessTelnet(FILE *f) {
struct _{
string_t login;
string_t passwd;
char s[128];
}*p = NutHeapAlloc(sizeof(struct _)); // kein Platz auf Stack!!
if (!p) return;
NutNvMemLoad(PASSWORD_EE_OFFSET,p->s,16);
do{
fputs_P(PSTR("BootSelektor-RJ45 Telnet-Interface\r\n"),f);
if (EnterLine(f,PSTR("login:"),p->login,sizeof(p->login))==3) goto exi;
if (*p->s) {
if (EnterLine(f,PSTR("Password:"),p->passwd,sizeof(p->passwd))==3) goto exi;
}else p->passwd[0] = 0;
}while (strcmp_P(p->login,PSTR("root")) || strcmp(p->passwd,p->s));
fputs_P(PSTR("\np <Nummer> <Auswahl> - Setze Einzelport auf Bootauswahl\r\n"),f);
fputs_P(PSTR( "p <Name> <Auswahl> - Setze Gruppe auf Bootauswahl\r\n"),f);
fputs_P(PSTR( "r <Nummer/Name> - Abfrage der Bootauswahl\r\n"),f);
fputs_P(PSTR( "a - Advise, u - Unadvise\r\n"),f);
fflush(f);
while (EnterLine(f,NULL,p->s,sizeof(p->s))!=3) {
switch (p->s[0]) {
case 'p': {
char *key, *val, *q;
u_char nkey, nval;
key = p->s+2;
val = strchr(key,p->s[1]);
if (!val) break;
*val++ = 0;
nkey = strtoul(key,&q,10);
// if (*q) nkey = 0; // keine numerische Portnummer
if (nkey > NUMPORTS) nkey = 0;
nval = val[0]-'0';
if (nval>4 || val[1]) nval=0xFF; // keine numerische Auswahl
// Hier nur numerisches Poke:
if (nkey && nval<=4) {
OutSetBootsel(nkey,nval);
fprintf_P(f,PSTR("%s OK"),p->s);
}else{
fputs_P(PSTR("Error"),f);
}
}break;
case 'r': {
u_char nkey, nval;
nkey = strtoul(p->s+2,NULL,10);
if (nkey > NUMPORTS) nkey = 0;
if (nkey) {
nval = OutGetBootsel(nkey);
fprintf_P(f,PSTR("%s %u"),p->s, nval);
}else{
fputs_P(PSTR("Error"),f);
}
}break;
}
fputs_P(PSTR("\r\n\n"),f);
fflush(f);
}
exi:
NutHeapFree(p);
}
/*****************
* Hauptprogramm *
*****************/
void __attribute__((noreturn)) main(void) {
u_long baud = 115200;
u_char i;
OutInit();
OutKnightrider();
OutRestore();
// Initialize the uart device.
NutRegisterDevice(&DEV_DEBUG, 0, 0);
freopen(DEV_DEBUG_NAME, "w", stdout);
_ioctl(_fileno(stdout), UART_SETSPEED, &baud);
NutSleep(200);
printf_P(PSTR("\n\nNut/OS %s HTTP Daemon..."), NutVersionString());
#ifdef NUTDEBUG
NutTraceTcp(stdout, 0);
NutTraceOs(stdout, 0);
NutTraceHeap(stdout, 0);
NutTracePPP(stdout, 0);
#endif
// Register Ethernet controller.
if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
puts_P(PSTR("Registering device failed"));
}
printf_P(PSTR("Configure %s..."), DEV_ETHER_NAME);
if (NutNetLoadConfig(DEV_ETHER_NAME)) {
u_char mac[] = MY_MAC;
printf_P(PSTR("initial boot..."));
if (NutDhcpIfConfig(DEV_ETHER_NAME, mac, 60000)) {
u_long ip_addr = inet_addr(MY_IPADDR);
u_long ip_mask = inet_addr(MY_IPMASK);
u_long ip_gate = inet_addr(MY_IPGATE);
printf_P(PSTR("No DHCP..."));
if (NutNetIfConfig(DEV_ETHER_NAME, mac, ip_addr, ip_mask) == 0) {
/* Without DHCP we had to set the default gateway manually.*/
if (ip_gate) {
printf_P(PSTR("hard coded gate..."));
NutIpRouteAdd(0, 0, ip_gate, &DEV_ETHER);
}
puts_P(PSTR("OK"));
}else{
puts_P(PSTR("failed"));
}
}
}else{
if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000)) puts_P(PSTR("failed"));
else puts_P(PSTR("OK"));
}
printf_P(PSTR("%s ready\n"), inet_ntoa(confnet.cdn_ip_addr));
NutRegisterDiscovery((u_long)-1, 0, DISF_INITAL_ANN);
// Register our device for the file system.
NutRegisterDevice(&MY_FSDEV, 0, 0);
// CGI-Seiten registrieren
NutRegisterCgi("bootsel.cgi", EditBootsel);
NutRegisterCgi("bootsgl.cgi", EditBootsgl);
NutRegisterCgi("groups.cgi", EditGroups);
NutRegisterCgi("password.cgi", EditPassword);
NutRegisterCgi("threads.cgi", ShowThreads);
NutRegisterCgi("timers.cgi", ShowTimers);
NutRegisterCgi("sockets.cgi", ShowSockets);
RegisterAuth(); // Verzeichnis "cgi-bin" schützen
// Start server threads.
for (i = 1; i <= 6; i++) {
static char thname[] = "httpd-";
thname[5] = '0' + i;
NutThreadCreate(thname, Service, (void *) (uptr_t) i, NUT_THREAD_MAINSTACK);
}
for (;;) {
TCPSOCKET *sock;
FILE *f;
sock = NutTcpCreateSocket();
if (sock) {
if (!NutTcpAccept(sock,23)) {
printf_P(PSTR("Telnet client %s connected\n"),inet_ntoa(sock->so_remote_addr));
f = _fdopen((int)sock,"r+b");
if (f) {
ProcessTelnet(f);
puts_P(PSTR("Telnet client disconnected\n"));
fclose(f);
}else puts_P(PSTR("Error: no file\n"));
}else puts_P(PSTR("Error: no accept\n"));
NutTcpCloseSocket(sock);
}else puts_P(PSTR("Error: no sock\n"));
}
}
Vorgefundene Kodierung: UTF-8 | 0
|