exit 1
fi
-echo 'Making the conf dir /usr/share/nginx/conf...'
-mkdir -p /usr/share/nginx/conf
+# Check for nginx dir
+nginx=/usr/share/nginx
+if [ ! -d "$nginx" ]; then
+ (echo "nginx folder not found at $nginx.") 1>&2
+ exit 1
+fi;
+
+echo 'Making the conf dir $nginx/conf...'
+mkdir -p $nginx/conf
echo Generating the server private key...
-openssl genrsa -out /usr/share/nginx/conf/server.key 2048
+openssl genrsa -out $nginx/conf/server.key 2048
echo Generating the CSR...
-openssl req -new -key /usr/share/nginx/conf/server.key \
--out /usr/share/nginx/conf/server.csr \
+openssl req -new -key $nginx/conf/server.key -out $nginx/conf/server.csr \
-subj "/C=AU/ST=WA/L=Perth/O=UWA/OU=Mechatronics/CN=$1"
echo Signing the certificate...
-openssl x509 -req -days 3650 -in /usr/share/nginx/conf/server.csr \
--signkey /usr/share/nginx/conf/server.key \
--out /usr/share/nginx/conf/server.crt
+openssl x509 -req -days 3650 -in $nginx/conf/server.csr \
+ -signkey $nginx/conf/server.key -out $nginx/conf/server.crt
--- /dev/null
+#!/bin/bash
+
+# Check running as root
+if [ "$(whoami)" != "root" ]; then
+ (echo "Run $0 as root.") 1>&2
+ exit 1
+fi
+
+nconf=/usr/share/nginx/conf
+# Generate the ssl cert, if necessary
+if [ ! -d "$nconf" ]; then
+ echo "ssl cert not found at $nconf, generating..."
+ ./gen_ssl_cert.sh $1
+ if [[ $? != 0 ]] ; then
+ (echo "gen_ssl_cert failed, exiting.") 1>&2
+ exit 1
+ fi;
+fi
+
+nginx=/etc/nginx
+# Check for nginx
+if [ ! -d "$nginx" ]; then
+ (echo "Install nginx.") 1>&2
+ exit 1
+fi
+
+# Copy nginx configs
+echo
+echo "Copying nginx configs..."
+cp -v nginx/fastcgi_params ./nginx/mime.types $nginx/
+rm -fv $nginx/sites-enabled/*
+cp -v nginx/sites-enabled/mctxconfig $nginx/sites-enabled
+
+echo
+echo "Restarting nginx..."
+/etc/init.d/nginx restart
+
+echo
+echo "Note: Check the document root for the nginx config is set correctly."
+
+# Copy syslog configs
+rsyslog=/etc/rsyslog.d
+lrotate=/etc/logrotate.d
+if [ -d "$rsyslog" ]; then
+ echo
+ echo "Copying rsyslog configs..."
+ cp -v rsyslog.d/30-mctxserv.conf $rsyslog/
+
+ echo
+ echo "Restarting rsyslog..."
+ /etc/init.d/rsyslog restart
+else
+ echo
+ (echo "Could not find rsyslog at $rsyslog. Skipping.") 1>&2
+fi
+
+if [ -d "$lrotate" ]; then
+ echo
+ echo "Copying logrotate configs..."
+ cp -v logrotate.d/mctxserv.conf $lrotate/
+else
+ echo
+ (echo "Could not find logrotate at $lrotate. Skipping.") 1>&2
+fi
+
+
FCGI_JSONPair("description", "MCTX3420 Server API (2013)");
FCGI_JSONPair("build_date", __DATE__ " " __TIME__);
FCGI_JSONLong("api_version", API_VERSION);
+ FCGI_JSONBool("logged_in", FCGI_HasControl(context, getenv("COOKIE_STRING")));
+ FCGI_JSONPair("friendly_name", "");
//Sensor and actuator information
if (ident_sensors) {
* the system at any one time. The key can be forcibly generated, revoking
* any previous control keys. To be used in conjunction with HTTP
* basic authentication.
- * This function will generate a JSON response that indicates success/failure.
* @param context The context to work in
* @param force Whether to force key generation or not.
- */
-void FCGI_LockControl(FCGIContext *context, bool force) {
+ * @return true on success, false otherwise (eg someone else already in control)
+ */
+bool FCGI_LockControl(FCGIContext *context, bool force) {
time_t now = time(NULL);
bool expired = now - context->control_timestamp > CONTROL_TIMEOUT;
-
+
if (force || !*(context->control_key) || expired)
{
SHA_CTX sha1ctx;
for (i = 0; i < 20; i++)
sprintf(context->control_key + i * 2, "%02x", sha1[i]);
snprintf(context->control_ip, 16, "%s", getenv("REMOTE_ADDR"));
+ return true;
}
+ return false;
}
/**
bool FCGI_HasControl(FCGIContext *context, const char *key) {
time_t now = time(NULL);
int result = (now - context->control_timestamp) <= CONTROL_TIMEOUT &&
- key != NULL && !strcmp(context->control_key, key);
+ key != NULL && context->control_key[0] != '\0' &&
+ !strcmp(context->control_key, key);
if (result) {
context->control_timestamp = now; //Update the control_timestamp
}
*/
void FCGI_ReleaseControl(FCGIContext *context) {
*(context->control_key) = 0;
- FCGI_BeginJSON(context, STATUS_OK);
- FCGI_EndJSON();
return;
}
FCGI_JSONPair("control_state", Control_GetModeName());
}
+/**
+ * Generic accept response in JSON format.
+ * @param context The context to work in
+ * @param description A short description.
+ * @param cookie Optional. If given, the cookie field is set to that value.
+ */
+void FCGI_AcceptJSON(FCGIContext *context, const char *description, const char *cookie)
+{
+ printf("Content-type: application/json; charset=utf-8\r\n");
+ if (cookie) {
+ printf("Set-Cookie: %s\r\n", cookie);
+ }
+ printf("\r\n{\r\n");
+ printf("\t\"module\" : \"%s\"", context->current_module);
+ FCGI_JSONLong("status", STATUS_OK);
+ FCGI_JSONPair("description", description);
+ FCGI_EndJSON();
+}
+
/**
* Adds a key/value pair to a JSON response. The response must have already
* been initiated by FCGI_BeginJSON. Special characters are not escaped.
while (FCGI_Accept() >= 0) {
ModuleHandler module_handler = NULL;
- char module[BUFSIZ], params[BUFSIZ], cookie[BUFSIZ];
+ char module[BUFSIZ], params[BUFSIZ];
+ //Don't need to copy if we're not modifying string contents
+ const char *cookie = getenv("COOKIE_STRING");
//strncpy doesn't zero-truncate properly
snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
- snprintf(cookie, BUFSIZ, "%s", getenv("COOKIE_STRING"));
Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params);
Log(LOGDEBUG, "Cookie: %s", cookie);
-
//Remove trailing slashes (if present) from module query
size_t lastchar = strlen(module) - 1;
context.current_module = module;
context.response_number++;
-
-
if (module_handler)
{
- if (module_handler != Login_Handler)
+ if (module_handler != Login_Handler && module_handler != IdentifyHandler)
{
if (cookie[0] == '\0')
{
typedef void (*ModuleHandler) (FCGIContext *context, char *params);
-extern void FCGI_LockControl(FCGIContext *context, bool force);
+extern bool FCGI_LockControl(FCGIContext *context, bool force);
extern void FCGI_ReleaseControl(FCGIContext *context);
extern bool FCGI_HasControl(FCGIContext *context, const char *key);
extern char *FCGI_KeyPair(char *in, const char **key, const char **value);
extern bool FCGI_ParseRequest(FCGIContext *context, char *params, FCGIValue values[], size_t count);
extern void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code);
+extern void FCGI_AcceptJSON(FCGIContext *context, const char *description, const char *cookie);
extern void FCGI_JSONPair(const char *key, const char *value);
extern void FCGI_JSONLong(const char *key, long value);
extern void FCGI_JSONDouble(const char *key, double value);
* @param params - Parameter string, UNUSED
*/
void Logout_Handler(FCGIContext * context, char * params)
-{
+{
FCGI_ReleaseControl(context);
+ FCGI_AcceptJSON(context, "Logged out", "0");
}
*/
void Login_Handler(FCGIContext * context, char * params)
{
-
- if (context->control_key[0] != '\0')
- {
- FCGI_RejectJSON(context, "Already logged in.");
- return;
- }
-
- char * user = ""; // The username supplied through CGI
- char * pass = ""; // The password supplied through CGI
- //TODO: Make sure these are passed through HTTPS, *not* HTTP .... otherwise people can eavesdrop on the passwords
+ char * user; // The username supplied through CGI
+ char * pass; // The password supplied through CGI
FCGIValue values[] = {
{"user", &user, FCGI_REQUIRED(FCGI_STRING_T)},
return;
}
-
- // Trim leading whitespace (the BUFSIZ check is to make sure incorrectly terminated strings don't cause an infinite loop)
+ // Trim leading whitespace
int i = 0;
- for (i = 0; i < BUFSIZ && isspace(user[0]) && user[0] != '\0'; ++i,++user);
+ for (i = 0; isspace(user[0]) && user[0] != '\0'; ++i, ++user);
// Truncate string at first non alphanumeric character
- for (i = 0; i < BUFSIZ && isalnum(user[i]) && user[i] != '\0'; ++i);
+ for (i = 0; isalnum(user[i]) && user[i] != '\0'; ++i);
user[i] = '\0';
-
-
bool authenticated = true;
if (!authenticated)
{
- FCGI_RejectJSON(context, "Authentication failure.");
- return;
+ FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Authentication failure.");
+ }
+ else
+ {
+ if (FCGI_LockControl(context, false))
+ {
+ // Give the user a cookie
+ FCGI_AcceptJSON(context, "Logged in", context->control_key);
+ }
+ else
+ {
+ FCGI_RejectJSON(context, "Someone else is already logged in");
+ }
}
-
- FCGI_LockControl(context, false);
-
- // Give the user a cookie
- FCGI_PrintRaw("Content-type: text\r\n");
- FCGI_PrintRaw("Set-Cookie: %s\r\n\r\n", context->control_key);
-
}
#!/bin/bash
-# Use this to quickly test run the server in valgrind
-#spawn-fcgi -p9005 -n ./valgrind.sh
-# Use this to run the server normally
-#./stream &
+
+# Check existence of program
+if [ ! -e "server" ]; then
+ (echo "Rebuild server.") 1>&2;
+ exit 1
+fi
+
if [[ "$(uname -m)" != *arm* ]]; then
echo Not running on the BBB
# Use this to quickly test run the server in valgrind
spawn-fcgi -p9005 -n ./valgrind.sh
# Use this to run the server normally
- #./stream &
+ #spawn-fcgi -p9005 -n ./server
exit 0
fi
exit 1
fi
-# Check existence of program
-if [ ! -e "server" ]; then
- (echo "Rebuild server.") 1>&2;
- exit 1
-fi
-
# Rotate the logs
echo Rotating the system logs
logrotate -f /etc/logrotate.d/mctxserv.conf
--- /dev/null
+<!DOCTYPE html>
+
+<html>
+ <head>
+ <Title> MCTX 3420 login</Title>
+ </head>
+
+ <body style>
+ <div id="window">
+ <div id= "container">
+ <h1><a href="http://www.uwa.edu.au/" target="_blank">
+ <img alt = "The University of Western Australia"
+ src="http://www.lms.uwa.edu.au/theme/image.php/uwa/theme/1378302164/uwacrest">
+ </a> </h1>
+ <div id="login-container">
+ <div id="login-section">
+ <form name="login-form" method="post" action="index.html" class="aui-login-form-container">
+ <h2> "log in"</h2>
+ <div id="action-messages">
+ </div>
+ <fieldset class="compact-form-feilds">
+ <div class="feild-group">
+ <label id="os_username"> Username </label>
+ <input type="text" name="os_username" id="os_username" class="text" data-focus="0">
+
+ </div>
+ <div class="feild-group"><label id="os-password-label" for="os_password"> "Password" </label>
+ <input type="password" name="os_password" id="os_password" class="password">
+ </div>
+ <div class="feild-group-form-buttons compact-form-buttons">
+ <input id="loginbutton" class="aui-button aui-style aui-button-primary" name="login" type="submit" value="Log In">
+
+ </div>
+ </fieldset>
+ </form>
+
+ </div>
+ </div>
+
+ </div>
+
+
+ </div>
+
+ </body>
+
+</html>
<link rel="stylesheet" type="text/css" href="static/style.css">
<link rel="stylesheet" type="text/css" href="static/nav-menu.css">
<script type="text/javascript">
+ runBeforeLoad();
$(document).ready(function () {
//$("#menu-container").populateNavbar();
$("#login").submit(function () {
return false;
});
+ $("#logout").click(function () {
+ $("#logout").logout();
+ });
+
$("#main_controls").submit(function () {
//Validate!
return false;
});
- //$("#cam1").setCamera();
- //$("#strain-graphs").setStrainGraphs();
+
$("#errorlog").setErrorLog();
});
</script>
<div id="logout-container">
<form action="#">
<div>
- <input type="button" name="logout" value="Logout">
+ <input type="button" id="logout" value="Logout">
</div>
</form>
</div>
<input type="submit" value="Submit">
</form>
</div>
-
- <!--Todo get rid of this and make a separate login page-->
- <div class="widget">
- <div class="title">Login</div>
- <div class="item">
- <form id="login" action="#">
- <table class="centre">
- <tr><td>Username</td><td><input name="username" type="text"></td></tr>
- <tr><td>Password</td><td><input name="pass" type="password"></td></tr>
- <tr>
- <td></td>
- <td>
- <input type="submit" value="Submit">
- <input type="checkbox" name="force"> Force
- </td>
- </tr>
- </table>
- </form>
- </div>
- </div>
</div>
<!-- End sidebar -->
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <title>MCTX3420 Web Interface</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <!--[if lte IE 8]>
+ <script language="javascript" type="text/javascript" src="static/excanvas.min.js"></script>
+ <![endif]-->
+ <script type="text/javascript" src="static/jquery-1.10.1.min.js"></script>
+ <script type="text/javascript" src="static/jquery.flot.min.js"></script>
+ <script type="text/javascript" src="static/base64.js"></script>
+ <script type="text/javascript" src="static/mctx.gui.js"></script>
+
+ <link rel="stylesheet" type="text/css" href="static/style.css">
+ <link rel="stylesheet" type="text/css" href="static/nav-menu.css">
+ <script type="text/javascript">
+ runBeforeLoad(true);
+
+ $(document).ready(function () {
+ $("#login").submit(function () {
+ $("#login").login();
+ return false;
+ });
+ });
+ </script>
+ </head>
+
+ <body>
+ <div id="header">
+ <div id="leftnav">
+ <a href="http://www.uwa.edu.au/" target="_blank">
+ <img alt = "The University of Western Australia"
+ src="static/uwacrest-text.png">
+ </a>
+ <span id="title">Exploding Cans</span>
+ </div>
+ <div id="rightnav">
+ <span id="date">
+ <script type="text/javascript">getDate();</script>
+ </span>
+ </div>
+ <div class="clear"></div>
+ </div>
+ <!-- End header -->
+
+ <div id="login-container">
+ <div class="widget">
+ <form id="login" action="#">
+ <p>
+ <label>
+ Username<br>
+ <input name="username" type="text">
+ </label>
+ </p>
+ <p>
+ <label>
+ Password<br>
+ <input name="pass" type="password">
+ </label>
+ </p>
+ <p style="float:left; margin:0;">
+ <a href="#">Forgotten password?</a>
+ </p>
+ <p style="float:right; margin:0;">
+ <input type="submit" value="Log In">
+ </p>
+ <p id="result">
+
+ </p>
+ </form>
+ </div>
+ </div>
+ </body>
+</html>
*/
mctx = {};
-mctx.api = location.protocol + "//" + location.host + "/api/";
+//Don't use this in the final version
+mctx.location = window.location.pathname;
+mctx.location = mctx.location.substring(0, mctx.location.lastIndexOf('/')) + "/";
+//mctx.location = location.protocol + "//" + location.host + "/";
+mctx.api = location.protocol + "//" + location.host + "/" + "api/";
mctx.expected_api_version = 0;
-mctx.key = undefined;
mctx.has_control = false;
+//mctx.debug = true;
-mctx.return_codes = {
+mctx.statusCodes = {
+ STATUS_OK : 1
+}
+
+mctx.statusCodesDescription = {
"1" : "Ok",
"-1" : "General error",
"-2" : "Unauthorized",
mctx.strain_gauges.ids = [0, 1, 2, 3];
mctx.strain_gauges.time_limit = 20;
+function debugLog (msg) {
+ if (typeof console === "undefined" || typeof console.log === "undefined") {
+ alert(msg);
+ } else {
+ console.log(msg);
+ }
+}
+
/**
* Writes the current date to wherever it's called.
*/
document.write((new Date()).toDateString());
}
+function runBeforeLoad(isLoginPage) {
+ $.ajax({
+ url : mctx.api + "identify"
+ }).done(function (data) {
+ if (mctx.debug) {
+ debugLog("Redirect disabled!");
+ } else if (data.logged_in && isLoginPage) {
+ window.location = mctx.location;
+ } else if (!data.logged_in && !isLoginPage) {
+ //Note: this only clears the nameless cookie
+ document.cookie = "";
+ window.location = mctx.location + "login.html";
+ } else {
+ mctx.friendlyName = data.friendly_name;
+ $("#content").css("display", "block");
+ }
+ }).fail(function (jqHXR) {
+ if (!isLoginPage) {
+ window.location = mctx.location + "login.html";
+ } else {
+ debugLog("Failed to ident server. Is API running?")
+ }
+ });
+}
+
/**
* Populates a submenu of the navigation bar
* @param {string} header The header
$.fn.login = function () {
var username = this.find("input[name='username']").val();
var password = this.find("input[name='pass']").val();
- var force = this.find("input[name='force']").is(":checked");
- var url = mctx.api + "control";
-
- var authFunc = function(xhr) {
- xhr.setRequestHeader("Authorization",
- "Basic " + base64.encode(username + ":" + password));
+ var out = this.find("#result");
+ var redirect = function () {
+ window.location.href = mctx.location;
};
-
+
+ out.removeAttr("class");
+ out.text("Logging in...");
+
$.ajax({
- url : url,
- data : {action : "lock", force : (force ? true : undefined)},
- beforeSend : authFunc
+ url : mctx.api + "bind",
+ data : {user: username, pass : password}
}).done(function (data) {
- mctx.key = data.key;
if (data.status < 0) {
- alert("no - " + data.description);
+ mctx.has_control = false;
+ out.attr("class", "fail");
+ out.text("Login failed: " + data.description);
} else {
+ //todo: error check
mctx.has_control = true;
- alert("yes - " + mctx.key);
+ out.attr("class", "pass");
+ out.text("Login ok!");
+ setTimeout(redirect, 800);
}
}).fail(function (jqXHR) {
- mctx.key = undefined;
mctx.has_control = false;
- alert("no");
+ out.attr("class", "fail");
+ out.text("Login request failed - connection issues.")
});
};
+$.fn.logout = function () {
+ $.ajax({
+ url : mctx.api + "unbind"
+ }).always(function () {
+ //Note: this only clears the nameless cookie
+ document.cookie = "";
+ window.location = mctx.location + "login.html";
+ });
+}
+
$.fn.setErrorLog = function () {
var url = mctx.api + "errorlog";
var outdiv = this;
margin: 1em auto;
}
+a {
+ color: #1188DD;
+}
+
+a:active {
+ color: #0066BB;
+}
+
+.pass {
+ color: #7AE309;
+ font-weight: bold;
+}
+
+.fail {
+ color: #E30909;
+ font-weight: bold
+}
+
div.centre {
text-align: center;
}
background-color: #E8E8E8;
}
-/* IE8 width bugfix */
+
input[type="text"], input[type="password"] {
- width: 100%;
+ width: 100%; /* IE8 width bugfix */
+ border-radius: 3px;
+ border: 1px solid #CCCCCC;
+ font-size: 16px;
+}
+
+input[type="text"]:focus, input[type="password"]:focus {
+ box-shadow: 0 0 2px #BBBBBB;
}
#header {
display: table;
}
+#header #leftnav > div, #header #leftnav > span {
+ display: table-cell;
+ vertical-align: middle;
+ padding-left: 1em;
+}
+
#header #leftnav a {
color: #2a2a2a;
}
-#header #title {
+#title {
font-size: 30px;
- display: table-cell;
- vertical-align: middle;
- padding-left: 1em;
}
#header #rightnav {
#content {
padding: 1em 2em;
+ /* display: none; Enable in non-debug mode*/
}
#sidebar{
- float: right;
+ float: left;
max-width: 26%;
}
#main {
overflow: auto;
- margin-right: 25%;
- padding-right: 2em;
+ padding-left: 2em;
min-width: 400px;
}
margin: 0.25em 0.25em 1.5em;
padding: 1em 1.25em;
box-shadow: 0 0 3px #CCCCCC;
+ border: 1px solid #CCCCCC;
transition: all 0.2s ease 0s;
overflow: auto;
}
max-width: 100%;
width: 100%;
height: 6em;
+}
+
+/** For login.html **/
+#login-container {
+ margin: 0 auto;
+ width: 400px;
+ padding: 1.8em 0 0;
+ color: #606060;
+ line-height: 1.8em;
+}
+
+#login-container form {
+ padding: 1em 2em;
+}
+
+#login-container input {
+ font-size: 24px;
+}
+
+#login-container label {
+ font-size: 14px;
+}
+
+#login-container input[type=submit] {
+ font-size: inherit;
+ padding: 0.4em 0.8em;
+}
+
+#login-container #result {
+ clear: both;
}
\ No newline at end of file