summaryrefslogtreecommitdiffstats
path: root/servers/fpga_programming_server_lin/src/fpga_progserver.cpp
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2014-01-13 05:10:18 -0600
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2014-01-13 05:10:18 -0600
commit87834696493fdc51c2375aff7f307d854766dd42 (patch)
treec122d72e897735e0c033829e786b37745320c4b8 /servers/fpga_programming_server_lin/src/fpga_progserver.cpp
parentc912b0f1d3994cde07d472e2c53f0375518a74df (diff)
downloadulab-87834696493fdc51c2375aff7f307d854766dd42.tar.gz
ulab-87834696493fdc51c2375aff7f307d854766dd42.zip
Fix incorrect file names
Diffstat (limited to 'servers/fpga_programming_server_lin/src/fpga_progserver.cpp')
-rw-r--r--servers/fpga_programming_server_lin/src/fpga_progserver.cpp383
1 files changed, 383 insertions, 0 deletions
diff --git a/servers/fpga_programming_server_lin/src/fpga_progserver.cpp b/servers/fpga_programming_server_lin/src/fpga_progserver.cpp
new file mode 100644
index 0000000..a0c2afe
--- /dev/null
+++ b/servers/fpga_programming_server_lin/src/fpga_progserver.cpp
@@ -0,0 +1,383 @@
+/*
+ * Remote Laboratory FPGA Server
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (c) 2012 Timothy Pearson
+ * Raptor Engineering
+ * http://www.raptorengineeringinc.com
+ */
+
+#include <stdio.h> /* perror() */
+#include <stdlib.h> /* atoi() */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h> /* read() */
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+
+#include <tqtimer.h>
+#include <tqfile.h>
+
+#include <tdelocale.h>
+
+#include "fpga_progserver.h"
+
+#define ABORT_SOCKET(s) s->close(); \
+ s->disconnect(); \
+ delete s; \
+ s = NULL;
+
+#define NETWORK_COMM_TIMEOUT_MS 5000
+
+/* exception handling */
+struct exit_exception {
+ int c;
+ exit_exception(int c):c(c) { }
+};
+
+enum connectionStates {
+ StateIdle = 0,
+ StateGetFileSize = 1,
+ StateGetFileContents = 2,
+ StateStartProgramming = 3,
+ StateCheckProgrammingStatus = 4,
+ StateProgammingFinished = 5
+};
+
+/*
+ The FPGASocket class provides a socket that is connected with a client.
+ For every client that connects to the server, the server creates a new
+ instance of this class.
+*/
+FPGASocket::FPGASocket(int sock, TQObject *parent, const char *name) :
+ TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_loopTimer(NULL), m_config(static_cast<FPGAServer*>(parent)->m_config), m_commandLoopState(StateIdle),
+ m_progpipe(NULL), m_progpipefd(-1), m_progErrorFlag(false), m_progDoneFlag(false)
+{
+
+ // Initialize timers
+ m_kerberosInitTimer = new TQTimer();
+ connect(m_kerberosInitTimer, SIGNAL(timeout()), this, SLOT(finishKerberosHandshake()));
+ m_servClientTimeout = new TQTimer();
+
+ setServiceName("ulab");
+
+ line = 0;
+ connect(this, SIGNAL(connectionClosed()), SLOT(connectionClosedHandler()));
+ connect(this, SIGNAL(connectionClosed()), parent, SLOT(remoteConnectionClosed()));
+ setSocket(sock);
+}
+
+FPGASocket::~FPGASocket() {
+ if (m_servClientTimeout) {
+ m_servClientTimeout->stop();
+ delete m_servClientTimeout;
+ m_servClientTimeout = NULL;
+ }
+ if (m_kerberosInitTimer) {
+ m_kerberosInitTimer->stop();
+ delete m_kerberosInitTimer;
+ m_kerberosInitTimer = NULL;
+ }
+ if (m_loopTimer) {
+ m_loopTimer->stop();
+ delete m_loopTimer;
+ m_loopTimer = NULL;
+ }
+}
+
+void FPGASocket::close() {
+ if (state() == TQSocket::Connected) {
+ TDEKerberosServerSocket::close();
+ connectionClosedHandler();
+ TQTimer::singleShot(0, parent(), SLOT(remoteConnectionClosed()));
+ }
+}
+
+void FPGASocket::connectionClosedHandler() {
+ printf("[DEBUG] Connection from %s closed\n\r", m_remoteHost.ascii());
+
+ if (m_criticalSection > 0) {
+ throw exit_exception(-1);
+ }
+}
+
+void FPGASocket::initiateKerberosHandshake() {
+ setUsingKerberos(true);
+ m_kerberosInitTimer->start(100, TRUE);
+}
+
+void FPGASocket::finishKerberosHandshake() {
+ if (kerberosStatus() == TDEKerberosServerSocket::KerberosInitializing) {
+ m_kerberosInitTimer->start(100, TRUE);
+ return;
+ }
+ if (kerberosStatus() == TDEKerberosServerSocket::KerberosInUse) {
+ m_config->setGroup("Security");
+ TQString masterUser = m_config->readEntry("masteruser");
+ TQString masterRealm = m_config->readEntry("masterrealm");
+ if (masterRealm == "") {
+ masterRealm = "(NULL)";
+ }
+ if ((m_authenticatedUserName != masterUser) || (m_authenticatedRealmName != masterRealm)) {
+ printf("[DEBUG] Connection from %s closed due to authentication failure (attempted connection as user %s@%s)\n\r", m_remoteHost.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii());
+ close();
+ return;
+ }
+
+ setDataTimeout(NETWORK_COMM_TIMEOUT_MS);
+
+ TQDataStream ds(this);
+ ds.setPrintableData(true);
+ ds << TQString("OK");
+ writeEndOfFrame();
+
+ enterCommandLoop();
+ return;
+ }
+ else {
+ printf("[DEBUG] Connection from %s closed due to Kerberos failure\n\r", m_remoteHost.ascii()); fflush(stdout);
+ close();
+ return;
+ }
+}
+
+void FPGASocket::commandLoop() {
+ bool transferred_data;
+
+ m_criticalSection++;
+ try {
+ transferred_data = false;
+ if (state() == TQSocket::Connected) {
+ if ((m_commandLoopState == StateIdle) || (m_commandLoopState == StateStartProgramming) || (m_commandLoopState == StateCheckProgrammingStatus) || (m_commandLoopState == StateProgammingFinished)) {
+ // Certain commands can come in at any time during some operations
+ if (canReadLine()) {
+ processPendingData();
+ }
+ if (canReadFrame()) {
+ TQDataStream ds(this);
+ ds.setPrintableData(true);
+ TQString command;
+ ds >> command;
+ clearFrameTail();
+ if (command == "STATUS") {
+ if (m_logMessages != "") {
+ ds << TQString("LOGMESSAGES");
+ writeEndOfFrame();
+ ds << m_logMessages;
+ writeEndOfFrame();
+ m_logMessages = "";
+ }
+ else if (m_progErrorFlag) {
+ ds << TQString("ERROR");
+ m_progErrorFlag = false;
+ writeEndOfFrame();
+ }
+ else if (m_progDoneFlag) {
+ ds << TQString("DONE");
+ ds << m_progRetCode;
+ m_progDoneFlag = false;
+ writeEndOfFrame();
+ }
+ else if (m_commandLoopState == StateIdle) {
+ ds << TQString("IDLE");
+ writeEndOfFrame();
+ }
+ else if ((m_commandLoopState == StateStartProgramming) || (m_commandLoopState == StateCheckProgrammingStatus) || (m_commandLoopState == StateProgammingFinished)) {
+ ds << TQString("PROGRAMMING");
+ writeEndOfFrame();
+ }
+ else {
+ ds << TQString("UNKNOWN");
+ writeEndOfFrame();
+ }
+ }
+ else if (m_commandLoopState == StateIdle) {
+ if (command == "FILE") {
+ m_commandLoopState = StateGetFileSize;
+ }
+ else if (command == "PROGRAM") {
+ m_commandLoopState = StateStartProgramming;
+ }
+ else {
+ printf("[WARNING] Received unknown command '%s'\n\r", command.ascii());
+ }
+ }
+ transferred_data = true;
+ }
+ }
+ if (m_commandLoopState == StateGetFileSize) {
+ if (canReadLine()) {
+ processPendingData();
+ }
+ if (canReadFrame()) {
+ TQDataStream ds(this);
+ ds.setPrintableData(true);
+ ds >> m_programmingFileSize;
+ clearFrameTail();
+ m_servClientTimeout->start(NETWORK_COMM_TIMEOUT_MS, TRUE);
+ m_commandLoopState = StateGetFileContents;
+ }
+ }
+ else if (m_commandLoopState == StateGetFileContents) {
+ if (canReadLine()) {
+ m_servClientTimeout->start(NETWORK_COMM_TIMEOUT_MS, TRUE);
+ processPendingData();
+ }
+ if (bytesAvailable() >= m_programmingFileSize) {
+ TQByteArray fileContents(m_programmingFileSize);
+ readBlock(fileContents.data(), fileContents.size());
+ m_programmingFileName = TQString("/tmp/%1#%2.dat").arg(m_remoteHost).arg(port());
+ TQFile outputFile(m_programmingFileName);
+ if (outputFile.open(IO_ReadWrite)) {
+ outputFile.writeBlock(fileContents);
+ outputFile.flush();
+ outputFile.close();
+ }
+ transferred_data = true;
+ m_commandLoopState = StateIdle;
+ }
+ else {
+ if (!m_servClientTimeout->isActive()) {
+ m_progErrorFlag = true;
+ transferred_data = true;
+ m_commandLoopState = StateIdle;
+ }
+ }
+ }
+ else if (m_commandLoopState == StateStartProgramming) {
+ // Start programming!
+
+ // Open programming process
+ m_config->setGroup("Programming");
+ TQString programmingScript = m_config->readEntry("script");
+ programmingScript.replace("%f", m_programmingFileName);
+ if (!programmingScript.contains("2>&1")) {
+ programmingScript.append(" 2>&1");
+ }
+ if ((m_progpipe = popen(programmingScript.ascii(), "r")) == NULL) {
+ m_logMessages.append(TQString("The system was unable to execute '%1'\nPlease contact your system administrator with this information").arg(programmingScript));
+ m_progErrorFlag = true;
+ transferred_data = true;
+ m_commandLoopState = StateIdle;
+ }
+ else {
+ m_progpipefd = fileno(m_progpipe);
+ fcntl(m_progpipefd, F_SETFL, O_NONBLOCK);
+ }
+ m_commandLoopState = StateCheckProgrammingStatus;
+ }
+ else if (m_commandLoopState == StateCheckProgrammingStatus) {
+ // Check programming status
+ TQCString buf;
+ buf.resize(8192);
+ ssize_t r = read(m_progpipefd, buf.data(), buf.size());
+ if ((r == -1) && (errno == EAGAIN)) {
+ // No data available yet
+ }
+ else if (r > 0) {
+ // Data was received
+ buf.data()[r] = 0;
+ m_logMessages.append(buf);
+ }
+ else {
+ // Process terminated
+ m_commandLoopState = StateProgammingFinished;
+ }
+ }
+ else if (m_commandLoopState == StateProgammingFinished) {
+ // Programming process terminated; get exit code and clean up
+ if (m_progpipe) {
+ m_progRetCode = pclose(m_progpipe);
+ }
+ else {
+ m_progRetCode = -1;
+ }
+ m_progpipe = NULL;
+ m_progpipefd = -1;
+
+ m_progDoneFlag = true;
+ m_commandLoopState = StateIdle;
+ }
+ }
+ m_criticalSection--;
+ if (transferred_data) {
+ if (m_loopTimer) m_loopTimer->start(0, TRUE);
+ }
+ else {
+ if (m_loopTimer) m_loopTimer->start(100, TRUE);
+ }
+ return;
+ }
+ catch (...) {
+ m_criticalSection--;
+ return;
+ }
+}
+
+int FPGASocket::enterCommandLoop() {
+ m_commandLoopState = StateIdle;
+ if (!m_loopTimer) {
+ m_loopTimer = new TQTimer();
+ connect(m_loopTimer, SIGNAL(timeout()), this, SLOT(commandLoop()));
+ }
+ if (m_loopTimer) m_loopTimer->start(0, TRUE);
+ return 0;
+}
+
+/*
+ The FPGAServer class handles new connections to the server. For every
+ client that connects, it creates a new FPGASocket -- that instance is now
+ responsible for the communication with that client.
+*/
+FPGAServer::FPGAServer(TQObject* parent, int port, KSimpleConfig* config) :
+ TQServerSocket( port, 1, parent ), m_config(config), m_numberOfConnections(0) {
+
+ if ( !ok() ) {
+ printf("[ERROR] Failed to bind to port %d\n\r", port);
+ exit(1);
+ }
+
+ printf("[INFO] Server started on port %d\n\r", port); fflush(stdout);
+}
+
+FPGAServer::~FPGAServer() {
+ //
+}
+
+void FPGAServer::newConnection(int socket) {
+ FPGASocket *s = new FPGASocket(socket, this);
+ s->m_remoteHost = s->peerAddress().toString();
+ printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii());
+ if (m_numberOfConnections > 0) {
+ printf("[DEBUG] Connection from %s closed due to multiple access attempt\n\r", s->m_remoteHost.ascii());
+ ABORT_SOCKET(s)
+ return;
+ }
+ connect(s, SIGNAL(connectionClosed()), s, SLOT(deleteLater()));
+ s->initiateKerberosHandshake();
+ emit newConnect(s);
+}
+
+void FPGAServer::remoteConnectionClosed() {
+ m_numberOfConnections--;
+}