1
0
mirror of https://github.com/binary-kitchen/doorlockd synced 2024-12-22 10:24:26 +01:00

Added comments

This commit is contained in:
Ralf Ramsauer 2015-05-21 13:35:30 +02:00
parent 13b392fc8b
commit e6cba90b37
9 changed files with 148 additions and 55 deletions

View File

@ -15,7 +15,6 @@
#define DEFAULT_LDAP_SERVER "ldaps://ldap.binary.kitchen" #define DEFAULT_LDAP_SERVER "ldaps://ldap.binary.kitchen"
#define DEFAULT_BINDDN "cn=%s,ou=Users,dc=binary-kitchen,dc=de" #define DEFAULT_BINDDN "cn=%s,ou=Users,dc=binary-kitchen,dc=de"
#define DEFAULT_LOG_FILE "/var/log/doorlockd.log" #define DEFAULT_LOG_FILE "/var/log/doorlockd.log"
#define DEFAULT_ALLOWED_IP_PREFIX "172.23.3."
#define DEFAULT_PID_FILE "/var/run/doorlockd.pid" #define DEFAULT_PID_FILE "/var/run/doorlockd.pid"
#endif #endif

View File

@ -3,6 +3,11 @@
#include <string> #include <string>
// Daemonizes the process if daemonize is true.
// If daemonize is true, it will write the new PID to the file "pidFile"
//
// This function will also redirect stdin, out and err to the
// specified files
void daemonize(const bool daemonize, void daemonize(const bool daemonize,
const std::string &dir, const std::string &dir,
const std::string &stdinfile, const std::string &stdinfile,

View File

@ -13,8 +13,8 @@ Door::Door() :
{ {
_l(LogLevel::info, "Initializing Raspberry Pi GPIOs"); _l(LogLevel::info, "Initializing Raspberry Pi GPIOs");
wiringPiSetup(); wiringPiSetup();
pinMode(_LOCKPIN, OUTPUT); pinMode(_HEARTBEATPIN, OUTPUT);
pinMode(_SCHNAPPER, OUTPUT); pinMode(_SCHNAPPERPIN, OUTPUT);
lock(); lock();
} }
@ -31,49 +31,73 @@ Door &Door::get()
void Door::lock() void Door::lock()
{ {
digitalWrite(_SCHNAPPER, HIGH); digitalWrite(_SCHNAPPERPIN, HIGH);
_l(LogLevel::info, "Door closed"); _l(LogLevel::info, "Door closed");
if (_open == true)
if (_state == State::Unlocked)
{ {
_open = false; // Stop the Heartbeat Thread
_state = State::Locked;
_heartbeat.join(); _heartbeat.join();
} }
// Turn off all lights
system("wget -O /dev/null --timeout 3 \"http://homer.binary.kitchen:8080/set?color=000000\" > /dev/null 2>&1"); system("wget -O /dev/null --timeout 3 \"http://homer.binary.kitchen:8080/set?color=000000\" > /dev/null 2>&1");
} }
void Door::unlock() void Door::unlock()
{ {
// In any case, klacker the schnapper
_schnapper = true; _schnapper = true;
if (_open == true) // If heartbeat is already running, return
if (_state == State::Unlocked)
{ {
return; return;
} }
_open = true; // If not, first set state to unlocked
_state = State::Unlocked;
// Start the Heartbeat Thread
_heartbeat = std::thread([this] () { _heartbeat = std::thread([this] () {
// One "beat" is one complete cycle of the heartbeat clock
auto beat = [] () { auto beat = [] () {
digitalWrite(_LOCKPIN, HIGH); digitalWrite(_HEARTBEATPIN, HIGH);
usleep(10000); usleep(10000);
digitalWrite(_LOCKPIN, LOW); digitalWrite(_HEARTBEATPIN, LOW);
usleep(10000); usleep(10000);
}; };
digitalWrite(_SCHNAPPER, HIGH); // The default of the Schnapperpin: always high
while (_open) { digitalWrite(_SCHNAPPERPIN, HIGH);
// Heartbeat while the state is unlocked
while (_state == State::Unlocked) {
// In case of schnapper, send 0x55 resp. 0xaa to the schnapperpin
if (_schnapper == true) if (_schnapper == true)
{ {
for (int i = 0; i < 32 ; i++) for (int i = 0; i < 32 ; i++)
{ {
digitalWrite(_SCHNAPPER, LOW); // Set '0'
digitalWrite(_SCHNAPPERPIN, LOW);
// cycle and send
beat(); beat();
digitalWrite(_SCHNAPPER, HIGH); // Set '1'
digitalWrite(_SCHNAPPERPIN, HIGH);
// cycle and send
beat(); beat();
} }
digitalWrite(_SCHNAPPER, HIGH);
// Reset schnapperpin
digitalWrite(_SCHNAPPERPIN, HIGH);
// and deactivate schnapper for the next round
_schnapper = false; _schnapper = false;
} }
// Heartbeat
beat(); beat();
} }
}); });

View File

@ -6,29 +6,52 @@
#include "logger.h" #include "logger.h"
/*
* The Door class.
*
* This class exists as a singleton as only one door and hence one object may exist.
* Available via the get() method.
*
* This class is responsible for opening and closing the door by sending
* the heartbeat to the AVR board via Raspberry Pi GPIOs
*
* Whenever the unlock() is called, this class also sends a 0x55 resp. 0xaa
* to the AVR in order to klacker the schnapper.
*/
class Door { class Door {
public: public:
static Door &get();
// Returns the singleton
static Door &get();
~Door(); ~Door();
enum class State {Locked, Unlocked};
// Lock the door
void lock(); void lock();
// Unlock the door
void unlock(); void unlock();
private: private:
Door(); Door();
// used for logging
const Logger &_l; const Logger &_l;
bool _open = { false }; // Indicates the internal state: Door is open or locked
State _state = { Door::State::Locked };
// A Heartbeat thread is started when the door is unlocked
std::thread _heartbeat = { }; std::thread _heartbeat = { };
// Read by the Heartbeat thread if it should klacker the schnapper or not
bool _schnapper = { false }; bool _schnapper = { false };
static constexpr int _LOCKPIN = 10; // WiringPi GPIO Pins
static constexpr int _SCHNAPPER = 7; static constexpr int _HEARTBEATPIN = 10;
static constexpr int _SCHNAPPERPIN = 7;
}; };
#endif #endif

View File

@ -16,7 +16,8 @@ using namespace std;
Epaper::Epaper() : Epaper::Epaper() :
_logger(Logger::get()) _logger(Logger::get())
{ {
memset(_prevImg, 0xFF, ARRAY_SIZE); memset(_prevImg, 0xFF, _ARRAY_SIZE);
// Initialize Epaper library
bsp_init(); bsp_init();
} }
@ -32,12 +33,12 @@ Epaper &Epaper::get()
void Epaper::draw(const string &uri) void Epaper::draw(const string &uri)
{ {
unsigned char buffer[ARRAY_SIZE]; unsigned char buffer[_ARRAY_SIZE];
snprintf((char*)buffer, ARRAY_SIZE, "qrencode -l M -d 100 -s 5 \"%s\" -o /tmp/qr.png", uri.c_str()); snprintf((char*)buffer, _ARRAY_SIZE, "qrencode -l M -d 100 -s 5 \"%s\" -o /tmp/qr.png", uri.c_str());
system((char*)buffer); system((char*)buffer);
FILE* f = popen("composite -geometry +90+0 /tmp/qr.png /usr/local/share/doorlockd/template.png -colorspace gray -depth 1 gray:-", "r"); FILE* f = popen("composite -geometry +90+0 /tmp/qr.png /usr/local/share/doorlockd/template.png -colorspace gray -depth 1 gray:-", "r");
int i = fread(buffer, ARRAY_SIZE, 1, f); int i = fread(buffer, _ARRAY_SIZE, 1, f);
if (i != 1) if (i != 1)
{ {
_logger(LogLevel::error, "Image format error"); _logger(LogLevel::error, "Image format error");
@ -47,5 +48,5 @@ void Epaper::draw(const string &uri)
pclose(f); pclose(f);
epd_DisplayImg(EPDType_270, buffer, _prevImg); epd_DisplayImg(EPDType_270, buffer, _prevImg);
memcpy(_prevImg, buffer, ARRAY_SIZE); memcpy(_prevImg, buffer, _ARRAY_SIZE);
} }

View File

@ -6,24 +6,35 @@
#include "logger.h" #include "logger.h"
/*
* The "Epaper" class.
*
* Wrapper for the epaper third Party library.
*
* Exists as singleton, as only one display may exist.
*/
class Epaper { class Epaper {
public: public:
constexpr static int HEIGHT = 176; // In Pixel
constexpr static int WIDTH = 33; // In Byte
constexpr static int ARRAY_SIZE = HEIGHT * WIDTH;
static Epaper &get(); static Epaper &get();
~Epaper(); ~Epaper();
// This function will draw template.png to the display and
// convert the uri to a QR-Code and paste it on top of template.png
// using imagemagick
void draw(const std::string &uri); void draw(const std::string &uri);
private: private:
constexpr static int _HEIGHT = 176; // In Pixel
constexpr static int _WIDTH = 33; // In Byte
constexpr static int _ARRAY_SIZE = _HEIGHT * _WIDTH;
Epaper(); Epaper();
uint8_t _prevImg[ARRAY_SIZE]; // The old image is needed when updating the Epaper display
// It informs the display which pixels have to be flipped
uint8_t _prevImg[_ARRAY_SIZE];
const Logger &_logger; const Logger &_logger;
}; };

View File

@ -16,16 +16,14 @@ using namespace std;
Logic::Logic(const chrono::seconds tokenTimeout, Logic::Logic(const chrono::seconds tokenTimeout,
const string &ldapServer, const string &ldapServer,
const string &bindDN, const string &bindDN,
const string &webPrefix, const string &webPrefix) :
const string &allowedIpPrefix) :
_logger(Logger::get()), _logger(Logger::get()),
_door(Door::get()), _door(Door::get()),
_epaper(Epaper::get()), _epaper(Epaper::get()),
_tokenTimeout(tokenTimeout), _tokenTimeout(tokenTimeout),
_ldapServer(ldapServer), _ldapServer(ldapServer),
_bindDN(bindDN), _bindDN(bindDN),
_webPrefix(webPrefix), _webPrefix(webPrefix)
_allowedIpPrefix(allowedIpPrefix)
{ {
srand(time(NULL)); srand(time(NULL));
_createNewToken(false); _createNewToken(false);
@ -34,7 +32,7 @@ Logic::Logic(const chrono::seconds tokenTimeout,
while (_run) while (_run)
{ {
unique_lock<mutex> l(_mutex); unique_lock<mutex> l(_mutex);
_c.wait_for(l, _tokenTimeout); _tokenCondition.wait_for(l, _tokenTimeout);
if (_run == false) if (_run == false)
{ {
break; break;
@ -48,7 +46,7 @@ Logic::Logic(const chrono::seconds tokenTimeout,
Logic::~Logic() Logic::~Logic()
{ {
_run = false; _run = false;
_c.notify_one(); _tokenCondition.notify_one();
_tokenUpdater.join(); _tokenUpdater.join();
} }
@ -119,14 +117,14 @@ out:
Logic::Response Logic::_lock() Logic::Response Logic::_lock()
{ {
if (_state == LOCKED) if (_state == Door::State::Locked)
{ {
_logger(LogLevel::warning, "Unable to lock: already closed"); _logger(LogLevel::warning, "Unable to lock: already closed");
return AlreadyLocked; return AlreadyLocked;
} }
_door.lock(); _door.lock();
_state = LOCKED; _state = Door::State::Locked;
_createNewToken(false); _createNewToken(false);
return Success; return Success;
@ -137,12 +135,12 @@ Logic::Response Logic::_unlock()
_door.unlock(); _door.unlock();
_createNewToken(false); _createNewToken(false);
if (_state == UNLOCKED) if (_state == Door::State::Unlocked)
{ {
_logger(LogLevel::warning, "Unable to unlock: already unlocked"); _logger(LogLevel::warning, "Unable to unlock: already unlocked");
return AlreadyUnlocked; return AlreadyUnlocked;
} else { } else {
_state = UNLOCKED; _state = Door::State::Unlocked;
} }
return Success; return Success;

View File

@ -12,6 +12,12 @@
#include "door.h" #include "door.h"
#include "logger.h" #include "logger.h"
/* The "Logic" class
*
* This class is initilized by all settings.
*
* It parses incoming JSON-Requests and returns the Response Code.
*/
class Logic class Logic
{ {
public: public:
@ -19,8 +25,7 @@ public:
Logic(const std::chrono::seconds tokenTimeout, Logic(const std::chrono::seconds tokenTimeout,
const std::string &ldapServer, const std::string &ldapServer,
const std::string &bindDN, const std::string &bindDN,
const std::string &webPrefix, const std::string &webPrefix);
const std::string &allowedIpPrefix);
~Logic(); ~Logic();
enum Response { enum Response {
@ -37,40 +42,63 @@ public:
LDAPInit, // Ldap initialization failed LDAPInit, // Ldap initialization failed
}; };
// Parse incoming JSON Requests
Response parseRequest(const std::string &str); Response parseRequest(const std::string &str);
private: private:
// Internal lock wrapper
Response _lock(); Response _lock();
// Internal unlock wrapper
Response _unlock(); Response _unlock();
// Checks if the incoming token is valid
bool _checkToken(const std::string &token); bool _checkToken(const std::string &token);
// Checks if incoming credentials against LDAP
Response _checkLDAP(const std::string &user, Response _checkLDAP(const std::string &user,
const std::string &password); const std::string &password);
// Creates a new random token and draws it on the epaper.
// stillValid indicates whether the old (previous) token is still valid
void _createNewToken(const bool stillValid); void _createNewToken(const bool stillValid);
const Logger &_logger; const Logger &_logger;
// Door reference
Door &_door; Door &_door;
// Epaper reference
Epaper &_epaper; Epaper &_epaper;
// Tokens are 64-bit hexadecimal values
using Token = uint64_t; using Token = uint64_t;
// The current token
Token _curToken = { 0x0000000000000000 }; Token _curToken = { 0x0000000000000000 };
bool _prevValid = { false }; // The previous token
Token _prevToken = { 0x0000000000000000 }; Token _prevToken = { 0x0000000000000000 };
// Indicates whether the previous token is valid
bool _prevValid = { false };
// Tokens are refreshed all tokenTimout seconds
const std::chrono::seconds _tokenTimeout; const std::chrono::seconds _tokenTimeout;
const std::string _ldapServer; // Thread for asynchronosly updating tokens
const std::string _bindDN;
const std::string _webPrefix;
const std::string _allowedIpPrefix;
std::thread _tokenUpdater = {}; std::thread _tokenUpdater = {};
std::condition_variable _c = {}; // Thread can be force-triggered for updates using the condition variable
std::mutex _mutex = {}; std::condition_variable _tokenCondition = {};
// stop indicator for the thread
bool _run = true; bool _run = true;
// Token mutex
std::mutex _mutex = {};
enum {LOCKED, UNLOCKED} _state = { LOCKED }; // The URI of the ldap server
const std::string _ldapServer;
// LDAP bindDN
const std::string _bindDN;
// Prefix of the website
const std::string _webPrefix;
Door::State _state = { Door::State::Locked };
}; };
#endif #endif

View File

@ -30,6 +30,9 @@ void signal_handler(int signum)
logic.reset(); logic.reset();
} }
/*
* Session class handles asynchronosly handles one incoming TCP session
*/
class session class session
: public std::enable_shared_from_this<session> : public std::enable_shared_from_this<session>
{ {
@ -63,6 +66,9 @@ private:
char _data[_maxLen]; char _data[_maxLen];
}; };
/*
* The TCP server
*/
class server class server
{ {
@ -105,13 +111,13 @@ int main(int argc, char** argv)
string ldapServer; string ldapServer;
string bindDN; string bindDN;
string lockPagePrefix; string lockPagePrefix;
string allowedIpPrefix;
string logfile; string logfile;
string pidFile; string pidFile;
bool foreground = false; bool foreground = false;
l(LogLevel::notice, "Starting doorlockd"); l(LogLevel::notice, "Starting doorlockd");
// Load SPI and I2C modules
system("/usr/bin/gpio load spi"); system("/usr/bin/gpio load spi");
system("/usr/bin/gpio load i2c"); system("/usr/bin/gpio load i2c");
@ -125,7 +131,6 @@ int main(int argc, char** argv)
("ldap,s", po::value<string>(&ldapServer)->default_value(DEFAULT_LDAP_SERVER), "Ldap Server") ("ldap,s", po::value<string>(&ldapServer)->default_value(DEFAULT_LDAP_SERVER), "Ldap Server")
("bidndn,b", po::value<string>(&bindDN)->default_value(DEFAULT_BINDDN), "Bind DN, %s means username") ("bidndn,b", po::value<string>(&bindDN)->default_value(DEFAULT_BINDDN), "Bind DN, %s means username")
("web,w", po::value<string>(&lockPagePrefix)->default_value(DEFAULT_WEB_PREFIX), "Prefix of the webpage") ("web,w", po::value<string>(&lockPagePrefix)->default_value(DEFAULT_WEB_PREFIX), "Prefix of the webpage")
("ip,i", po::value<string>(&allowedIpPrefix)->default_value(DEFAULT_ALLOWED_IP_PREFIX), "Default allowed IP Prefix")
("foreground,f", po::bool_switch(&foreground)->default_value(false), "Run in foreground") ("foreground,f", po::bool_switch(&foreground)->default_value(false), "Run in foreground")
("logfile,l", po::value<string>(&logfile)->default_value(DEFAULT_LOG_FILE), "Log file") ("logfile,l", po::value<string>(&logfile)->default_value(DEFAULT_LOG_FILE), "Log file")
("pid,z", po::value<string>(&pidFile)->default_value(DEFAULT_PID_FILE), "PID file"); ("pid,z", po::value<string>(&pidFile)->default_value(DEFAULT_PID_FILE), "PID file");
@ -166,8 +171,7 @@ int main(int argc, char** argv)
logic = unique_ptr<Logic>(new Logic(tokenTimeout, logic = unique_ptr<Logic>(new Logic(tokenTimeout,
ldapServer, ldapServer,
bindDN, bindDN,
lockPagePrefix, lockPagePrefix));
allowedIpPrefix));
try { try {
server s(io_service, port); server s(io_service, port);