Remove HTTP server, get ready to write basic threaded program
authorSam Moore <[email protected]>
Wed, 14 Aug 2013 10:04:50 +0000 (18:04 +0800)
committerSam Moore <[email protected]>
Wed, 14 Aug 2013 10:04:50 +0000 (18:04 +0800)
We will use a FastCGI to handle HTTP requests instead of writing a custom HTTP server.

rpi/log.c
rpi/log.h
rpi/main.c [new file with mode: 0644]
rpi/network.c [deleted file]
rpi/network.h [deleted file]
rpi/options.h
rpi/webserver.c [deleted file]

index 3a8c55c..15e6618 100644 (file)
--- a/rpi/log.c
+++ b/rpi/log.c
@@ -1,16 +1,43 @@
+/**
+ * @file log.c
+ * @purpose Implement logging and error handling functions
+ */
+
+
+#include <unistd.h>
+
+// --- Custom headers --- //
 #include "log.h"
 #include "options.h"
-#include <unistd.h>
 
-static int last_len = 0;
+// --- Static variables --- //
+static char * unspecified_funct = (char*)"???";
+
+// --- Function implementations --- //
 
-void log_print(int level, char * funct, char * fmt, ...)
+/**
+ * @funct Log
+ * @purpose Print a message to stderr
+ * @param level - Specify how severe the message is.
+       If level is higher (less urgent) than the program's verbosity (see options.h) no message will be printed
+ * @param funct - String indicating the function name from which this function was called.
+       If this is NULL, Log will show the unspecified_funct string instead
+ * @param fmt - A format string
+ * @param ... - Arguments to be printed according to the format string
+ */
+void Log(int level, char * funct, char * fmt, ...)
 {
-       if (level > options.verbosity)
+       if (fmt == NULL) // sanity check
+               Fatal("Log", "Format string is NULL");
+
+       // Don't print the message unless we need to
+       if (level > options.verbosity) 
                return;
 
-       
+       if (funct == NULL)
+               funct = unspecified_funct;
 
+       // Make a human readable severity string
        char severity[BUFSIZ];
        switch (level)
        {
@@ -31,30 +58,43 @@ void log_print(int level, char * funct, char * fmt, ...)
                        break;
        }
 
-       if (funct != NULL)
-               last_len = fprintf(stderr, "%s [%d] : %s : %s - ", options.program, getpid(), severity, funct);
-       else
-       {
-               for (int i = 0; i < last_len; ++i);
-                       fprintf(stderr, " ");
-       }
+       // Print: Program name, PID, severity string, function name first
+       fprintf(stderr, "%s [%d] : %s : %s - ", options.program, getpid(), severity, funct);
+
+       // Then pass additional arguments with the format string to vfprintf for printing
        va_list va;
        va_start(va, fmt);
        vfprintf(stderr, fmt, va);
        va_end(va);
+
+       // End log messages with a newline
        fprintf(stderr, "\n");
 }
 
-void error(char * funct, char * fmt, ...)
+/**
+ * @funct Fatal
+ * @purpose Handle a Fatal error in the program by printing a message and exiting the program
+       CALLING THIS FUNCTION WILL CAUSE THE PROGAM TO EXIT
+ * @param funct - Name of the calling function
+ * @param fmt - A format string
+ * @param ... - Arguments to be printed according to the format string
+ */
+void Fatal(char * funct, char * fmt, ...)
 {
-       if (funct != NULL)
-               last_len = fprintf(stderr, "%s [%d] : Fatal error in %s - ", options.program, getpid(), funct);
-       else
+       
+       if (fmt == NULL)
        {
-               for (int i = 0; i < last_len; ++i)
-                       fprintf(stderr, " ");
-               fprintf(stderr, "Fatal - ");
+               // Fatal error in the Fatal function.
+               // (This really shouldn't happen unless someone does something insanely stupid)
+               Fatal("Fatal", "Format string is NULL");
+               return; // Should never get here
        }
+
+       if (funct == NULL)
+               funct = unspecified_funct;
+
+       fprintf(stderr, "%s [%d] : %s : FATAL - ", options.program, getpid(), funct);
+
        va_list va;
        va_start(va, fmt);
        vfprintf(stderr, fmt, va);
index 75e2780..99d54d0 100644 (file)
--- a/rpi/log.h
+++ b/rpi/log.h
@@ -1,16 +1,22 @@
+/**
+ * @file log.h
+ * @purpose Declaration of functions for printing log messages and/or terminating program after a fatal error
+ */
+
 #ifndef _LOG_H
 #define _LOG_H
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdbool.h>
-
 #include <stdarg.h>
 
+
+// An enum to make the severity of log messages human readable in code
 enum {LOGERR=0, LOGWARN=1, LOGNOTE=2, LOGINFO=3,LOGDEBUG=4};
 
-extern void log_print(int level, char * funct, char * fmt,...);
-extern void error(char * funct, char * fmt, ...);
+extern void Log(int level, char * funct, char * fmt,...); // General function for printing log messages to stderr
+extern void Fatal(char * funct, char * fmt, ...); // Function that deals with a fatal error (prints a message, then exits the program).
 
 #endif //_LOG_H
 
diff --git a/rpi/main.c b/rpi/main.c
new file mode 100644 (file)
index 0000000..23ef071
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * @file main.c
+ * @purpose Entry point to the program, starts threads, handles cleanup on program exit
+ */
+
+// --- Standard headers --- //
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h> // for signal handling
+
+// --- Custom headers --- //
+#include "options.h"
+
+// --- Variable definitions --- //
+Options g_options; // options passed to program through command line arguments
+
+// --- Function definitions --- //
+
+/**
+ * @funct ParseArguments
+ * @purpose Parse command line arguments, set up an options variable
+ * @param argc - Num args
+ * @param argv - Array of args
+ * @param opts - Pointer to options.  &g_options
+ */
+void ParseArguments(int argc, char ** argv, Options * opts)
+{
+       options.program = argv[0]; // program name
+       options.verbosity = LOGDEBUG; // default log level
+       if (argc > 1)
+               options.port = atoi(argv[1]); // Allow us change the port for testing (I keep getting "address in use" errors)
+       else
+               options.port = 8080; // Using 8080 instead of 80 for now because to use 80 you have to run the program as root
+
+       Log(LOGDEBUG, "ParseArguments", "Called as %s with %d arguments.", options.program, argc);
+}
+
+/**
+ * @funct SignalHandler
+ * @purpose Handle signals
+ * @param sig - The signal
+ */
+void SignalHandler(int sig)
+{
+       // At the moment just always exit.
+       // Call `exit` so that Cleanup will be called to... clean up.
+       Log(LOGWARN, "SignalHandler", "Got signal %d (%s). Exiting.", sig, strsignal(sig));
+       exit(sig);
+}
+
+/**
+ * @funct Cleanup
+ * @purpose Called when program exits
+ */
+void Cleanup()
+{
+       Log(LOGDEBUG, "Cleanup", "Begin cleanup.");
+       Log(LOGDEBUG, "Cleanup", "Finish cleanup.");
+
+}
+
+
diff --git a/rpi/network.c b/rpi/network.c
deleted file mode 100644 (file)
index 1a1b862..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-#include "network.h"
-#include <assert.h>
-#include <errno.h>
-#include <sys/select.h>
-#include "log.h"
-
-#define h_addr h_addr_list[0]
-
-
-
-
-
-int Network_get_port(int sfd)
-{
-       static struct sockaddr_in sin;
-       static socklen_t len = sizeof(struct sockaddr_in);
-
-       if (getsockname(sfd, (struct sockaddr *)&sin, &len) != 0)
-                error("Network_port", "getsockname : %s", strerror(errno));
-       return ntohs(sin.sin_port);
-}
-
-int Network_server_bind(int port, int * bound)
-{
-       int sfd = socket(PF_INET, SOCK_STREAM, 0);
-       if (sfd < 0)
-       {
-               error("Network_server", "Creating socket on port %d : %s", port, strerror(errno));
-       }
-
-       struct   sockaddr_in name;
-
-       name.sin_family = AF_INET;
-       name.sin_addr.s_addr = htonl(INADDR_ANY);
-       name.sin_port = htons(port);
-
-       if (bind( sfd, (struct sockaddr *) &name, sizeof(name) ) < 0)
-       {
-               error("Network_server", "Binding socket on port %d : %s", port, strerror(errno));
-       }
-
-       if (bound != NULL)
-               *bound = Network_get_port(sfd);
-       return sfd;     
-}
-
-int Network_server_listen(int sfd, char * addr)
-{
-       int port = Network_get_port(sfd);
-       if (listen(sfd, 1) < 0)
-       {
-               error("Network_server", "Listening on port %d : %s", port, strerror(errno));
-       }
-       
-       int psd;
-       if (addr == NULL)
-               psd = accept(sfd, 0, 0);
-       else
-       {
-               struct  sockaddr_in client;
-               struct  hostent *hp;
-
-               client.sin_family = AF_INET;
-               hp = gethostbyname(addr);
-               bcopy ( hp->h_addr, &(client.sin_addr.s_addr), hp->h_length);
-               client.sin_port = htons(port);
-               socklen_t len = sizeof(client);
-
-               psd = accept(sfd, (struct sockaddr*)&client, &len);
-       }
-       //close(sfd); // don't close the bind socket here; we might want to reuse the port
-       assert(psd >= 0);
-       return psd;
-}
-
-int Network_server(char * addr, int port)
-{
-       int bind = Network_server_bind(port, &port);
-       int sfd = Network_server_listen(bind, addr);
-       close(bind); // won't be able to reuse the port (it goes into TIME_WAIT)
-       return sfd;
-}
-
-int Network_client(const char * addr, int port, int timeout)
-{
-       int sfd = socket(PF_INET, SOCK_STREAM, 0);
-
-       //log_print(2, "Network_client", "Created socket");
-       long arg = fcntl(sfd, F_GETFL, NULL);
-       arg |= O_NONBLOCK;
-       fcntl(sfd, F_SETFL, arg);
-
-       if (sfd < 0)
-       {
-               error("Network_client", "Creating socket for address %s:%d : %s", addr, port, strerror(errno));
-       }
-       struct  sockaddr_in server;
-       struct  hostent *hp;
-
-
-       server.sin_family = AF_INET;
-       hp = gethostbyname(addr);
-       if (hp == NULL)
-       {
-               error("Network_client", "Can't get host by name %s", addr);
-       }
-       bcopy ( hp->h_addr, &(server.sin_addr.s_addr), hp->h_length);
-       server.sin_port = htons(port);
-
-
-       int res = connect(sfd, (struct sockaddr *) &server, sizeof(server));
-       
-
-       if (res < 0 && errno == EINPROGRESS)
-       {
-               
-               fd_set writeSet;
-               FD_ZERO(&writeSet);
-               FD_SET(sfd, &writeSet);
-
-               struct timeval tv;
-               tv.tv_sec = timeout;
-               tv.tv_usec = 0;
-
-               struct timeval * tp;
-               tp = (timeout < 0) ? NULL : &tv;
-               
-               int err = select(sfd+1, NULL, &writeSet, NULL, tp);
-               
-               if (err == 0)
-               {
-                       error("Network_client", "Timed out trying to connect to %s:%d after %d seconds", addr, port, timeout);
-               }
-               else if (err < 0)
-               {
-                       error("Network_client", "Connecting to %s:%d - Error in select(2) call : %s", addr, port, strerror(errno));
-               }
-               else if (FD_ISSET(sfd, &writeSet))
-               {
-                       int so_error;
-                       socklen_t len = sizeof so_error;
-                       getsockopt(sfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
-                       if (so_error != 0)
-                       {
-                               error("Network_client", "Connecting to %s:%d : %s", addr, port, strerror(so_error));
-                       }
-               }
-               else
-               {
-                       error("Network_client", "select(2) returned %d but the socket is not writable!?", err);
-               }
-       }
-       else
-       {
-               error("Network_client", "Connecting to %s:%d : %s", addr, port, strerror(errno));
-       }
-
-       arg = fcntl(sfd, F_GETFL, NULL);
-       arg &= (~O_NONBLOCK);
-       fcntl(sfd, F_SETFL, arg);
-       
-       
-       
-       return sfd;
-}
-
-void Network_close(int sfd)
-{
-       if (shutdown(sfd, 2) != 0)
-       {
-               error("Network_close", "Closing socket : %s", strerror(errno));
-       }
-       close(sfd);
-}
diff --git a/rpi/network.h b/rpi/network.h
deleted file mode 100644 (file)
index 07c9459..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef _NETWORK_H
-#define _NETWORK_H
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <strings.h>
-#include <stdarg.h>
-
-extern int Network_get_port(int socket); // get port used by socket
-extern int Network_server(char * addr, int port);
-extern int Network_client(const char * addr, int port, int timeout);
-
-extern int Network_server_bind(int port, int * bound);
-extern int Network_server_listen(int sfd, char * addr);
-
-extern void Network_close(int sfd);
-
-#endif //_NETWORK_H
-
-//EOF
index 2a6d323..df78870 100644 (file)
@@ -3,18 +3,16 @@
  * @purpose Declaration of structure to handle options passed to program
  */
 
-#include <stdint.h>
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
 
 typedef struct
 {
        const char * program; //name of program
-       uint8_t verbosity; // verbosity level
-       int port; // port to use for webserver
-       int bound_sfd; // socket webserver has bound to
-       int sfd; // socket connected to client
-       
-} Options;
+       int verbosity; // verbosity level
 
+} Options;
 
+extern Options g_options;
 
-extern Options options;
+#endif //_OPTIONS_H
diff --git a/rpi/webserver.c b/rpi/webserver.c
deleted file mode 100644 (file)
index 6ff4f46..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * @file webserver.c
- * @purpose Test implementing a minimalistic webserver
- * 
- */
-
-#define _POSIX_C_SOURCE 200809L // needed for some POSIX stuff to work
-
-// --- Standard headers --- //
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h> // string helper functions
-#include <ctype.h> // character types
-#include <signal.h> // for signal handling
-
-// --- Custom headers --- //
-#include "log.h" // C functions to handle logging
-#include "options.h" // Options structure
-#include "network.h" // C functions to handle low level networking
-
-// --- Variable definitions --- //
-Options options; // declared in "options.h"
-
-// --- Function definitions --- //
-
-/**
- * @funct ParseArguments
- * @purpose Parse Arguments and setup the global options variable
- * @param argc - Num args
- * @param argv - Array of args
- */
-void ParseArguments(int argc, char ** argv)
-{
-       options.program = argv[0];
-       options.verbosity = LOGDEBUG;
-       if (argc > 1)
-               options.port = atoi(argv[1]); // Allow us change the port for testing (I keep getting "address in use" errors)
-       else
-               options.port = 8080; // Using 8080 instead of 80 for now because to use 80 you have to run the program as root
-       options.sfd = -1;
-       options.bound_sfd = -1;
-       log_print(LOGDEBUG, "ParseArguments", "Called as %s with %d arguments.", options.program, argc);
-}
-
-/**
- * @funct SignalHandler
- * @purpose Handle signals
- * @param sig - The signal
- */
-void SignalHandler(int sig)
-{
-       // At the moment just always exit.
-       // Call `exit` so that Cleanup will be called to... clean up.
-       log_print(LOGWARN, "SignalHandler", "Got signal %d (%s). Exiting.", sig, strsignal(sig));
-       exit(sig);
-}
-
-/**
- * @funct Cleanup
- * @purpose Called when program exits
- */
-void Cleanup()
-{
-       log_print(LOGDEBUG, "Cleanup", "Begin cleanup.");
-       if (options.sfd >= 0)
-       {
-               Network_close(options.sfd); // close socket
-               options.sfd = -1;
-       }
-       if (options.bound_sfd >= 0)
-       {
-               Network_close(options.bound_sfd); // unbind
-               options.bound_sfd = -1;
-       }
-       log_print(LOGDEBUG, "Cleanup", "Unbound from port %d successfully", options.port);
-       log_print(LOGDEBUG, "Cleanup", "Done.");
-
-}
-
-
-/**
- * @funct Get
- * @purpose Respond to a GET request
- * @param request - The request string
- * @param sfd - Socket to respond through
- */
-void Get(char * request, int sfd)
-{
-       log_print(LOGDEBUG, "Get", "Got GET request: \"%s\"", request);
-
-       int i = 0;
-       while (!isspace(request[++i]) && request[i] != '\0'); //NOTE: Don't need to check first character
-       request[i] = '\0';
-
-       char response[BUFSIZ];
-       // TODO: Magical low level interfacing stuff!
-
-       int len = sprintf(response, "HTTP/1.1 200 OK\nContent-type: text/html\n\n");
-       write(sfd, response, len);
-       len = 0;
-
-       if (strcmp("/sensor", request) == 0) // dummy test
-       {
-               len = sprintf(response, "SENSOR OFFLINE\n");
-       }
-       else
-       {
-               FILE * f = fopen(request+1, "r");
-               if (f == NULL)
-               {
-                       log_print(LOGWARN, "Get", "File \"%s\" doesn't exist", request+1);
-                       len = sprintf(response, "You requested \"%s\" using GET\n", request);
-               }
-               else
-               {
-                       while (fgets(response, sizeof(response), f) != NULL)
-                       {
-                               write(sfd, response, strlen(response));
-                       }
-               }
-       }
-       if (len > 0)
-               write(sfd, response, len);
-}
-
-/**
- * @funct Post
- * @purpose Respond to a POST request
- * @param request - The request string
- * @param sfd - Socket to respond through
- */
-void Post(char * request, int sfd)
-{
-       log_print(LOGDEBUG, "Post", "Got POST request: \"%s\"", request);
-       int i = 0;
-       while (!isspace(request[++i]) && request[i] != '\0'); //NOTE: Don't need to check first character
-       request[i] = '\0';
-
-       char response[BUFSIZ];
-       int len = sprintf(response, "HTTP/1.1 200 OK\nContent-type: text/html\n\n");
-       write(sfd, response, len);
-       len = 0;
-
-       // TODO: Magical low level interfacing stuff!
-
-
-       if (strcmp("/actuator", request) == 0) // dummy test
-       {
-               len = sprintf(response, "ACTUATOR OFFLINE\n");
-       }
-       else
-       {       
-               len = sprintf(response, "You requested \"%s\" using POST\n", request);
-       }
-       if (len > 0)
-               write(sfd, response, len);
-
-}
-
-/**
- * @funct main
- * @purpose Main program
- * @param argc - Num arguments
- * @param argv - Argument string array
- * @returns error code (0 for no error)
- */
-
-int main(int argc, char ** argv)
-{
-       // Parse Arguments
-       ParseArguments(argc, argv);
-       // Set Cleanup to be called on program exit
-       atexit(Cleanup);
-
-       // Setup signal handlers
-       int signals_to_handle[] = {SIGTERM, SIGINT, SIGHUP, SIGPIPE};
-       for (int i = 0; i < sizeof(signals_to_handle)/sizeof(int); ++i)
-       {
-               int s = signals_to_handle[i];
-               if (signal(s, SignalHandler) == SIG_ERR)
-                       error("main", "Setting signal handler for %d (%s): %s", s, strsignal(s), strerror(errno));
-       }
-
-       // Bind to the port
-       options.bound_sfd = Network_server_bind(options.port, &(options.port));
-       log_print(LOGDEBUG, "main", "Bound to port %d succesfully", options.port);
-
-       while (true)
-       {
-                       // Listen for a client
-                       options.sfd = Network_server_listen(options.bound_sfd, NULL);
-                       
-                       log_print(LOGDEBUG, "main", "Connected to client");
-       
-                       char buffer[BUFSIZ]; //NOTE: Won't be able to respond to requests longer than BUFSIZ
-                       // read a request
-                       int len = read(options.sfd, buffer, sizeof(buffer));
-                       log_print(LOGDEBUG, "main", "Read %d characters. Buffer is \"%s\"", len, buffer);
-
-                       // Parse request
-                       for (int i = 0; i < sizeof(buffer) && buffer[i] != '\0'; ++i)
-                       {
-                               // Look for "GET" or "POST" followed by a whitespace
-                               if (isspace(buffer[i])) // whitespace
-                               {
-                                       while (isspace(buffer[++i]) && buffer[i] != '\0'); // Skip whitespace
-                                       char * req = buffer+i; // set request string
-                               
-                                       buffer[i-1] = '\0'; // terminate request type
-                                       while (buffer[++i] != '\n' && buffer[i] != '\0'); // find next newline
-                                       buffer[i-1] = '\0';
-                                       
-                                       if (strcmp("GET", buffer) == 0) // Compare with "GET"
-                                       {
-                                               Get(req, options.sfd);
-                                       }
-                                       else if (strcmp("POST", buffer) == 0) // Compare with "POST"
-                                       {
-                                               Post(req, options.sfd);
-                                       }
-                                       else // Unknown request
-                                       {
-                                               log_print(LOGWARN, "main", "Unrecognised request type \"%s\" (request \"%s\")", buffer, req);
-                                               char response[] = "Error: Unrecognised request\n";
-                                               write(options.sfd, response, sizeof(response));
-                                       }
-                                       break;
-                               }
-                       }
-
-                       // Close connection
-                       Network_close(options.sfd);
-                       options.sfd = -1;
-                       log_print(LOGDEBUG, "main", "Closed connection to client");
-       }
-
-
-       
-       
-       return 0;
-}
-
-

UCC git Repository :: git.ucc.asn.au