2017-12-20 Fred Gleason <fredg@paravelsystems.com>

* Added a 'LOGS.LOCK_GUID' field to the database.
	* Incremented the database version to 274.
	* Added a 'LockLog' call to the Web API.
This commit is contained in:
Fred Gleason 2017-12-20 18:16:22 -05:00
parent 6b34f4d70e
commit aec8bba723
15 changed files with 365 additions and 43 deletions

View File

@ -16526,3 +16526,7 @@
2017-12-20 Fred Gleason <fredg@paravelsystems.com>
* Modified the behavior of the 'new' command in rdclilogedit(1) to
match that of rdlogedit(1).
2017-12-20 Fred Gleason <fredg@paravelsystems.com>
* Added a 'LOGS.LOCK_GUID' field to the database.
* Incremented the database version to 274.
* Added a 'LockLog' call to the Web API.

View File

@ -2669,6 +2669,63 @@
</table>
</sect1>
<sect1>
<title>LockLog</title>
<subtitle>Set / Update / Clear a log lock</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_LOCKLOG</code>
</para>
<para>
Required User Permissions: none
</para>
<table xml:id="ex.locklog" frame="all">
<title>LockLog Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>FIELD NAME</entry>
<entry>MEANING</entry>
<entry>REMARKS</entry>
</row>
</thead>
<tbody>
<row>
<entry>COMMAND</entry>
<entry>34</entry>
<entry>Mandatory</entry>
</row>
<row>
<entry>OPERATION</entry>
<entry>Operation to Perform</entry>
<entry>
<para>
Mandatory. Possible values:
</para>
<simplelist>
<member>CREATE</member>
<member>UPDATE</member>
<member>CLEAR</member>
</simplelist>
</entry>
</row>
<row>
<entry>LOG_NAME</entry>
<entry>Name of log</entry>
<entry>Mandatory</entry>
</row>
<row>
<entry>LOCK_GUID</entry>
<entry>Opaque GUID string, returned by the CREATE operation.</entry>
<entry>Mandatory. For CREATE, send an empty string.</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1>
<title>Rehash</title>
<subtitle>Generate a SHA-1 hash for a cut and write it to the database</subtitle>

View File

@ -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)

View File

@ -24,7 +24,7 @@
/*
* Current Database Version
*/
#define RD_VERSION_DATABASE 273
#define RD_VERSION_DATABASE 274
#endif // DBVERSION_H

View File

@ -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");
}

View File

@ -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;
};

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -24,18 +24,19 @@
#include <fcntl.h>
#include <errno.h>
#include <rdconf.h>
#include <rdcreate_log.h>
#include <rddb.h>
#include <rdescape_string.h>
#include <rdformpost.h>
#include <rdweb.h>
#include <rduser.h>
#include <rdlog.h>
#include <rdlog_event.h>
#include <rdlog_line.h>
#include <rdconf.h>
#include <rdescape_string.h>
#include <rdloglock.h>
#include <rduser.h>
#include <rdweb.h>
#include <rdxport.h>
#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+="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
xml+="<logLock>\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+="</logLock>\n";
return xml;
}

View File

@ -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;

View File

@ -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;
};

View File

@ -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\

48
web/tests/locklog.html Normal file
View File

@ -0,0 +1,48 @@
<html>
<head>
<title>Rivendell LOCKLOG Service Test Harness</title>
<body>
<form action="/rd-bin/rdxport.cgi" method="post" enctype="multipart/form-data">
<input type="hidden" name="COMMAND" value="34">
<table cellpadding="0" cellspacing="2" border="0">
<tr>
<td align="right">LOGIN NAME:</td>
<td><input type="text" name="LOGIN_NAME" size="20" maxlength="255"></td>
</tr>
<tr>
<td align="right">PASSWORD:</td>
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">OPERATION:</td>
<td>
<select name="OPERATION">
<option value="CREATE">CREATE</option>
<option value="UPDATE">UPDATE</option>
<option value="CLEAR">CLEAR</option>
</select>
</td>
</tr>
<tr>
<td align="right">LOG_NAME:</td>
<td><input type="text" name="LOG_NAME" size="40" maxlength="64"></td>
</tr>
<tr>
<td align="right">LOCK_GUID:</td>
<td><input type="text" name="LOCK_GUID" size="40" maxlength="82"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>