gawkinet: REMCONF

 
 3.3 REMCONF: Remote Configuration of Embedded Systems
 =====================================================
 
 Today, you often find powerful processors in embedded systems.
 Dedicated network routers and controllers for all kinds of machinery are
 examples of embedded systems.  Processors like the Intel 80x86 or the
 AMD Elan are able to run multitasking operating systems, such as XINU or
 GNU/Linux in embedded PCs.  These systems are small and usually do not
 have a keyboard or a display.  Therefore it is difficult to set up their
 configuration.  There are several widespread ways to set them up:
 
    * DIP switches
 
    * Read Only Memories such as EPROMs
 
    * Serial lines or some kind of keyboard
 
    * Network connections via 'telnet' or SNMP
 
    * HTTP connections with HTML GUIs
 
    In this node, we look at a solution that uses HTTP connections to
 control variables of an embedded system that are stored in a file.
 Since embedded systems have tight limits on resources like memory, it is
 difficult to employ advanced techniques such as SNMP and HTTP servers.
 'gawk' fits in quite nicely with its single executable which needs just
 a short script to start working.  The following program stores the
 variables in a file, and a concurrent process in the embedded system may
 read the file.  The program uses the site-independent part of the simple
 web server that we developed in SeeA Web Service with Interaction
 Interacting Service.  As mentioned there, all we have to do is to write
 two new procedures 'SetUpServer()' and 'HandleGET()':
 
      function SetUpServer() {
        TopHeader = "<HTML><title>Remote Configuration</title>"
        TopDoc = "<BODY>\
          <h2>Please choose one of the following actions:</h2>\
          <UL>\
            <LI><A HREF=" MyPrefix "/AboutServer>About this server</A></LI>\
            <LI><A HREF=" MyPrefix "/ReadConfig>Read Configuration</A></LI>\
            <LI><A HREF=" MyPrefix "/CheckConfig>Check Configuration</A></LI>\
            <LI><A HREF=" MyPrefix "/ChangeConfig>Change Configuration</A></LI>\
            <LI><A HREF=" MyPrefix "/SaveConfig>Save Configuration</A></LI>\
          </UL>"
        TopFooter  = "</BODY></HTML>"
        if (ConfigFile == "") ConfigFile = "config.asc"
      }
 
    The function 'SetUpServer()' initializes the top level HTML texts as
 usual.  It also initializes the name of the file that contains the
 configuration parameters and their values.  In case the user supplies a
 name from the command line, that name is used.  The file is expected to
 contain one parameter per line, with the name of the parameter in column
 one and the value in column two.
 
    The function 'HandleGET()' reflects the structure of the menu tree as
 usual.  The first menu choice tells the user what this is all about.
 The second choice reads the configuration file line by line and stores
 the parameters and their values.  Notice that the record separator for
 this file is '"\n"', in contrast to the record separator for HTTP. The
 third menu choice builds an HTML table to show the contents of the
 configuration file just read.  The fourth choice does the real work of
 changing parameters, and the last one just saves the configuration into
 a file:
 
      function HandleGET() {
        if(MENU[2] == "AboutServer") {
          Document  = "This is a GUI for remote configuration of an\
            embedded system. It is is implemented as one GAWK script."
        } else if (MENU[2] == "ReadConfig") {
          RS = "\n"
          while ((getline < ConfigFile) > 0)
             config[$1] = $2;
          close(ConfigFile)
          RS = "\r\n"
          Document = "Configuration has been read."
        } else if (MENU[2] == "CheckConfig") {
          Document = "<TABLE BORDER=1 CELLPADDING=5>"
          for (i in config)
            Document = Document "<TR><TD>" i "</TD>" \
              "<TD>" config[i] "</TD></TR>"
          Document = Document "</TABLE>"
        } else if (MENU[2] == "ChangeConfig") {
          if ("Param" in GETARG) {            # any parameter to set?
            if (GETARG["Param"] in config) {  # is  parameter valid?
              config[GETARG["Param"]] = GETARG["Value"]
              Document = (GETARG["Param"] " = " GETARG["Value"] ".")
            } else {
              Document = "Parameter <b>" GETARG["Param"] "</b> is invalid."
            }
          } else {
            Document = "<FORM method=GET><h4>Change one parameter</h4>\
              <TABLE BORDER CELLPADDING=5>\
              <TR><TD>Parameter</TD><TD>Value</TD></TR>\
              <TR><TD><input type=text name=Param value=\"\" size=20></TD>\
                  <TD><input type=text name=Value value=\"\" size=40></TD>\
              </TR></TABLE><input type=submit value=\"Set\"></FORM>"
          }
        } else if (MENU[2] == "SaveConfig") {
          for (i in config)
            printf("%s %s\n", i, config[i]) > ConfigFile
          close(ConfigFile)
          Document = "Configuration has been saved."
        }
      }
 
    We could also view the configuration file as a database.  From this
 point of view, the previous program acts like a primitive database
 server.  Real SQL database systems also make a service available by
 providing a TCP port that clients can connect to.  But the application
 level protocols they use are usually proprietary and also change from
 time to time.  This is also true for the protocol that MiniSQL uses.