// Copyright (C)2013-2018, Philip Munts, President, Munts AM Corp. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mongoose.h" #define RESPONSEDELAY 0 #define PROGRAMNAME "spi_agent_http_server" #define SERVICENAME "spiagent-http" #define REPLYSTRING "HTTP/1.1 200 OK\r\n" \ "Content-Type: text/plain\r\n\r\n" \ "%d,%u,%u,%u,%u;\r\n" #define REGEX_QUERY_TEMPLATE "^(cmd=)?[0-9]+,[0-9]+,[0-9]+$" static regex_t query_template; // Signal handler for SIGTERM void SIGTERMhandler(int sig) { signal(sig, SIGTERMhandler); } // ioctl wrapper functions from libspiagent.so extern void spiagent_open_ioctl(char *servername, int32_t *error); extern void spiagent_command_ioctl(SPIAGENT_COMMAND_MSG_t *cmd, SPIAGENT_RESPONSE_MSG_t *resp, int32_t *error); extern void spiagent_close_ioctl(int32_t *error); // HTML macros #define HTML_PROLOG(title) ("HTTP/1.1 200 OK\r\n" \ "Content-Type: text/html\r\n\r\n" \ "\r\n" \ "\r\n" \ "\r\n" \ "" title "\r\n" \ "\r\n" \ "\r\n" \ "
\r\n")

#define HTML_EPILOG		("
\r\n" \ "\r\n" \ "\r\n") // This function will be called by mongoose on every new request. static int request_handler(struct mg_connection *conn) { const struct mg_request_info *request_info = mg_get_request_info(conn); SPIAGENT_COMMAND_MSG_t cmd; SPIAGENT_RESPONSE_MSG_t resp; const char *p; int32_t error; // We only allow GET if (strcasecmp(request_info->request_method, "GET")) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: Illegal method => %s", request_info->request_method); #endif return 0; } #ifdef MUNTSOS // Handle LPC1114 firmware update if (!strcasecmp(request_info->uri, "/LPC1114/flash")) { FILE *zz; char outbuf[256]; mg_printf(conn, HTML_PROLOG("Flashing LPC1114")); // Kickoff LPC1114 flash update command zz = popen(CMDFLASH " 2>&1", "r"); if (zz == NULL) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: popen() for %s failed, %s", CMDFLASH, strerror(errno)); #endif mg_printf(conn, "ERROR: popen() failed, %s\r\n", strerror(errno)); mg_printf(conn, HTML_EPILOG); return 1; } // Copy stdout from LPC1114 flash update script while (fgets(outbuf, sizeof(outbuf), zz)) mg_printf(conn, outbuf); // Close the pipeline if (pclose(zz)) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: pclose() for %s failed, %s", CMDFLASH, strerror(errno)); #endif mg_printf(conn, "ERROR: pclose() failed, %s\r\n", strerror(errno)); mg_printf(conn, HTML_EPILOG); return 1; } mg_printf(conn, HTML_EPILOG); return 1; } // Handle LPC1114 reset if (!strcasecmp(request_info->uri, "/LPC1114/reset")) { FILE *zz; char outbuf[256]; mg_printf(conn, HTML_PROLOG("Resetting LPC1114")); // Kickoff LPC1114 reset command zz = popen(CMDRESET " 2>&1", "r"); if (zz == NULL) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: popen() for %s failed, %s", CMDRESET, strerror(errno)); #endif mg_printf(conn, "ERROR: popen() failed, %s\r\n", strerror(errno)); mg_printf(conn, HTML_EPILOG); return 1; } // Copy stdout from LPC1114 reset script while (fgets(outbuf, sizeof(outbuf), zz)) mg_printf(conn, outbuf); // Close the pipeline if (pclose(zz)) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: pclose() for %s failed, %s", CMDRESET, strerror(errno)); #endif mg_printf(conn, "ERROR: pclose() failed, %s\r\n", strerror(errno)); mg_printf(conn, HTML_EPILOG); return 1; } mg_printf(conn, HTML_EPILOG); return 1; } #endif // We only allow URI /SPIAGENT if (strcasecmp(request_info->uri, "/SPIAGENT")) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: Illegal URI => %s", request_info->uri); #endif return 0; } // We require a query string if (request_info->query_string == NULL) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: Missing query string"); #endif return 0; } // The query string must be of the form [cmd=]n,n,n if (regexec(&query_template, request_info->query_string, 0, NULL, 0)) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: Illegal query string => %s", request_info->query_string); #endif return 0; } // Skip past optional cmd= p = request_info->query_string; if (!strncasecmp(p, "cmd=", 4)) p += 4; // Decode the query string if (sscanf(p, "%u,%u,%u", &cmd.command, &cmd.pin, &cmd.data) != 3) { #ifdef DEBUG syslog(LOG_INFO, "ERROR: sscanf() failed"); #endif return 0; } // Perform the SPI transfers spiagent_command_ioctl(&cmd, &resp, &error); #ifdef DEBUG if (error) syslog(LOG_INFO, "ERROR: spiagent_command_ioctl() failed, %s", strerror(error)); #endif // Generate reply mg_printf(conn, REPLYSTRING, error, resp.command, resp.pin, resp.data, resp.error); return 1; } int main(void) { struct servent *service; int32_t error; char portstr[8]; char *options[3]; struct mg_callbacks callbacks; struct mg_context *ctx; openlog(PROGRAMNAME, LOG_NDELAY|LOG_PID|LOG_PERROR, LOG_DAEMON); syslog(LOG_INFO, "Starting LPC1114 SPI Agent Firmware HTTP server"); // Look up service definition service = getservbyname(SERVICENAME, "tcp"); if (service == NULL) { syslog(LOG_ERR, "ERROR: getservbyname() failed, unknown service"); exit(1); } // Open the Raspberry Pi LPC1114 I/O Processor Expansion Board SPI device spiagent_open_ioctl(NULL, &error); if (error) { syslog(LOG_ERR, "ERROR: spiagent_open_ioctl() failed, %s", strerror(error)); exit(1); } // Become a nobody LINUX_drop_privileges("nobody", &error); if (error) { syslog(LOG_ERR, "ERROR: LINUX_drop_privileges() failed"); exit(1); } // Install signal handler for SIGTERM if (signal(SIGTERM, SIGTERMhandler) == SIG_ERR) { syslog(LOG_ERR, "ERROR: signal() for SIGTERM failed, %s", strerror(errno)); exit(1); } // Compile the query string validation regular expression template if (regcomp(&query_template, REGEX_QUERY_TEMPLATE, REG_EXTENDED|REG_ICASE)) { fprintf(stderr, "ERROR: regcomp() failed\n"); exit(1); } // Switch to background if (daemon(0, 0)) { syslog(LOG_ERR, "ERROR: daemon() failed, %s", strerror(errno)); exit(1); } // Request our callback handler memset(&callbacks, 0, sizeof(callbacks)); callbacks.begin_request = request_handler; // Start the web server. memset(portstr, 0, sizeof(portstr)); snprintf(portstr, sizeof(portstr), "%d", ntohs(service->s_port)); options[0] = "listening_ports"; options[1] = portstr; options[2] = NULL; ctx = mg_start(&callbacks, NULL, (const char **) options); // Put the main thread to sleep until we are terminated by SIGTERM pause(); // Stop the HTTP server mg_stop(ctx); // Close the Raspberry Pi LPC1114 I/O Processor Expansion Board SPI device spiagent_close_ioctl(&error); if (error) { syslog(LOG_ERR, "ERROR: spiagent_close_ioctl() failed, %s", strerror(error)); exit(1); } exit(0); }