Use experiment folders instead of chdir
authorJeremy Tan <[email protected]>
Sat, 19 Oct 2013 07:45:30 +0000 (15:45 +0800)
committerJeremy Tan <[email protected]>
Sat, 19 Oct 2013 07:45:30 +0000 (15:45 +0800)
server/actuator.c
server/common.h
server/control.c
server/control.h
server/fastcgi.c
server/fastcgi.h
server/main.c
server/options.h
server/sensor.c

index 9b9a1a8..2be4194 100644 (file)
@@ -8,9 +8,6 @@
 // Files containing GPIO and PWM definitions
 #include "bbb_pin.h"
 
-
-
-
 /** Number of actuators **/
 int g_num_actuators = 0;
 
@@ -79,12 +76,17 @@ void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg)
        {
                case CONTROL_START:
                        {
+                               // Set filename
                                char filename[BUFSIZ];
-                               const char *experiment_name = (const char*) arg;
+                               const char *experiment_path = (const char*) arg;
+                               int ret;
+
+                               ret = snprintf(filename, BUFSIZ, "%s/actuator_%d", experiment_path, a->id);
 
-                               if (snprintf(filename, BUFSIZ, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
+                               if (ret >= BUFSIZ) 
                                {
-                                       Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
+                                       Fatal("Experiment path \"%s\" too long (%d, limit %d)",
+                                                       experiment_path, ret, BUFSIZ);
                                }
 
                                Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
@@ -141,7 +143,7 @@ void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg)
  */
 void Actuator_SetModeAll(ControlModes mode, void * arg)
 {
-       for (int i = 0; i < ACTUATORS_MAX; i++)
+       for (int i = 0; i < g_num_actuators; i++)
                Actuator_SetMode(&g_actuators[i], mode, arg);
 }
 
index d4defd9..fa997c5 100644 (file)
@@ -46,6 +46,7 @@
 
 
 extern bool PathExists(const char * path);
+extern bool DirExists(const char * path);
 
 
 
index c6b5823..59f9b73 100644 (file)
@@ -8,16 +8,30 @@
 #include "sensor.h"
 #include "actuator.h"
 #include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 typedef struct ControlData {
        ControlModes current_mode;
        pthread_mutex_t mutex;
        struct timeval start_time;
        char user_name[31]; // The user who owns the currently running experiment
+       char experiment_dir[BUFSIZ]; //Directory for experiment
+       char experiment_name[BUFSIZ];
 } ControlData;
 
 ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
 
+bool DirExists(const char *path)
+{
+       DIR *dir = opendir(path);
+       if (dir) {
+               closedir(dir);
+               return true;
+       }
+       return false;
+}
+
 bool PathExists(const char *path) 
 {
        FILE *fp = fopen(path, "r");
@@ -28,6 +42,43 @@ bool PathExists(const char *path)
        return false;
 }
 
+/**
+ * Lists all experiments for the current user.
+ * @param The context to work in
+ */
+void ListExperiments(FCGIContext *context) 
+{
+       DIR * dir = opendir(context->user_dir);
+       if (dir == NULL)
+       {
+               FCGI_RejectJSON(context, "Failed to open user directory");
+               return;
+       }
+       struct dirent * ent;
+       FCGI_BeginJSON(context, STATUS_OK);
+       FCGI_JSONKey("experiments");
+       FCGI_PrintRaw("[");
+
+       bool first = true;
+       while ((ent = readdir(dir)) != NULL) {
+               char *ext = strrchr(ent->d_name, '.');
+               if (ext && !strcmp(ext, ".exp")) {
+                       if (!first) {
+                               FCGI_PrintRaw(", ");
+                       }
+
+                       *ext = '\0'; // Ummm... probably not a great idea
+                       FCGI_PrintRaw("\"%s\"", ent->d_name);
+                       first = false;
+               }
+       }
+       FCGI_PrintRaw("]");
+       FCGI_EndJSON();
+       
+       closedir(dir);
+       return;
+}
+
 /**
  * System control handler. This covers high-level control, including
  * admin related functions and starting/stopping experiments..
@@ -40,10 +91,6 @@ void Control_Handler(FCGIContext *context, char *params) {
        bool force = false;
        ControlModes desired_mode;
        
-       
-       
-
-
        // Login/auth now handled entirely in fastcgi.c and login.c
        //TODO: Need to not have the ability for any user to stop someone else' experiment...
        // (achieve by storing the username of the person running the current experiment, even when they log out?)
@@ -54,43 +101,19 @@ void Control_Handler(FCGIContext *context, char *params) {
                {"name", &name, FCGI_STRING_T}
        };
 
-
        if (!FCGI_ParseRequest(context, params, values, 3))
                return;
 
-       //HACKETY HACK HACK (should really be a seperate function)
-       if (strcmp(action, "list") == 0)
-       {
-               DIR * dir = opendir(context->user_name);
-               if (dir == NULL)
-               {
-                       FCGI_RejectJSON(context, "Failed to open user directory");
-                       return;
-               }
-               struct dirent * ent;
+       if (!strcmp(action, "identify")) {
                FCGI_BeginJSON(context, STATUS_OK);
-               FCGI_JSONKey("experiments");
-               FCGI_PrintRaw("[");
-
-               bool first = true;
-               while ((ent = readdir(dir)) != NULL)
-               {
-                       char * c;
-                       for (c = ent->d_name; *c != '\0' && *c != '.'; ++c);
-
-                       if (*c != '\0' && strcmp(c, ".exp") == 0)
-                       {
-                               if (!first)
-                                       FCGI_PrintRaw(",");
-                               *c = '\0'; // Ummm... probably not a great idea
-                               FCGI_PrintRaw(ent->d_name);
-                               first = false;
-                       }
-               }
-               FCGI_PrintRaw("]");
+               FCGI_JSONLong("control_state_id", g_controls.current_mode);
+               FCGI_JSONPair("control_user_name", g_controls.user_name);
+               FCGI_JSONPair("control_experiment_name", g_controls.experiment_name);
                FCGI_EndJSON();
-               
-               return; // Dear god this is terrible
+               return;
+       } else if (!strcmp(action, "list")) {
+               ListExperiments(context);
+               return;
        }
        //TODO: Need a "load" action to set data files (but not run) from a past experiment
 
@@ -111,67 +134,71 @@ void Control_Handler(FCGIContext *context, char *params) {
                return;
        }
 
-       if (*g_controls.user_name != '\0' && strcmp(g_controls.user_name,context->user_name) != 0)
+       if (strcmp(g_controls.user_name, context->user_name) != 0)
        {
-               if (context->user_type != USER_ADMIN)
-               {
+               if (context->user_type != USER_ADMIN) {
                        FCGI_RejectJSON(context, "Another user has an experiment in progress.");
                        return;
                }
                
-               if (!force)
-               {
+               if (!force) {
                        Log(LOGERR, "User %s is currently running an experiment!", g_controls.user_name);
                        FCGI_RejectJSON(context, "Pass \"force\" to take control over another user's experiment");
                        return;
                }
        }
 
-       
-       
-       //HACK
-       chdir(context->user_name);
-
        void *arg = NULL;
+       char experiment_dir[BUFSIZ] = {0};
        if (desired_mode == CONTROL_START) {
-               if (PathExists(name) && !force) {
-                       FCGI_RejectJSON(context, "An experiment with that name already exists.");
-                       chdir(g_options.root_dir); // REVERSE HACK
+               int ret;
+
+               if (*name == '\0') {
+                       FCGI_RejectJSON(context, "An experiment name must be specified.");
+                       return;
+               } if (strpbrk(name, INVALID_CHARACTERS)) {
+                       FCGI_RejectJSON(context, 
+                               "An experiment name must not contain: " INVALID_CHARACTERS_JSON);
                        return;
                }
-               char * c = (char*)name;
-               for (c = (char*)name; *c != '\0' && *c != '.'; ++c);
-               if (*c == '.')
-               {
-                       FCGI_RejectJSON(context, "Can't include '.' characters in experiment names (at this point we don't care anymore, go find someone who does).");
-                       chdir(g_options.root_dir); // REVERSE HACK
+
+               if (*(context->user_dir) == '\0') {
+                       FCGI_RejectJSON(context, "Not creating experiment in root dir.");
                        return;
                }
-               arg = (void*)name;
-       }
 
-       
+               ret = snprintf(experiment_dir, BUFSIZ, "%s/%s.exp", 
+                                               context->user_dir, name);
+               if (ret >= BUFSIZ) {
+                       FCGI_RejectJSON(context, "The experiment name is too long.");
+                       return;
+               } else if (DirExists(experiment_dir) && !force) {
+                       FCGI_RejectJSON(context, "An experiment with that name already exists.");
+                       return;
+               }
+
+               arg = (void*) experiment_dir;
+       }
 
        const char *ret;
-       if ((ret = Control_SetMode(desired_mode, arg)) != NULL) 
-       {
+       if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
                FCGI_RejectJSON(context, ret);
-       } 
-       else 
-       {
-               if (desired_mode == CONTROL_STOP)
+       } else {
+               if (desired_mode == CONTROL_STOP) {
                        g_controls.user_name[0] = '\0';
-               else
-               {
-                       snprintf(g_controls.user_name, sizeof(g_controls.user_name), context->user_name);
+                       g_controls.experiment_dir[0] = '\0';
+                       g_controls.experiment_name[0] = '\0';
+               } else if (desired_mode == CONTROL_START) {
+                       snprintf(g_controls.user_name, sizeof(g_controls.user_name), 
+                                               "%s", context->user_name);
+                       snprintf(g_controls.experiment_dir, sizeof(g_controls.experiment_dir),
+                                               "%s", experiment_dir);
+                       snprintf(g_controls.experiment_name, sizeof(g_controls.experiment_name),
+                                               "%s", name);
                }
-               FCGI_BeginJSON(context, STATUS_OK);
-               FCGI_JSONPair("description", "ok");
-               FCGI_EndJSON();
-       }
 
-       //REVERSE HACK
-       chdir(g_options.root_dir);
+               FCGI_AcceptJSON(context, "Ok", NULL);
+       }
 }
 
 /**
@@ -192,18 +219,13 @@ const char* Control_SetMode(ControlModes desired_mode, void * arg)
        else switch (desired_mode) {
                case CONTROL_START:
                        if (g_controls.current_mode == CONTROL_STOP) {
-                               const char * name = arg;
-                               if (!*name)
-                                       ret = "An experiment name must be specified";
-                               else if (strpbrk(name, INVALID_CHARACTERS))
-                                       ret = "The experiment name must not contain: " INVALID_CHARACTERS_JSON;
-                               else {
-                                       FILE *fp = fopen((const char*) arg, "a");
-                                       if (fp) {
-                                               fclose(fp);
-                                               gettimeofday(&(g_controls.start_time), NULL);
-                                       } else
-                                               ret = "Cannot open experiment name marker";
+                               const char * path = arg;
+                               if (mkdir(path, 0777) != 0 && errno != EEXIST) {
+                                       Log(LOGERR, "Couldn't create experiment directory %s - %s", 
+                                               path, strerror(errno));
+                                       ret = "Couldn't create experiment directory.";
+                               } else {
+                                       gettimeofday(&(g_controls.start_time), NULL);
                                }
                        } else 
                                ret = "Cannot start when not in a stopped state.";
index 7b3511a..3ceea9f 100644 (file)
@@ -14,9 +14,9 @@ typedef enum ControlModes {
 } ControlModes;
 
 /** Invalid filename characters **/
-#define INVALID_CHARACTERS "\"*/:<>?\\|"
+#define INVALID_CHARACTERS "\"*/:<>?\\|"
 /** The same as INVALID_CHARACTERS, except escaped for use in JSON strings **/
-#define INVALID_CHARACTERS_JSON "\\\"*/:<>?\\\\|"
+#define INVALID_CHARACTERS_JSON "\\\"*/:<>?\\\\|"
 
 extern void Control_Handler(FCGIContext *context, char *params);
 extern const char* Control_SetMode(ControlModes desired_mode, void * arg);
index a77a3a4..fd5839e 100644 (file)
  */ 
 static void IdentifyHandler(FCGIContext *context, char *params) {
        bool ident_sensors = false, ident_actuators = false;
-       bool has_control = FCGI_HasControl(context, getenv("COOKIE_STRING"));
+       char control_key[CONTROL_KEY_BUFSIZ];
+       bool has_control;
        int i;
 
+       snprintf(control_key, CONTROL_KEY_BUFSIZ, "%s", getenv("COOKIE_STRING"));
+       has_control = FCGI_HasControl(context, control_key);
+       
        FCGIValue values[2] = {{"sensors", &ident_sensors, FCGI_BOOL_T},
                                         {"actuators", &ident_actuators, FCGI_BOOL_T}};
        if (!FCGI_ParseRequest(context, params, values, 2))
@@ -89,6 +93,7 @@ bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType use
        // Get current time
        time_t now = time(NULL);
        bool expired = now - context->control_timestamp > CONTROL_TIMEOUT;
+       int i;
 
        // Can't lock control if: User not actually logged in (sanity), or key is still valid and the user is not an admin
        if (user_type == USER_UNAUTH || 
@@ -104,7 +109,7 @@ bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType use
        // Generate a SHA1 hash for the user
        SHA_CTX sha1ctx;
        unsigned char sha1[20];
-       int i = rand();
+       i = rand();
        SHA1_Init(&sha1ctx);
        SHA1_Update(&sha1ctx, &now, sizeof(now));
        SHA1_Update(&sha1ctx, &i, sizeof(i));
@@ -112,25 +117,38 @@ bool FCGI_LockControl(FCGIContext *context, const char * user_name, UserType use
        for (i = 0; i < sizeof(sha1); i++)
                sprintf(context->control_key + i * 2, "%02x", sha1[i]);
 
-       // Set the IP address
+       // Set the IPv4 address
        snprintf(context->control_ip, 16, "%s", getenv("REMOTE_ADDR"));
+
        // Set the user name
        int uname_len = strlen(user_name);
-       if (snprintf(context->user_name, sizeof(context->user_name), "%s", user_name) < uname_len)
-       {
-               Log(LOGERR, "Username at %d characters too long (limit %d)", uname_len, sizeof(context->user_name));
+       i = snprintf(context->user_name, sizeof(context->user_name), "%s", user_name);
+       if (i < uname_len) {
+               Log(LOGERR, "Username at %d characters too long (limit %d)", 
+                       uname_len, sizeof(context->user_name));
                return false; // :-(
        }
        // Set the user type
        context->user_type = user_type;
+
+       // Build the user directory
+       i = snprintf(context->user_dir, sizeof(context->user_dir), "%s/%s", 
+                                       g_options.experiment_dir, context->user_name);
+       if (i >= sizeof(context->user_dir)) {
+               Log(LOGERR, "Experiment dir too long (required %d, limit %d)",
+                       i, sizeof(context->user_dir));
+               return false;
+       }
+
+       Log(LOGDEBUG, "User dir: %s", context->user_dir);
        // Create directory
-       if (mkdir(user_name, 0777) != 0 && errno != EEXIST)
+       if (mkdir(context->user_dir, 0777) != 0 && errno != EEXIST)
        {
-               Log(LOGERR, "Couldn't create user directory %s/%s - %s", g_options.root_dir, user_name, strerror(errno));
+               Log(LOGERR, "Couldn't create user directory %s - %s", 
+                       context->user_dir, strerror(errno));
                return false; // :-(
        }
 
-
        return true; // :-)
 }
 
@@ -491,16 +509,17 @@ void * FCGI_RequestLoop (void *data)
        while (FCGI_Accept() >= 0) {
                
                ModuleHandler module_handler = NULL;
-               char module[BUFSIZ], params[BUFSIZ];
-               //Don't need to copy if we're not modifying string contents
-               const char *cookie = getenv("COOKIE_STRING");
+               char module[BUFSIZ], params[BUFSIZ], control_key[CONTROL_KEY_BUFSIZ];
                
                //strncpy doesn't zero-truncate properly
                snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
                snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
 
+               //Hack to get the nameless cookie only
+               snprintf(control_key, CONTROL_KEY_BUFSIZ, "%s", getenv("COOKIE_STRING"));
+
                Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params);
-               Log(LOGDEBUG, "Cookie: %s", cookie);
+               Log(LOGDEBUG, "Control key: %s", control_key);
 
                
                //Remove trailing slashes (if present) from module query
@@ -538,7 +557,7 @@ void * FCGI_RequestLoop (void *data)
                        if (module_handler != Login_Handler && module_handler != IdentifyHandler && module_handler)
                        //if (false) // Testing
                        {
-                               if (!FCGI_HasControl(&context, cookie))
+                               if (!FCGI_HasControl(&context, control_key))
                                {
                                        FCGI_RejectJSON(&context, "Please login. Invalid control key.");
                                        continue;       
index 85dab57..de02501 100644 (file)
@@ -33,6 +33,8 @@ typedef enum StatusCodes {
 #define FCGI_RECEIVED(x) ((x) & FCGI_PARAM_RECEIVED)
 #define FCGI_TYPE(x) ((x) & ~(FCGI_PARAM_REQUIRED | FCGI_PARAM_RECEIVED))
 
+#define CONTROL_KEY_BUFSIZ 41
+
 typedef struct FCGIValue {
        const char *key;
        void *value;
@@ -47,13 +49,15 @@ typedef struct
        /**The time of last valid user access possessing the control key**/
        time_t control_timestamp;
        /**A SHA-1 hash that is the control key, determining who is logged in**/
-       char control_key[41]; 
+       char control_key[CONTROL_KEY_BUFSIZ]; 
        /**The IPv4 address of the logged-in user**/
        char control_ip[16];
        /**Determines if the user is an admin or not**/
        UserType user_type;
        /**Name of the logged in user**/
        char user_name[31];
+       /**User directory for the logged in user**/
+       char user_dir[BUFSIZ];
        /**The name of the current module**/
        const char *current_module;
        /**For debugging purposes?**/
index 7933bab..e7edc7e 100644 (file)
@@ -29,13 +29,11 @@ Options g_options; // options passed to program through command line arguments
  */
 void ParseArguments(int argc, char ** argv)
 {
-
-
        g_options.program = argv[0]; // program name
        g_options.verbosity = LOGDEBUG; // default log level
        // Set the main directory
-       if (getcwd(g_options.root_dir, sizeof(g_options.root_dir)) == NULL)
-               Fatal("Couldn't get current working directory - %s", strerror(errno));
+       //if (getcwd(g_options.root_dir, sizeof(g_options.root_dir)) == NULL)
+       //      Fatal("Couldn't get current working directory - %s", strerror(errno));
 
        gettimeofday(&(g_options.start_time), NULL); // Start time
 
@@ -43,9 +41,8 @@ void ParseArguments(int argc, char ** argv)
        g_options.auth_method = AUTH_NONE;  // Don't use authentication
        g_options.auth_uri = ""; // 
        g_options.ldap_base_dn = "";
+       g_options.experiment_dir = ".";
        
-
-
        for (int i = 1; i < argc; ++i)
        {
                if (argv[i][0] != '-')
@@ -76,6 +73,10 @@ void ParseArguments(int argc, char ** argv)
                        case 'd':
                                g_options.ldap_base_dn = argv[++i];
                                break;
+                       case 'e':
+                       // Experiments directory
+                               g_options.experiment_dir = argv[++i];
+                               break;
                        default:
                                Fatal("Unrecognised switch %s", argv[i]);
                                break;
@@ -89,7 +90,13 @@ void ParseArguments(int argc, char ** argv)
        Log(LOGDEBUG, "Pin Module Enabled: %d", g_options.enable_pin);
        Log(LOGDEBUG, "Auth URI: %s", g_options.auth_uri);
        Log(LOGDEBUG, "LDAP Base DN: %s", g_options.ldap_base_dn);
-       Log(LOGDEBUG, "Root directory: %s", g_options.root_dir);
+       //Log(LOGDEBUG, "Root directory: %s", g_options.root_dir);
+       Log(LOGDEBUG, "Experiment directory: %s", g_options.experiment_dir);
+
+       if (!DirExists(g_options.experiment_dir))
+       {
+               Fatal("Experiment directory '%s' does not exist.", g_options.experiment_dir);
+       }
 
        if (g_options.auth_uri[0] != '\0')
        {
index bf668b3..385a05b 100644 (file)
@@ -31,11 +31,8 @@ typedef struct
        /** Authentication method **/
        enum {AUTH_NONE, AUTH_LDAP, AUTH_SHADOW} auth_method;
 
-       /** Starting directory **/
-       char root_dir[BUFSIZ];
-
-       
-
+       /** Experiments directory **/
+       const char *experiment_dir;
 } Options;
 
 /** The only instance of the Options struct **/
index e136988..c41d39e 100644 (file)
@@ -120,11 +120,15 @@ void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg)
                        {
                                // Set filename
                                char filename[BUFSIZ];
-                               const char *experiment_name = (const char*) arg;
+                               const char *experiment_path = (const char*) arg;
+                               int ret;
+
+                               ret = snprintf(filename, BUFSIZ, "%s/sensor_%d", experiment_path, s->id);
 
-                               if (snprintf(filename, BUFSIZ, "%s_%d", experiment_name, s->id) >= BUFSIZ)
+                               if (ret >= BUFSIZ) 
                                {
-                                       Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
+                                       Fatal("Experiment path \"%s\" too long (%d, limit %d)",
+                                                       experiment_path, ret, BUFSIZ);
                                }
 
                                Log(LOGDEBUG, "Sensor %d with DataFile \"%s\"", s->id, filename);
@@ -291,7 +295,6 @@ void Sensor_Handler(FCGIContext *context, char * params)
        struct timeval now;
        gettimeofday(&now, NULL);
        double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime());
-
        int id = 0;
        const char * name = "";
        double start_time = 0;

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