From aec8bba72313ee125dc0eab15b2107c1653377ad Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Wed, 20 Dec 2017 18:16:22 -0500 Subject: [PATCH] 2017-12-20 Fred Gleason * Added a 'LOGS.LOCK_GUID' field to the database. * Incremented the database version to 274. * Added a 'LockLog' call to the Web API. --- ChangeLog | 4 ++ docs/apis/web_api.xml | 57 +++++++++++++++++ docs/tables/logs.txt | 3 +- lib/dbversion.h | 2 +- lib/rdloglock.cpp | 102 ++++++++++++++++++++---------- lib/rdloglock.h | 6 ++ lib/rdxport_interface.h | 1 + rdadmin/createdb.cpp | 15 ++++- utils/rdrevert/rdrevert.cpp | 23 ++++++- utils/rdrevert/rdrevert.h | 1 + web/rdxport/logs.cpp | 123 ++++++++++++++++++++++++++++++++++-- web/rdxport/rdxport.cpp | 13 ++++ web/rdxport/rdxport.h | 7 ++ web/tests/Makefile.am | 3 + web/tests/locklog.html | 48 ++++++++++++++ 15 files changed, 365 insertions(+), 43 deletions(-) create mode 100644 web/tests/locklog.html diff --git a/ChangeLog b/ChangeLog index 21c107a7..50b285a4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16526,3 +16526,7 @@ 2017-12-20 Fred Gleason * Modified the behavior of the 'new' command in rdclilogedit(1) to match that of rdlogedit(1). +2017-12-20 Fred Gleason + * Added a 'LOGS.LOCK_GUID' field to the database. + * Incremented the database version to 274. + * Added a 'LockLog' call to the Web API. diff --git a/docs/apis/web_api.xml b/docs/apis/web_api.xml index 9008cdb2..08defe58 100644 --- a/docs/apis/web_api.xml +++ b/docs/apis/web_api.xml @@ -2669,6 +2669,63 @@ + + LockLog + Set / Update / Clear a log lock + + Command Code: RDXPORT_COMMAND_LOCKLOG + + + Required User Permissions: none + + + LockLog Call Fields + + + + + + + FIELD NAME + MEANING + REMARKS + + + + + COMMAND + 34 + Mandatory + + + OPERATION + Operation to Perform + + + Mandatory. Possible values: + + + CREATE + UPDATE + CLEAR + + + + + LOG_NAME + Name of log + Mandatory + + + LOCK_GUID + Opaque GUID string, returned by the CREATE operation. + Mandatory. For CREATE, send an empty string. + + + +
+
+ Rehash Generate a SHA-1 hash for a cut and write it to the database diff --git a/docs/tables/logs.txt b/docs/tables/logs.txt index 152d6d8f..a83951f2 100644 --- a/docs/tables/logs.txt +++ b/docs/tables/logs.txt @@ -6,7 +6,7 @@ table: FIELD NAME TYPE REMARKS ------------------------------------------------------------------- -NAME char(64) Table name, ends with '_LOG' +NAME char(64) LOG_EXISTS enum('N','Y') TYPE int(11) 0=Log, 1=Event, 2=Clock, 3=Grid SERVICE char(10) From SERVICES.NAME @@ -31,3 +31,4 @@ LOCK_USER_NAME char(255) From USER.LOGIN_NAME LOCK_STATION_NAME char(64) From STATIONS.NAME LOCK_IPV4_ADDRESS char(16) LOCK_DATETIME datetime +LOCK_GUID char(82) diff --git a/lib/dbversion.h b/lib/dbversion.h index 8dbbd90f..111c8deb 100644 --- a/lib/dbversion.h +++ b/lib/dbversion.h @@ -24,7 +24,7 @@ /* * Current Database Version */ -#define RD_VERSION_DATABASE 273 +#define RD_VERSION_DATABASE 274 #endif // DBVERSION_H diff --git a/lib/rdloglock.cpp b/lib/rdloglock.cpp index d9f5109e..c37de24f 100644 --- a/lib/rdloglock.cpp +++ b/lib/rdloglock.cpp @@ -59,6 +59,43 @@ bool RDLogLock::isLocked() const bool RDLogLock::tryLock(QString *username,QString *stationname, QHostAddress *addr) +{ + bool ret=false; + QString guid=RDLogLock::makeGuid(lock_station->name()); + + *username=lock_user->name(); + *stationname=lock_station->name(); + addr->setAddress(lock_station->address().toString()); + + if(RDLogLock::tryLock(username,stationname,addr,lock_log_name,guid)) { + lock_timer->start(RD_LOG_LOCK_TIMEOUT/2); + lock_guid=guid; + lock_locked=true; + ret=true; + } + + return ret; +} + + +void RDLogLock::clearLock() +{ + RDLogLock::clearLock(lock_guid); + lock_guid=QString(); + lock_timer->stop(); + lock_locked=false; +} + + +void RDLogLock::updateLock() +{ + RDLogLock::updateLock(lock_log_name,lock_guid); +} + + +bool RDLogLock::tryLock(QString *username,QString *stationname, + QHostAddress *addr,const QString &log_name, + const QString &guid) { QString sql; RDSqlQuery *q; @@ -67,18 +104,17 @@ bool RDLogLock::tryLock(QString *username,QString *stationname, QDateTime now=QDateTime::currentDateTime(); sql=QString("update LOGS set ")+ - "LOCK_USER_NAME=\""+RDEscapeString(lock_user->name())+"\","+ - "LOCK_STATION_NAME=\""+RDEscapeString(lock_station->name())+"\","+ - "LOCK_IPV4_ADDRESS=\""+RDEscapeString(lock_station->address().toString())+ + "LOCK_USER_NAME=\""+RDEscapeString(*username)+"\","+ + "LOCK_STATION_NAME=\""+RDEscapeString(*stationname)+"\","+ + "LOCK_IPV4_ADDRESS=\""+RDEscapeString(addr->toString())+ "\","+ + "LOCK_GUID=\""+RDEscapeString(guid)+"\","+ "LOCK_DATETIME=now() where "+ - "(NAME=\""+RDEscapeString(lock_log_name)+"\")&&"+ + "(NAME=\""+RDEscapeString(log_name)+"\")&&"+ "((LOCK_DATETIME is null)||"+ "(LOCK_DATETIME<\""+RDEscapeString(now.addSecs(-RD_LOG_LOCK_TIMEOUT/1000).toString("yyyy-MM-dd hh:mm:ss"))+"\"))"; q=new RDSqlQuery(sql); if(q->numRowsAffected()>0) { - lock_timer->start(RD_LOG_LOCK_TIMEOUT/2); - lock_locked=true; ret=true; } else { @@ -87,7 +123,7 @@ bool RDLogLock::tryLock(QString *username,QString *stationname, "LOCK_STATION_NAME,"+ "LOCK_IPV4_ADDRESS "+ "from LOGS where "+ - "NAME=\""+RDEscapeString(lock_log_name)+"\""; + "NAME=\""+RDEscapeString(log_name)+"\""; q1=new RDSqlQuery(sql); if(q1->first()) { *username=q1->value(0).toString(); @@ -102,7 +138,26 @@ bool RDLogLock::tryLock(QString *username,QString *stationname, } -void RDLogLock::clearLock() +void RDLogLock::updateLock(const QString &log_name,const QString &guid) +{ + QString sql; + RDSqlQuery *q; + + sql=QString("update LOGS set ")+ + "LOCK_DATETIME=now() where "+ + "LOCK_GUID=\""+RDEscapeString(guid)+"\""; + q=new RDSqlQuery(sql); +#ifndef WIN32 + if(q->numRowsAffected()==0) { + syslog(LOG_WARNING,"lock on log \"%s\" has evaporated!", + (const char *)log_name); + } +#endif // WIN32 + delete q; +} + + +void RDLogLock::clearLock(const QString &guid) { QString sql; RDSqlQuery *q; @@ -111,37 +166,16 @@ void RDLogLock::clearLock() "LOCK_USER_NAME=null,"+ "LOCK_STATION_NAME=null,"+ "LOCK_IPV4_ADDRESS=null,"+ + "LOCK_GUID=null,"+ "LOCK_DATETIME=null where "+ - "(NAME=\""+RDEscapeString(lock_log_name)+"\")&&"+ - "(LOCK_USER_NAME=\""+RDEscapeString(lock_user->name())+"\")&&"+ - "(LOCK_STATION_NAME=\""+RDEscapeString(lock_station->name())+"\")&&"+ - "(LOCK_IPV4_ADDRESS=\""+RDEscapeString(lock_station->address().toString())+ - "\")"; + "LOCK_GUID=\""+RDEscapeString(guid)+"\""; q=new RDSqlQuery(sql); delete q; - lock_timer->stop(); - lock_locked=false; } -void RDLogLock::updateLock() +QString RDLogLock::makeGuid(const QString &stationname) { - QString sql; - RDSqlQuery *q; - - sql=QString("update LOGS set ")+ - "LOCK_DATETIME=now() where "+ - "(NAME=\""+RDEscapeString(lock_log_name)+"\")&&"+ - "(LOCK_USER_NAME=\""+RDEscapeString(lock_user->name())+"\")&&"+ - "(LOCK_STATION_NAME=\""+RDEscapeString(lock_station->name())+"\")&&"+ - "(LOCK_IPV4_ADDRESS=\""+RDEscapeString(lock_station->address().toString())+ - "\")"; - q=new RDSqlQuery(sql); -#ifndef WIN32 - if(q->numRowsAffected()==0) { - syslog(LOG_WARNING,"lock on log \"%s\" has gone stale", - (const char *)lock_log_name); - } -#endif // WIN32 - delete q; + return stationname+QDateTime::currentDateTime(). + toString("yyyyMMddhhmmsszzz"); } diff --git a/lib/rdloglock.h b/lib/rdloglock.h index 59f6a20a..a6bf22a5 100644 --- a/lib/rdloglock.h +++ b/lib/rdloglock.h @@ -39,6 +39,11 @@ class RDLogLock : public QObject bool isLocked() const; bool tryLock(QString *username,QString *stationname,QHostAddress *addr); void clearLock(); + static bool tryLock(QString *username,QString *stationname,QHostAddress *addr, + const QString &log_name,const QString &guid); + static void updateLock(const QString &log_name,const QString &guid); + static void clearLock(const QString &guid); + static QString makeGuid(const QString &stationname); private slots: void updateLock(); @@ -49,6 +54,7 @@ class RDLogLock : public QObject RDStation *lock_station; QTimer *lock_timer; bool lock_locked; + QString lock_guid; }; diff --git a/lib/rdxport_interface.h b/lib/rdxport_interface.h index 199e28f2..016034af 100644 --- a/lib/rdxport_interface.h +++ b/lib/rdxport_interface.h @@ -54,6 +54,7 @@ #define RDXPORT_COMMAND_CREATETICKET 31 #define RDXPORT_COMMAND_REHASH 32 #define RDXPORT_COMMAND_LISTSYSTEMSETTINGS 33 +#define RDXPORT_COMMAND_LOCKLOG 34 #endif // RDXPORT_INTERFACE_H diff --git a/rdadmin/createdb.cpp b/rdadmin/createdb.cpp index 213d9814..7456a884 100644 --- a/rdadmin/createdb.cpp +++ b/rdadmin/createdb.cpp @@ -988,13 +988,15 @@ bool CreateDb(QString name,QString pwd,RDConfig *config) "LOCK_STATION_NAME char(64),"+ "LOCK_IPV4_ADDRESS char(16),"+ "LOCK_DATETIME datetime,"+ + "LOCK_GUID char(82),"+ "index NAME_IDX (NAME,LOG_EXISTS),"+ "index SERVICE_IDX (SERVICE),"+ "index DESCRIPTION_IDX (DESCRIPTION),"+ "index ORIGIN_USER_IDX (ORIGIN_USER),"+ "index START_DATE_IDX (START_DATE),"+ "index END_DATE_IDX (END_DATE),"+ - "index TYPE_IDX(TYPE,LOG_EXISTS))"+ + "index TYPE_IDX(TYPE,LOG_EXISTS),"+ + "index LOCK_GUID_IDX(LOCK_GUID))"+ config->createTablePostfix(); if(!RunQuery(sql)) { return false; @@ -8001,6 +8003,17 @@ int UpdateDb(int ver,RDConfig *config) delete q; } + if(ver<274) { + sql=QString("alter table LOGS ")+ + "add column LOCK_GUID char(82) after LOCK_DATETIME"; + q=new RDSqlQuery(sql,false); + delete q; + + sql=QString("alter table LOGS add index LOCK_GUID_IDX(LOCK_GUID)"); + q=new RDSqlQuery(sql,false); + delete q; + } + // // Maintainer's Note: diff --git a/utils/rdrevert/rdrevert.cpp b/utils/rdrevert/rdrevert.cpp index 116e5674..f858fafb 100644 --- a/utils/rdrevert/rdrevert.cpp +++ b/utils/rdrevert/rdrevert.cpp @@ -251,6 +251,10 @@ void MainObject::Revert(int schema) const case 273: Revert273(); break; + + case 274: + Revert274(); + break; } } @@ -787,6 +791,23 @@ void MainObject::Revert273() const } +void MainObject::Revert274() const +{ + QString sql; + RDSqlQuery *q; + + sql=QString("alter table LOGS drop index LOCK_GUID_IDX"); + q=new RDSqlQuery(sql,false); + delete q; + + sql=QString("alter table LOGS drop column LOCK_GUID"); + q=new RDSqlQuery(sql,false); + delete q; + + SetVersion(273); +} + + int MainObject::GetVersion() const { QString sql; @@ -832,7 +853,7 @@ int MainObject::MapSchema(const QString &ver) version_map["2.16"]=263; version_map["2.17"]=268; version_map["2.18"]=272; - version_map["2.19"]=273; + version_map["2.19"]=274; // // Normalize String diff --git a/utils/rdrevert/rdrevert.h b/utils/rdrevert/rdrevert.h index 4ffeac64..575ad2fb 100644 --- a/utils/rdrevert/rdrevert.h +++ b/utils/rdrevert/rdrevert.h @@ -69,6 +69,7 @@ class MainObject : public QObject void Revert271() const; void Revert272() const; void Revert273() const; + void Revert274() const; int GetVersion() const; void SetVersion(int schema) const; int MapSchema(const QString &ver); diff --git a/web/rdxport/logs.cpp b/web/rdxport/logs.cpp index 3cb3ce51..64f53640 100644 --- a/web/rdxport/logs.cpp +++ b/web/rdxport/logs.cpp @@ -24,18 +24,19 @@ #include #include +#include #include #include +#include #include -#include -#include #include #include #include -#include -#include +#include +#include +#include -#include +#include "rdxport.h" void Xport::AddLog() { @@ -541,3 +542,115 @@ bool Xport::ServiceUserValid(const QString &svc_name) return ret; } + + +void Xport::LockLog() +{ + RDLog *log; + QString log_name=""; + Xport::LockLogOperation op_type=Xport::LockLogClear; + QString op_string; + QString lock_guid; + QString username; + QString stationname; + QHostAddress addr; + + // + // Get Options + // + if(!xport_post->getValue("LOG_NAME",&log_name)) { + XmlExit("Missing LOG_NAME",400,"logs.cpp",LINE_NUMBER); + } + if(!xport_post->getValue("OPERATION",&op_string)) { + XmlExit("Missing OPERATION",400,"logs.cpp",LINE_NUMBER); + } + if(op_string.lower()=="create") { + op_type=Xport::LockLogCreate; + } + else { + if(op_string.lower()=="update") { + op_type=Xport::LockLogUpdate; + } + else { + if(op_string.lower()=="clear") { + op_type=Xport::LockLogClear; + } + else { + XmlExit("Unrecognized OPERATION type",400,"logs.cpp",LINE_NUMBER); + } + } + } + if(!xport_post->getValue("LOCK_GUID",&lock_guid)) { + XmlExit("Missing LOCK_GUID",400,"logs.cpp",LINE_NUMBER); + } + + // + // Verify that log exists + // + log=new RDLog(log_name); + if((!ServiceUserValid(log->service()))||(!log->exists())) { + delete log; + XmlExit("No such log",404,"logs.cpp",LINE_NUMBER); + } + + printf("Content-type: application/xml\n"); + printf("Status: 200\n\n"); + switch(op_type) { + case Xport::LockLogCreate: + username=xport_user->name(); + stationname=xport_remote_hostname; + addr=xport_remote_address; + lock_guid=RDLogLock::makeGuid(xport_remote_hostname); + if(RDLogLock::tryLock(&username,&stationname,&addr,log_name,lock_guid)) { + printf("%s",(const char *)LogLockXml(true,log_name,lock_guid,"","",addr)); + } + else { + printf("%s",(const char *)LogLockXml(false,log_name,"",username, + stationname,addr)); + } + Exit(0); + break; + + case Xport::LockLogUpdate: + RDLogLock::updateLock(log_name,lock_guid); + printf("%s",(const char *)LogLockXml(true,log_name,lock_guid,"","",addr)); + Exit(0); + break; + + case Xport::LockLogClear: + RDLogLock::clearLock(lock_guid); + printf("%s",(const char *)LogLockXml(true,log_name,lock_guid,"","",addr)); + Exit(0); + break; + } + + XmlExit("Unexpected exit",500); +} + + +QString Xport::LogLockXml(bool result,const QString &log_name, + const QString &guid,const QString &username, + const QString &stationname, + const QHostAddress addr) const +{ + QString xml=""; + + xml+="\n"; + xml+="\n"; + xml+=RDXmlField("result",result); + xml+=RDXmlField("logName",log_name); + if(!guid.isEmpty()) { + xml+=RDXmlField("lockGuid",guid); + } + if(!username.isEmpty()) { + xml+=RDXmlField("userName",username); + } + if(!stationname.isEmpty()) { + xml+=RDXmlField("stationName",stationname); + } + xml+=RDXmlField("address",addr.toString()); + xml+=RDXmlField("lockTimeout",RD_LOG_LOCK_TIMEOUT); + xml+="\n"; + + return xml; +} diff --git a/web/rdxport/rdxport.cpp b/web/rdxport/rdxport.cpp index 6637639d..7c96781b 100644 --- a/web/rdxport/rdxport.cpp +++ b/web/rdxport/rdxport.cpp @@ -119,6 +119,15 @@ Xport::Xport(QObject *parent) db->removeDatabase(xport_config->mysqlDbname()); Exit(0); } + if(getenv("REMOTE_ADDR")!=NULL) { + xport_remote_address.setAddress(getenv("REMOTE_ADDR")); + } + if(getenv("REMOTE_HOST")!=NULL) { + xport_remote_hostname=getenv("REMOTE_HOST"); + } + if(xport_remote_hostname.isEmpty()) { + xport_remote_hostname=xport_remote_address.toString(); + } // // Load System Settings @@ -272,6 +281,10 @@ Xport::Xport(QObject *parent) ListSystemSettings(); break; + case RDXPORT_COMMAND_LOCKLOG: + LockLog(); + break; + case RDXPORT_COMMAND_REHASH: Rehash(); break; diff --git a/web/rdxport/rdxport.h b/web/rdxport/rdxport.h index 92675981..5aa28119 100644 --- a/web/rdxport/rdxport.h +++ b/web/rdxport/rdxport.h @@ -39,6 +39,7 @@ class Xport : public QObject { public: + enum LockLogOperation {LockLogCreate=0,LockLogUpdate=1,LockLogClear=2}; Xport(QObject *parent=0); private: @@ -80,6 +81,10 @@ class Xport : public QObject void ListCartSchedCodes(); void ListServices(); void ListSystemSettings(); + void LockLog(); + QString LogLockXml(bool result,const QString &log_name,const QString &guid, + const QString &username,const QString &stationname, + const QHostAddress addr) const; void Exit(int code); void XmlExit(const QString &msg,int code, const QString &srcfile="",int line=-1, @@ -89,6 +94,8 @@ class Xport : public QObject RDConfig *xport_config; RDSystem *xport_system; RDStation *xport_station; + QString xport_remote_hostname; + QHostAddress xport_remote_address; }; diff --git a/web/tests/Makefile.am b/web/tests/Makefile.am index 56c5cbaa..ebdccd22 100644 --- a/web/tests/Makefile.am +++ b/web/tests/Makefile.am @@ -51,6 +51,7 @@ install-exec-am: cp listschedcodes.html $(DESTDIR)@libexecdir@ cp listservices.html $(DESTDIR)@libexecdir@ cp listsystemsettings.html $(DESTDIR)@libexecdir@ + cp locklog.html $(DESTDIR)@libexecdir@ cp rehash.html $(DESTDIR)@libexecdir@ cp removecart.html $(DESTDIR)@libexecdir@ cp removecut.html $(DESTDIR)@libexecdir@ @@ -89,6 +90,7 @@ uninstall-local: rm -f $(DESTDIR)@libexecdir@/listschedcodes.html rm -f $(DESTDIR)@libexecdir@/listservices.html rm -f $(DESTDIR)@libexecdir@/listsystemsettings.html + rm -f $(DESTDIR)@libexecdir@/locklog.html rm -f $(DESTDIR)@libexecdir@/rehash.html rm -f $(DESTDIR)@libexecdir@/removecart.html rm -f $(DESTDIR)@libexecdir@/removecut.html @@ -126,6 +128,7 @@ EXTRA_DIST = addcart.html\ listschedcodes.html\ listservices.html\ listsystemsettings.html\ + locklog.html\ rehash.html\ removecart.html\ removecut.html\ diff --git a/web/tests/locklog.html b/web/tests/locklog.html new file mode 100644 index 00000000..5deedd66 --- /dev/null +++ b/web/tests/locklog.html @@ -0,0 +1,48 @@ + + +Rivendell LOCKLOG Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
OPERATION: + +
LOG_NAME:
LOCK_GUID:
 
+
+ +