Remoto - VFS: VFS_cron.cpp Source File
Remoto - VFS
VFS_cron.cpp
Go to the documentation of this file.
1 
2 #include <QDateTime>
3 #include <QTimer>
4 #include <QThread>
5 
6 #include "VFS.h"
7 #include "VFS_cron.h"
8 #include "VFS_base/VFS_icons.h"
9 
115 VFS_cron::VFS_cron(QString crontab)//, QString settings)
116 : VFS_node()
117 , _cronID(-1)
118 {
119  //Q_UNUSED(settings)
120 
121  parseCrontab(crontab);
122 
123  connect(VFS::root(),SIGNAL(initialized()),this,SLOT(initialize()));
124 }
125 
127 {
128  //destructor
129 }
130 
139 {
140  if (_crontab.contains(r->_path))
141  return this;
142  //else
143  // printf("VFS_cron not subpath: %s\n",qUtf8Printable(r->_path));
144 
145  if (r->_metadata["createIfMissing"].toBool())
146  return this;
147 
148  if (r->_path == "")
149  return this;
150 
151  return VFS_node::find(r);
152 }
153 
163 {
164  QMutexLocker l(&_lock);
165 
166  QStringList r;
167  QMapIterator<QString, crontime> i(_crontab);
168  while (i.hasNext()) {
169  i.next();
170  r << i.key() + ":\n"+i.value().toString();
171  }
172 
173  if (r.isEmpty())
174  return "(no events)";
175 
176  return r.join("\n");
177 }
178 
185 {
186  return true;
187 }
188 
194 QByteArray VFS_cron::icon()
195 {
196  return VFS_icons::get("clock");
197 }
198 
208 {
209  QTime now = QTime::currentTime();
210  QTime next = QTime::currentTime();
211  next.setHMS(next.hour(),next.minute(),next.second(),0);
212  next = next.addSecs(1);
213 
214  //int w = 1000 - now.msec() - 10;
215  int w = now.msecsTo(next) - 5;
216  w = qMax(0,w);
217 
218  QTimer::singleShot(w,Qt::PreciseTimer,this,SLOT(startCron())); //try to start at the beginning of the wall-clock second
219 }
220 
229 {
230  //A sleep loop in case the timer fired early... may want to use QThread::wait() instead
231  QTime now = QTime::currentTime();
232  while (now.msec() != 0)
233  { QThread::usleep(1);
234  now = QTime::currentTime();
235  }
236 
237  _cronID = startTimer(1000,Qt::PreciseTimer);
238 }
239 
248 void VFS_cron::timerEvent(QTimerEvent *event)
249 {
250  Q_UNUSED(event)
251 
252  if (!_crontab.size()) //only if we have events to track
253  return;
254 
255  if (!_subscribers.size()) //only if we have subscribers
256  return;
257 
258  //QDateTime now = QDateTime::currentDateTimeUtc(); //how do we deal with time zones?
259  QDateTime now = QDateTime::currentDateTime();
260  QTime nowt = now.time();
261  QDate nowd = now.date();
262  quint8 second = nowt.second();
263  quint8 minute = nowt.minute();
264  quint8 hour = nowt.hour();
265  quint8 date = nowd.day();
266  quint8 month = nowd.month();
267  quint8 weekday = nowd.dayOfWeek() % 7; // Sunday = 0
268  quint16 year = nowd.year();
269 
270  for( QMultiMap<QString, crontime>::iterator i = _crontab.begin();
271  i != _crontab.end();
272  ++i
273  )
274  {
275  QString event = i.key();
276  crontime time = i.value();
277 
278  if (_subscribers.contains(event)) //only if we have subscribers
279  if (time.matches(second,minute,hour,date,month,weekday,year))
280  {
281  VFS::LOG( QString("%1 event: '%2' %3").arg(className()).arg(event).arg(now.toString()), 9, "cron" );
282 
283  //printf("event: %s\n%s",qUtf8Printable(event),qUtf8Printable(time.toString()));
284 
285  QJsonObject o;
286  o["time"] = QDateTime::currentDateTime().toMSecsSinceEpoch();
287 
288  VFS_request *r = createRequest(VFS_request::diff,event,"cron",QJsonDocument(o));
289  r->_metadata["type"] = "cron";
290  emit diff(this,r);
291  delete r;
292  }
293  }
294 }
295 
300 void VFS_cron::parseCrontab(QString tab)
301 {
302  QStringList entries = tab.split("|",Qt::SkipEmptyParts);
303  QString entry;
304  QString event;
305 
306  for (int i=0;i<entries.length();i++)
307  {
308  QStringList e = entries[i].split(":",Qt::SkipEmptyParts);
309  if (e.length() != 2)
310  VFS::ERROR( QString("%1 bad crontab entry: \"%2\"").arg(className()).arg(entries[i]) );
311  else
312  {
313  event = e[0].trimmed();
314  crontime time(e[1]);
315 
316  if (time.valid())
317  _crontab.insert(event,time);
318  }
319  }
320 }
321 
331 {
332  //FIXME: if two subscriptions to the same event have different crontimes, the second entry
333  // will not be created... _crontab.contains() is not a complete enough test because
334  // of insertMulti below.
335 
336  if (!_crontab.contains(r->_path))
337  if (r->_metadata["createIfMissing"].toBool())
338  {
339  QJsonObject o = r->_data.object();
340 
341  for(QJsonObject::iterator i=o.begin();
342  i!=o.end();
343  ++i
344  )
345  {
346  QString event = i.key();
347  crontime time(i.value().toString());
348 
349  if (time.valid())
350  { _crontab.insert(event,time);
351  //VFS::WARN( QString("Adding crontab entry: %1\n%2").arg(event).arg(time.toString()) );
352  }
353  //else
354  // VFS::WARN( "Not adding invalid crontab entry..." );
355  }
356  }
357 
358  if (_crontab.contains(r->_path) || r->_path == "")
360  else
361  { r->_success = false;
362  r->_reason = QString("%1 unknown event: '%2'. Not subscribing.").arg(className()).arg(r->_path);
363  }
364 }
365 
374 {
375  read(r);
376 }
377 
390 {
391  if (r->_path == "")
392  {
393  QVariantMap o;
394 
395  for( QMultiMap<QString, crontime>::iterator i = _crontab.begin();
396  i != _crontab.end();
397  ++i
398  )
399  {
400  o[i.key()] = false; //entries are not containers, so false.
401  }
402 
403  r->_data = QJsonDocument::fromVariant(o);
404  r->_success = true;
405  return;
406  }
407 
408  if (_crontab.contains(r->_path))
409  {
410  QVariantMap o;
411 
413  o["crontime"] = _crontab.value(r->_path).toCrontime();
414  else if (r->_requestType == VFS_request::ls)
415  { //leave o empty, which is equivalent to having no children
416  }
417 
418  r->_data = QJsonDocument::fromVariant(o);
419  r->_success = true;
420  return;
421  }
422 
423  r->_success = false;
424  r->_reason = QString("%1 unknown path/event: '%2'. Can't read.").arg(className()).arg(r->_path);
425 }
426 
436 {
437  //there's actually nothing to do here... unsubscription based on path will happen automagically
438 
440 }
441 
454 //, _valid(false)
455 {
456  QString c = spec.trimmed();
457  QStringList fields = c.split(QRegExp("\\s+"),Qt::SkipEmptyParts);
458 
459  _valid = true;
460 
461  _seconds = parseField(fields.value(0,"*"));
462  _minutes = parseField(fields.value(1,"*"));
463  _hours = parseField(fields.value(2,"*"));
464  _dates = parseField(fields.value(3,"*"));
465  _months = parseField(fields.value(4,"*"));
466  _weekdays = parseField(fields.value(5,"*"));
467  _years = parseYear(fields.value(6,"*"));
468 
469  _dates[0] = true;
470  _months[0] = true;
471 }
472 
479 quint64 VFS_cron::crontime::parseField(QString field)
480 {
481  quint64 mask = 0;
482 
483  QStringList parts = field.split(",",Qt::SkipEmptyParts);
484 
485  for(int i=0;i<parts.length();i++)
486  mask |= parseRange(parts[i]);
487 
488  return mask;
489 }
490 
497 quint64 VFS_cron::crontime::parseRange(QString range)
498 {
499  quint64 mask = 0;
500 
501  if (range.contains('*'))
502  return mask = 0xFFFFFFFFFFFFFFFF;
503 
504  QStringList r = range.split("-",Qt::SkipEmptyParts);
505 
506  if (r.length() == 1)
507  {
508  bool ok;
509  int v = r[0].trimmed().toInt(&ok);
510  if (ok && v>=0)
511  mask |= (ulong) 1 << v;
512  }
513  else if (r.length() == 2)
514  {
515  bool ok1,ok2;
516  int s = r[0].trimmed().toInt(&ok1);
517  int e = r[1].trimmed().toInt(&ok2);
518  int start = qMin(s,e);
519  int end = qMax(s,e);
520 
521  if (ok1 && ok2 && start>=0 && end>=0)
522  for (int i=start;i<=end;i++)
523  mask |= 1 << i;
524  else
525  { VFS::ERROR( QString("%1 bad crontab range: \"%2\"").arg("crontime").arg(range) );
526  _valid = false;
527  }
528  }
529  else
530  { VFS::ERROR( QString("%1 bad crontab range: \"%2\"").arg("crontime").arg(range) );
531  _valid = false;
532  }
533 
534  return mask;
535 }
536 
543 QList<quint16> VFS_cron::crontime::parseYear(QString y)
544 {
545  QList<quint16> l;
546 
547  if (y.contains("*"))
548  return l; //an empty list matches all years
549 
550  QStringList parts = y.split(",",Qt::SkipEmptyParts);
551  quint16 v;
552 
553  for (int i=0;i<parts.length();i++)
554  {
555  QString part = parts[i];
556  QStringList r = part.split("-",Qt::SkipEmptyParts);
557 
558  if (r.length() == 1)
559  {
560  bool ok;
561  v = r[0].trimmed().toInt(&ok);
562  if (ok)
563  l << v;
564  }
565  else if (r.length() == 2)
566  {
567  bool ok1;
568  bool ok2;
569  int s = r[0].trimmed().toInt(&ok1);
570  int e = r[0].trimmed().toInt(&ok2);
571  int start = qMin(s,e);
572  int end = qMax(s,e);
573 
574  if (ok1 && ok2 && start>=0 && end>=0)
575  for (int i=start; i<=end; i++)
576  l << i;
577  else
578  { VFS::ERROR( QString("%1 bad crontab range: \"%2\"").arg("crontime").arg(part) );
579  _valid = false;
580  }
581  }
582  else
583  { VFS::ERROR( QString("%1 bad crontab range: \"%2\"").arg("crontime").arg(part) );
584  _valid = false;
585  }
586  }
587 
588  return l;
589 }
590 
599 {
600  return _valid;
601 }
602 
614 bool VFS_cron::crontime::matches(quint8 s, quint8 m, quint8 h, quint8 d, quint8 mo, quint8 w, quint16 y)
615 {
616  if (
617  ( _valid ) &&
618  ( _years.isEmpty() || _years.contains(y) ) &&
619  ( _weekdays[w] ) &&
620  ( _months[mo] ) &&
621  ( _dates[d] ) &&
622  ( _hours[h] ) &&
623  ( _minutes[m] ) &&
624  ( _seconds[s] )
625  )
626  return true;
627 
628  return false;
629 }
630 
637 {
638  std::string s;
639 
640  s += "Seconds: "+_seconds.to_string() + "\n";
641  s += "Minutes: "+_minutes.to_string() + "\n";
642  s += "Hours: "+_hours.to_string() + "\n";
643  s += "Dates: "+_dates.to_string() + "\n";
644  s += "Months: "+_months.to_string() + "\n";
645  s += "Weekdays: "+_weekdays.to_string() + "\n";
646 
647  s += "Years: ";
648 
649  QString q = QString::fromStdString(s);
650 
651  if (_years.length())
652  for(int i=0; i<_years.length(); i++)
653  q += QString::number(_years[i]) + (i==_years.length()-1?",":"");
654  else
655  q += "(all years)";
656 
657  q += "\n";
658 
659  return q;
660 }
661 
668 {
669  QString t;
670 
671  t += toCrontimeString("seconds") + " ";
672  t += toCrontimeString("minutes") + " ";
673  t += toCrontimeString("hours") + " ";
674  t += toCrontimeString("dates") + " ";
675  t += toCrontimeString("months") + " ";
676  t += toCrontimeString("weekdays") + " ";
677  t += toCrontimeString("years");
678 
679  return t;
680 }
681 
688 QString VFS_cron::crontime::toCrontimeString(QString which) const
689 {
690  QString s;
691 
692  if (which == "seconds") { if (_seconds.all())
693  s = "*";
694  else
695  for (size_t i=0;i<_seconds.size(); i++)
696  s += QString("%1,").arg(i);
697  }
698 
699  if (which == "minutes") { if (_minutes.all())
700  s = "*";
701  else
702  for (size_t i=0;i<_minutes.size(); i++)
703  s += QString("%1,").arg(i);
704  }
705 
706  if (which == "hours") { if (_hours.all())
707  s = "*";
708  else
709  for (size_t i=0;i<_hours.size(); i++)
710  s += QString("%1,").arg(i);
711  }
712 
713  if (which == "dates") { if (_dates.all())
714  s = "*";
715  else
716  for (size_t i=1;i<_dates.size(); i++)
717  s += QString("%1,").arg(i);
718  }
719 
720  if (which == "months") { if (_months.all())
721  s = "*";
722  else
723  for (size_t i=1;i<_months.size(); i++)
724  s += QString("%1,").arg(i);
725  }
726 
727  if (which == "weekdays"){ if (_weekdays.all())
728  s = "*";
729  else
730  for (size_t i=0;i<_weekdays.size(); i++)
731  s += QString("%1,").arg(i);
732  }
733 
734  if (which == "years") { if (_years.isEmpty())
735  s = "*";
736  else
737  for (int i=0;i<_years.size(); i++)
738  s += QString("%1,").arg(_years[i]);
739  }
740 
741  if (s.endsWith(','))
742  s.chop(1);
743 
744  return s;
745 }
A class to represent a crontime, which can be matched against a (current) time to see if it is active...
Definition: VFS_cron.h:24
bool valid()
Vailidty of a VFS_cron::crontime.
Definition: VFS_cron.cpp:598
std::bitset< 60 > _minutes
bitset of minutes
Definition: VFS_cron.h:37
bool _valid
Is the crontime valid?
Definition: VFS_cron.h:34
crontime(QString time="")
Definition: VFS_cron.cpp:453
QList< quint16 > parseYear(QString y)
Parse the year component of a crontime string.
Definition: VFS_cron.cpp:543
std::bitset< 7 > _weekdays
bitset of weekdays
Definition: VFS_cron.h:41
QString toString() const
A pretty print version of a crontime.
Definition: VFS_cron.cpp:636
quint64 parseRange(QString range)
Parse a range component from a crontime string.
Definition: VFS_cron.cpp:497
std::bitset< 24 > _hours
bitset of hours
Definition: VFS_cron.h:38
QList< quint16 > _years
list of years
Definition: VFS_cron.h:42
std::bitset< 60 > _seconds
bitset of seconds
Definition: VFS_cron.h:36
std::bitset< 32 > _dates
bitset of dates
Definition: VFS_cron.h:39
QString toCrontime() const
Create a parseable crontime string.
Definition: VFS_cron.cpp:667
bool matches(quint8 s, quint8 m, quint8 h, quint8 d, quint8 mo, quint8 w, quint16 y)
Check if a crontime matches.
Definition: VFS_cron.cpp:614
QString toCrontimeString(QString which) const
Generate a crontime string component.
Definition: VFS_cron.cpp:688
quint64 parseField(QString field)
VFS_cron::crontime::parseField.
Definition: VFS_cron.cpp:479
std::bitset< 13 > _months
bitset of months
Definition: VFS_cron.h:40
virtual void unsubscribe(VFS_request *r)
Definition: VFS_cron.cpp:435
virtual QString reportDetails()
Definition: VFS_cron.cpp:162
virtual QByteArray icon()
Return the clock icon from VFS_icons.
Definition: VFS_cron.cpp:194
virtual void parseCrontab(QString tab)
Definition: VFS_cron.cpp:300
virtual bool isContainer()
A VFS_cron node cannot contain children, however listing the contents will return the entry list.
Definition: VFS_cron.cpp:184
virtual void subscribe(VFS_request *r)
Definition: VFS_cron.cpp:330
virtual ~VFS_cron()
Definition: VFS_cron.cpp:126
virtual void timerEvent(QTimerEvent *event)
Scan the event map for entries that match the current time.
Definition: VFS_cron.cpp:248
Q_INVOKABLE VFS_cron(QString crontab)
Definition: VFS_cron.cpp:115
int _cronID
the timerID of the cron node
Definition: VFS_cron.h:53
virtual VFS_node * find(VFS_request *r)
Definition: VFS_cron.cpp:138
virtual void initialize()
Schedule a start for the scheduler.
Definition: VFS_cron.cpp:207
virtual void startCron()
Start the scheduler.
Definition: VFS_cron.cpp:228
virtual void read(VFS_request *r)
Read or list the event entries.
Definition: VFS_cron.cpp:389
virtual void ls(VFS_request *r)
List all entries in the crontab by name.
Definition: VFS_cron.cpp:373
QMultiMap< QString, crontime > _crontab
map of "event:crontime" fields
Definition: VFS_cron.h:52
static char * get(QString which="")
Fetch an icon from the library.
Definition: VFS_icons.cpp:34
VFS_node is the base class from which all other VFS_node classes derive.
Definition: VFS_node.h:143
virtual VFS_request * createRequest(VFS_request::requestType type, QString path, QString user="unknown", QJsonDocument data=QJsonDocument(), QJsonObject metadata=QJsonObject(), bool dontDelete=false)
Create a new VFS_request with this object as _origin.
Definition: VFS_node.cpp:1913
VFS_node * find(QString path)
Find a node by string path.
Definition: VFS_node.cpp:1100
void diff(VFS_node *origin, VFS_request *t)
Emit a diff, which will trigger notifySubscribers() for a mounted node.
QString className()
Return the class name of a node.
Definition: VFS_node.cpp:2039
VFS_subscriptionType _subscribers
This node's subscribers. These subscribers will receive diff notifications.
Definition: VFS_node.h:180
QMutex _lock
A recursive mutex that is local to this node.
Definition: VFS_node.h:178
virtual void unsubscribe(VFS_request *r)
Remove an entry from this node's _subscription list.
Definition: VFS_node.cpp:1263
virtual void subscribe(VFS_request *r)
Add an entry to this node's _subscription list.
Definition: VFS_node.cpp:1204
The base class for all requests between nodes.
Definition: VFS_node.h:54
@ diff
send a diff (7)
Definition: VFS_node.h:71
@ read
read full contents (4)
Definition: VFS_node.h:68
@ ls
list children of a node (1)
Definition: VFS_node.h:65
requestType _requestType
the action this request is performing or requesting
Definition: VFS_node.h:87
QString _reason
if something (probably bad) happened, this is the reason
Definition: VFS_node.h:108
QString _path
the target path remnant... the remaining path element once the request has found its target
Definition: VFS_node.h:95
bool _success
if the request was successfully completed
Definition: VFS_node.h:107
QJsonDocument _data
the request payload
Definition: VFS_node.h:102
QJsonObject _metadata
the request payload
Definition: VFS_node.h:101
static VFS * root()
Return the root node of the VFS filesystem.
Definition: VFS.cpp:399
static void LOG(QString message, int level=0, QString user="server")
Send a message to the VFS::_messages VFS_stream.
Definition: VFS.cpp:209
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
The remoto date object, which has useful features not in the default javascript Date object.