// Files containing GPIO and PWM definitions
#include "bbb_pin.h"
-
-
-
/** Number of actuators **/
int g_num_actuators = 0;
{
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);
*/
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);
}
extern bool PathExists(const char * path);
+extern bool DirExists(const char * path);
#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");
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..
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?)
{"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
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);
+ }
}
/**
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.";
} 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);
*/
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))
// 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 ||
// 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));
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; // :-)
}
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
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;
#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;
/**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?**/
*/
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
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] != '-')
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;
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')
{
/** 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 **/
{
// 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);
struct timeval now;
gettimeofday(&now, NULL);
double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime());
-
int id = 0;
const char * name = "";
double start_time = 0;