mirror of
https://github.com/binary-kitchen/doorlockd
synced 2024-12-22 10:24:26 +01:00
Added comments
This commit is contained in:
parent
13b392fc8b
commit
e6cba90b37
@ -15,7 +15,6 @@
|
||||
#define DEFAULT_LDAP_SERVER "ldaps://ldap.binary.kitchen"
|
||||
#define DEFAULT_BINDDN "cn=%s,ou=Users,dc=binary-kitchen,dc=de"
|
||||
#define DEFAULT_LOG_FILE "/var/log/doorlockd.log"
|
||||
#define DEFAULT_ALLOWED_IP_PREFIX "172.23.3."
|
||||
#define DEFAULT_PID_FILE "/var/run/doorlockd.pid"
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,11 @@
|
||||
|
||||
#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,
|
||||
const std::string &dir,
|
||||
const std::string &stdinfile,
|
||||
|
@ -13,8 +13,8 @@ Door::Door() :
|
||||
{
|
||||
_l(LogLevel::info, "Initializing Raspberry Pi GPIOs");
|
||||
wiringPiSetup();
|
||||
pinMode(_LOCKPIN, OUTPUT);
|
||||
pinMode(_SCHNAPPER, OUTPUT);
|
||||
pinMode(_HEARTBEATPIN, OUTPUT);
|
||||
pinMode(_SCHNAPPERPIN, OUTPUT);
|
||||
lock();
|
||||
}
|
||||
|
||||
@ -31,49 +31,73 @@ Door &Door::get()
|
||||
|
||||
void Door::lock()
|
||||
{
|
||||
digitalWrite(_SCHNAPPER, HIGH);
|
||||
digitalWrite(_SCHNAPPERPIN, HIGH);
|
||||
_l(LogLevel::info, "Door closed");
|
||||
if (_open == true)
|
||||
|
||||
if (_state == State::Unlocked)
|
||||
{
|
||||
_open = false;
|
||||
// Stop the Heartbeat Thread
|
||||
_state = State::Locked;
|
||||
_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");
|
||||
}
|
||||
|
||||
void Door::unlock()
|
||||
{
|
||||
// In any case, klacker the schnapper
|
||||
_schnapper = true;
|
||||
|
||||
if (_open == true)
|
||||
// If heartbeat is already running, return
|
||||
if (_state == State::Unlocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_open = true;
|
||||
// If not, first set state to unlocked
|
||||
_state = State::Unlocked;
|
||||
|
||||
// Start the Heartbeat Thread
|
||||
_heartbeat = std::thread([this] () {
|
||||
|
||||
// One "beat" is one complete cycle of the heartbeat clock
|
||||
auto beat = [] () {
|
||||
digitalWrite(_LOCKPIN, HIGH);
|
||||
digitalWrite(_HEARTBEATPIN, HIGH);
|
||||
usleep(10000);
|
||||
digitalWrite(_LOCKPIN, LOW);
|
||||
digitalWrite(_HEARTBEATPIN, LOW);
|
||||
usleep(10000);
|
||||
};
|
||||
|
||||
digitalWrite(_SCHNAPPER, HIGH);
|
||||
while (_open) {
|
||||
// The default of the Schnapperpin: always high
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < 32 ; i++)
|
||||
{
|
||||
digitalWrite(_SCHNAPPER, LOW);
|
||||
// Set '0'
|
||||
digitalWrite(_SCHNAPPERPIN, LOW);
|
||||
// cycle and send
|
||||
beat();
|
||||
digitalWrite(_SCHNAPPER, HIGH);
|
||||
// Set '1'
|
||||
digitalWrite(_SCHNAPPERPIN, HIGH);
|
||||
// cycle and send
|
||||
beat();
|
||||
}
|
||||
digitalWrite(_SCHNAPPER, HIGH);
|
||||
|
||||
// Reset schnapperpin
|
||||
digitalWrite(_SCHNAPPERPIN, HIGH);
|
||||
// and deactivate schnapper for the next round
|
||||
_schnapper = false;
|
||||
}
|
||||
|
||||
// Heartbeat
|
||||
beat();
|
||||
}
|
||||
});
|
||||
|
@ -6,29 +6,52 @@
|
||||
|
||||
#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 {
|
||||
|
||||
public:
|
||||
static Door &get();
|
||||
|
||||
// Returns the singleton
|
||||
static Door &get();
|
||||
~Door();
|
||||
|
||||
enum class State {Locked, Unlocked};
|
||||
|
||||
// Lock the door
|
||||
void lock();
|
||||
// Unlock the door
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
|
||||
Door();
|
||||
|
||||
// used for logging
|
||||
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 = { };
|
||||
|
||||
// Read by the Heartbeat thread if it should klacker the schnapper or not
|
||||
bool _schnapper = { false };
|
||||
|
||||
static constexpr int _LOCKPIN = 10;
|
||||
static constexpr int _SCHNAPPER = 7;
|
||||
// WiringPi GPIO Pins
|
||||
static constexpr int _HEARTBEATPIN = 10;
|
||||
static constexpr int _SCHNAPPERPIN = 7;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,8 @@ using namespace std;
|
||||
Epaper::Epaper() :
|
||||
_logger(Logger::get())
|
||||
{
|
||||
memset(_prevImg, 0xFF, ARRAY_SIZE);
|
||||
memset(_prevImg, 0xFF, _ARRAY_SIZE);
|
||||
// Initialize Epaper library
|
||||
bsp_init();
|
||||
}
|
||||
|
||||
@ -32,12 +33,12 @@ Epaper &Epaper::get()
|
||||
|
||||
void Epaper::draw(const string &uri)
|
||||
{
|
||||
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());
|
||||
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());
|
||||
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");
|
||||
int i = fread(buffer, ARRAY_SIZE, 1, f);
|
||||
int i = fread(buffer, _ARRAY_SIZE, 1, f);
|
||||
if (i != 1)
|
||||
{
|
||||
_logger(LogLevel::error, "Image format error");
|
||||
@ -47,5 +48,5 @@ void Epaper::draw(const string &uri)
|
||||
pclose(f);
|
||||
|
||||
epd_DisplayImg(EPDType_270, buffer, _prevImg);
|
||||
memcpy(_prevImg, buffer, ARRAY_SIZE);
|
||||
memcpy(_prevImg, buffer, _ARRAY_SIZE);
|
||||
}
|
||||
|
@ -6,24 +6,35 @@
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
/*
|
||||
* The "Epaper" class.
|
||||
*
|
||||
* Wrapper for the epaper third Party library.
|
||||
*
|
||||
* Exists as singleton, as only one display may exist.
|
||||
*/
|
||||
class Epaper {
|
||||
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();
|
||||
~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);
|
||||
|
||||
private:
|
||||
|
||||
constexpr static int _HEIGHT = 176; // In Pixel
|
||||
constexpr static int _WIDTH = 33; // In Byte
|
||||
constexpr static int _ARRAY_SIZE = _HEIGHT * _WIDTH;
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -16,16 +16,14 @@ using namespace std;
|
||||
Logic::Logic(const chrono::seconds tokenTimeout,
|
||||
const string &ldapServer,
|
||||
const string &bindDN,
|
||||
const string &webPrefix,
|
||||
const string &allowedIpPrefix) :
|
||||
const string &webPrefix) :
|
||||
_logger(Logger::get()),
|
||||
_door(Door::get()),
|
||||
_epaper(Epaper::get()),
|
||||
_tokenTimeout(tokenTimeout),
|
||||
_ldapServer(ldapServer),
|
||||
_bindDN(bindDN),
|
||||
_webPrefix(webPrefix),
|
||||
_allowedIpPrefix(allowedIpPrefix)
|
||||
_webPrefix(webPrefix)
|
||||
{
|
||||
srand(time(NULL));
|
||||
_createNewToken(false);
|
||||
@ -34,7 +32,7 @@ Logic::Logic(const chrono::seconds tokenTimeout,
|
||||
while (_run)
|
||||
{
|
||||
unique_lock<mutex> l(_mutex);
|
||||
_c.wait_for(l, _tokenTimeout);
|
||||
_tokenCondition.wait_for(l, _tokenTimeout);
|
||||
if (_run == false)
|
||||
{
|
||||
break;
|
||||
@ -48,7 +46,7 @@ Logic::Logic(const chrono::seconds tokenTimeout,
|
||||
Logic::~Logic()
|
||||
{
|
||||
_run = false;
|
||||
_c.notify_one();
|
||||
_tokenCondition.notify_one();
|
||||
_tokenUpdater.join();
|
||||
}
|
||||
|
||||
@ -119,14 +117,14 @@ out:
|
||||
|
||||
Logic::Response Logic::_lock()
|
||||
{
|
||||
if (_state == LOCKED)
|
||||
if (_state == Door::State::Locked)
|
||||
{
|
||||
_logger(LogLevel::warning, "Unable to lock: already closed");
|
||||
return AlreadyLocked;
|
||||
}
|
||||
|
||||
_door.lock();
|
||||
_state = LOCKED;
|
||||
_state = Door::State::Locked;
|
||||
_createNewToken(false);
|
||||
|
||||
return Success;
|
||||
@ -137,12 +135,12 @@ Logic::Response Logic::_unlock()
|
||||
_door.unlock();
|
||||
_createNewToken(false);
|
||||
|
||||
if (_state == UNLOCKED)
|
||||
if (_state == Door::State::Unlocked)
|
||||
{
|
||||
_logger(LogLevel::warning, "Unable to unlock: already unlocked");
|
||||
return AlreadyUnlocked;
|
||||
} else {
|
||||
_state = UNLOCKED;
|
||||
_state = Door::State::Unlocked;
|
||||
}
|
||||
|
||||
return Success;
|
||||
|
@ -12,6 +12,12 @@
|
||||
#include "door.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
|
||||
{
|
||||
public:
|
||||
@ -19,8 +25,7 @@ public:
|
||||
Logic(const std::chrono::seconds tokenTimeout,
|
||||
const std::string &ldapServer,
|
||||
const std::string &bindDN,
|
||||
const std::string &webPrefix,
|
||||
const std::string &allowedIpPrefix);
|
||||
const std::string &webPrefix);
|
||||
~Logic();
|
||||
|
||||
enum Response {
|
||||
@ -37,40 +42,63 @@ public:
|
||||
LDAPInit, // Ldap initialization failed
|
||||
};
|
||||
|
||||
// Parse incoming JSON Requests
|
||||
Response parseRequest(const std::string &str);
|
||||
|
||||
private:
|
||||
|
||||
// Internal lock wrapper
|
||||
Response _lock();
|
||||
// Internal unlock wrapper
|
||||
Response _unlock();
|
||||
|
||||
// Checks if the incoming token is valid
|
||||
bool _checkToken(const std::string &token);
|
||||
|
||||
// Checks if incoming credentials against LDAP
|
||||
Response _checkLDAP(const std::string &user,
|
||||
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);
|
||||
|
||||
const Logger &_logger;
|
||||
|
||||
// Door reference
|
||||
Door &_door;
|
||||
// Epaper reference
|
||||
Epaper &_epaper;
|
||||
|
||||
// Tokens are 64-bit hexadecimal values
|
||||
using Token = uint64_t;
|
||||
|
||||
// The current token
|
||||
Token _curToken = { 0x0000000000000000 };
|
||||
bool _prevValid = { false };
|
||||
// The previous token
|
||||
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::string _ldapServer;
|
||||
const std::string _bindDN;
|
||||
const std::string _webPrefix;
|
||||
const std::string _allowedIpPrefix;
|
||||
|
||||
// Thread for asynchronosly updating tokens
|
||||
std::thread _tokenUpdater = {};
|
||||
std::condition_variable _c = {};
|
||||
std::mutex _mutex = {};
|
||||
// Thread can be force-triggered for updates using the condition variable
|
||||
std::condition_variable _tokenCondition = {};
|
||||
// stop indicator for the thread
|
||||
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
|
||||
|
@ -30,6 +30,9 @@ void signal_handler(int signum)
|
||||
logic.reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Session class handles asynchronosly handles one incoming TCP session
|
||||
*/
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
@ -63,6 +66,9 @@ private:
|
||||
char _data[_maxLen];
|
||||
};
|
||||
|
||||
/*
|
||||
* The TCP server
|
||||
*/
|
||||
class server
|
||||
{
|
||||
|
||||
@ -105,13 +111,13 @@ int main(int argc, char** argv)
|
||||
string ldapServer;
|
||||
string bindDN;
|
||||
string lockPagePrefix;
|
||||
string allowedIpPrefix;
|
||||
string logfile;
|
||||
string pidFile;
|
||||
bool foreground = false;
|
||||
|
||||
l(LogLevel::notice, "Starting doorlockd");
|
||||
|
||||
// Load SPI and I2C modules
|
||||
system("/usr/bin/gpio load spi");
|
||||
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")
|
||||
("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")
|
||||
("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")
|
||||
("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");
|
||||
@ -166,8 +171,7 @@ int main(int argc, char** argv)
|
||||
logic = unique_ptr<Logic>(new Logic(tokenTimeout,
|
||||
ldapServer,
|
||||
bindDN,
|
||||
lockPagePrefix,
|
||||
allowedIpPrefix));
|
||||
lockPagePrefix));
|
||||
|
||||
try {
|
||||
server s(io_service, port);
|
||||
|
Loading…
Reference in New Issue
Block a user