MythTV  master
mythsystemevent.cpp
Go to the documentation of this file.
1 
2 #include <QApplication>
3 #include <QFileInfo>
4 #include <QRunnable>
5 #include <utility>
6 
7 #include "mythcorecontext.h"
8 #include "mthreadpool.h"
9 #include "mythsystemlegacy.h"
10 #include "mythsystemevent.h"
11 #include "programinfo.h"
12 #include "cardutil.h"
13 #include "remoteutil.h"
14 #include "exitcodes.h"
15 #include "mythlogging.h"
16 
17 #define LOC QString("MythSystemEventHandler: ")
18 
28 class SystemEventThread : public QRunnable
29 {
30  public:
37  explicit SystemEventThread(QString cmd, QString eventName = "")
38  : m_command(std::move(cmd)), m_event(std::move(eventName)) {};
39 
45  void run(void) override // QRunnable
46  {
48  uint result = myth_system(m_command, flags);
49 
50  LOG(VB_GENERAL,
51  (result == GENERIC_EXIT_OK ? LOG_INFO : LOG_WARNING), LOC +
52  QString("Finished '%1' result %2")
53  .arg(m_command).arg(result));
54 
55  if (m_event.isEmpty())
56  return;
57 
59  QString("SYSTEM_EVENT_RESULT %1 SENDER %2 RESULT %3")
60  .arg(m_event).arg(gCoreContext->GetHostName()).arg(result));
61  }
62 
63  private:
64  // Private data storage
65  QString m_command;
66  QString m_event;
67 };
68 
69 
76 {
77  setObjectName("MythSystemEventHandler");
79 }
80 
87 {
89 }
90 
104 void MythSystemEventHandler::SubstituteMatches(const QStringList &tokens,
105  QString &command)
106 {
107  if (command.isEmpty())
108  return;
109 
110  LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: BEFORE: %1")
111  .arg(command));
112  QString args;
113  uint chanid = 0;
114  QDateTime recstartts;
115 
116  QStringList::const_iterator it = tokens.begin();
117  ++it;
118  command.replace(QString("%EVENTNAME%"), *it);
119 
120  ++it;
121  while (it != tokens.end())
122  {
123  if (!args.isEmpty())
124  args += " ";
125  args += *it;
126 
127  // Check for some token names that we substitute one for one as
128  // %MATCH% type variables.
129  if ((*it == "CARDID") ||
130  (*it == "COMMAND") ||
131  (*it == "RECSTATUS") ||
132  (*it == "HOSTNAME") ||
133  (*it == "SECS") ||
134  (*it == "SENDER") ||
135  (*it == "PATH") ||
136  (*it == "VIDEODEVICE") ||
137  (*it == "VBIDEVICE"))
138  {
139  QString token = *it;
140 
141  if (++it == tokens.end())
142  break;
143 
144  // The following string is broken up on purpose to indicate
145  // what we're replacing is the token surrounded by percent signs
146  command.replace(QString("%" "%1" "%").arg(token), *it);
147 
148  if (!args.isEmpty())
149  args += " ";
150  args += *it;
151  }
152 
153  // Remember any chanid and starttime so we can lookup info about
154  // the recording from the database.
155  if (*it == "CHANID")
156  {
157  if (++it == tokens.end())
158  break;
159 
160  chanid = (*it).toUInt();
161 
162  if (!args.isEmpty())
163  args += " ";
164  args += *it;
165  }
166 
167  if (*it == "STARTTIME")
168  {
169  if (++it == tokens.end())
170  break;
171 
172  recstartts = MythDate::fromString(*it);
173 
174  if (!args.isEmpty())
175  args += " ";
176  args += *it;
177  }
178 
179  ++it;
180  }
181 
182  command.replace(QString("%ARGS%"), args);
183 
184  // 1st, try loading RecordingInfo / ProgramInfo
185  RecordingInfo recinfo(chanid, recstartts);
186  bool loaded = recinfo.GetChanID() != 0U;
187  if (loaded)
188  recinfo.SubstituteMatches(command);
189  else
190  {
191  // 2rd Try searching for RecordingInfo
193  RecordingInfo recinfo2(chanid, recstartts, false, 0, &status);
194  // cppcheck-suppress knownConditionTrueFalse
195  if (status == RecordingInfo::kFoundProgram)
196  recinfo2.SubstituteMatches(command);
197  else
198  {
199  // 3th just use what we know
200  command.replace(QString("%CHANID%"), QString::number(chanid));
201  command.replace(QString("%STARTTIME%"),
202  MythDate::toString(recstartts,
204  command.replace(QString("%STARTTIMEISO%"),
205  recstartts.toString(Qt::ISODate));
206  }
207  }
208 
209  command.replace(QString("%VERBOSELEVEL%"), QString("%1").arg(verboseMask));
210  command.replace("%VERBOSEMODE%", QString("%1").arg(logPropagateArgs));
211 
212  LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: AFTER : %1")
213  .arg(command));
214 }
215 
225 QString MythSystemEventHandler::EventNameToSetting(const QString &name)
226 {
227  QString result("EventCmd");
228 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
229  QStringList parts = name.toLower().split('_', QString::SkipEmptyParts);
230 #else
231  QStringList parts = name.toLower().split('_', Qt::SkipEmptyParts);
232 #endif
233 
234  for (const auto & part : qAsConst(parts))
235  {
236  result += part.at(0).toUpper();
237  result += part.midRef(1);
238  }
239 
240  return result;
241 }
242 
259 {
260  if (e->type() == MythEvent::MythEventMessage)
261  {
262  auto *me = dynamic_cast<MythEvent *>(e);
263  if (me == nullptr)
264  return;
265  QString msg = me->Message().simplified();
266 
267  if (msg == "CLEAR_SETTINGS_CACHE")
268  msg = "SYSTEM_EVENT SETTINGS_CACHE_CLEARED";
269 
270  // Listen for any GLOBAL_SYSTEM_EVENT messages and resend to
271  // the master backend as regular SYSTEM_EVENT messages.
272  if (msg.startsWith("GLOBAL_SYSTEM_EVENT "))
273  {
274  gCoreContext->SendMessage(msg.mid(7) +
275  QString(" SENDER %1").arg(gCoreContext->GetHostName()));
276  return;
277  }
278 
279  if ((!msg.startsWith("SYSTEM_EVENT ")) &&
280  (!msg.startsWith("LOCAL_SYSTEM_EVENT ")))
281  return;
282 
283 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
284  QStringList tokens = msg.split(' ', QString::SkipEmptyParts);
285 #else
286  QStringList tokens = msg.split(' ', Qt::SkipEmptyParts);
287 #endif
288 
289  // Return if this event is for another host
290  if ((tokens.size() >= 4) &&
291  (tokens[2] == "HOST") &&
292  (tokens[3] != gCoreContext->GetHostName()))
293  return;
294 
295  QString cmd;
296 
297  // See if this system has a command that runs for all system events
298  cmd = gCoreContext->GetSetting("EventCmdAll");
299  if (!cmd.isEmpty())
300  {
301  SubstituteMatches(tokens, cmd);
302 
303  auto *eventThread = new SystemEventThread(cmd);
305  eventThread, "SystemEvent");
306  }
307 
308  // Check for an EventCmd for this particular event
309  cmd = gCoreContext->GetSetting(EventNameToSetting(tokens[1]));
310  if (!cmd.isEmpty())
311  {
312  SubstituteMatches(tokens, cmd);
313 
314  LOG(VB_GENERAL, LOG_INFO, LOC +
315  QString("Starting thread for command '%1'").arg(cmd));
316 
317  auto *eventThread = new SystemEventThread(cmd, tokens[1]);
319  eventThread, "SystemEvent");
320  }
321  }
322 }
323 
324 /****************************************************************************/
325 
332 void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
333 {
334  if (pginfo)
335  {
336  uint cardid = pginfo->GetInputID();
338  QString("%1 CARDID %2 CHANID %3 STARTTIME %4 RECSTATUS %5 "
339  "VIDEODEVICE %6 VBIDEVICE %7")
340  .arg(msg).arg(cardid)
341  .arg(pginfo->GetChanID())
343  .arg(pginfo->GetRecordingStatus())
344  .arg(CardUtil::GetVideoDevice(cardid))
345  .arg(CardUtil::GetVBIDevice(cardid)));
346  }
347  else
348  {
349  LOG(VB_GENERAL, LOG_ERR, LOC +
350  "SendMythSystemRecEvent() called with empty RecordingInfo");
351  }
352 }
353 
360 void SendMythSystemPlayEvent(const QString &msg, const ProgramInfo *pginfo)
361 {
362  if (pginfo)
363  {
365  QString("%1 HOSTNAME %2 CHANID %3 STARTTIME %4")
366  .arg(msg).arg(gCoreContext->GetHostName())
367  .arg(pginfo->GetChanID())
369  }
370  else
371  {
372  LOG(VB_GENERAL, LOG_ERR, LOC + "SendMythSystemPlayEvent() called with "
373  "empty ProgramInfo");
374  }
375 }
376 
377 /****************************************************************************/
378 
391  const char *name)
392  : RawSettingsEditor(parent, name)
393 {
394  m_title = tr("System Event Command Editor");
395 
396  // Event names are programmatically converted to settings names in
397  // EventNameToSetting(). For convenience of searching the code
398  // base, the event names are listed in comments.
399  m_settings["EventCmdRecPending"] = // REC_PENDING
400  tr("Recording pending");
401  m_settings["EventCmdRecPreFail"] = // REC_PREFAIL
402  tr("Recording about to fail");
403  m_settings["EventCmdRecFailing"] = // REC_FAILING
404  tr("Recording failing");
405  m_settings["EventCmdRecStarted"] = // REC_STARTED
406  tr("Recording started");
407  m_settings["EventCmdRecStartedWriting"] = // REC_STARTED_WRITING
408  tr("Recording started writing");
409  m_settings["EventCmdRecFinished"] = // REC_FINISHED
410  tr("Recording finished");
411  m_settings["EventCmdRecDeleted"] = // REC_DELETED
412  tr("Recording deleted");
413  m_settings["EventCmdRecExpired"] = // REC_EXPIRED
414  tr("Recording expired");
415  m_settings["EventCmdLivetvStarted"] = // LIVETV_STARTED
416  tr("LiveTV started");
417  m_settings["EventCmdLivetvEnded"] = // LIVETV_ENDED
418  tr("LiveTV ended");
419  m_settings["EventCmdPlayStarted"] = // PLAY_STARTED
420  tr("Playback started");
421  m_settings["EventCmdPlayStopped"] = // PLAY_STOPPED
422  tr("Playback stopped");
423  m_settings["EventCmdPlayPaused"] = // PLAY_PAUSED
424  tr("Playback paused");
425  m_settings["EventCmdPlayUnpaused"] = // PLAY_UNPAUSED
426  tr("Playback unpaused");
427  m_settings["EventCmdPlayChanged"] = // PLAY_CHANGED
428  tr("Playback program changed");
429  m_settings["EventCmdTuningSignalTimeout"] = // TUNING_SIGNAL_TIMEOUT
430  tr("Tuning signal waiting");
431  m_settings["EventCmdMasterStarted"] = // MASTER_STARTED
432  tr("Master backend started");
433  m_settings["EventCmdMasterShutdown"] = // MASTER_SHUTDOWN
434  tr("Master backend shutdown");
435  m_settings["EventCmdClientConnected"] = // CLIENT_CONNECTED
436  tr("Client connected to master backend");
437  m_settings["EventCmdClientDisconnected"] = // CLIENT_DISCONNECTED
438  tr("Client disconnected from master backend");
439  m_settings["EventCmdSlaveConnected"] = // SLAVE_CONNECTED
440  tr("Slave backend connected to master");
441  m_settings["EventCmdSlaveDisconnected"] = // SLAVE_DISCONNECTED
442  tr("Slave backend disconnected from master");
443  m_settings["EventCmdNetCtrlConnected"] = // NET_CTRL_CONNECTED
444  tr("Network Control client connected");
445  m_settings["EventCmdNetCtrlDisconnected"] = // NET_CTRL_DISCONNECTED
446  tr("Network Control client disconnected");
447  m_settings["EventCmdMythfilldatabaseRan"] = // MYTHFILLDATABASE_RAN
448  tr("mythfilldatabase ran");
449  m_settings["EventCmdSchedulerRan"] = // SCHEDULER_RAN
450  tr("Scheduler ran");
451  m_settings["EventCmdSettingsCacheCleared"] = // SETTINGS_CACHE_CLEARED
452  tr("Settings cache cleared");
453  m_settings["EventCmdScreenType"] = // SCREEN_TYPE
454  tr("Screen created or destroyed");
455  m_settings["EventCmdKey01"] = // KEY_%1
456  tr("Keystroke event #1");
457  m_settings["EventCmdKey02"] = // KEY_%1
458  tr("Keystroke event #2");
459  m_settings["EventCmdKey03"] = // KEY_%1
460  tr("Keystroke event #3");
461  m_settings["EventCmdKey04"] = // KEY_%1
462  tr("Keystroke event #4");
463  m_settings["EventCmdKey05"] = // KEY_%1
464  tr("Keystroke event #5");
465  m_settings["EventCmdKey06"] = // KEY_%1
466  tr("Keystroke event #6");
467  m_settings["EventCmdKey07"] = // KEY_%1
468  tr("Keystroke event #7");
469  m_settings["EventCmdKey08"] = // KEY_%1
470  tr("Keystroke event #8");
471  m_settings["EventCmdKey09"] = // KEY_%1
472  tr("Keystroke event #9");
473  m_settings["EventCmdKey10"] = // KEY_%1
474  tr("Keystroke event #10");
475  m_settings["EventCmdCecCommandReceived"] = // CEC_COMMAND_RECEIVED
476  tr("CEC command received");
477  m_settings["EventCmdAll"] = // EventCmdAll
478  tr("Any event");
479 }
480 
481 /* vim: set expandtab tabstop=4 shiftwidth=4: */
build_compdb.args
args
Definition: build_compdb.py:11
MythSystemEventHandler::MythSystemEventHandler
MythSystemEventHandler()
Null Constructor.
Definition: mythsystemevent.cpp:75
e
QDomElement e
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1420
MythCoreContext::SendMessage
void SendMessage(const QString &message)
Definition: mythcorecontext.cpp:1527
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:80
MythEvent::MythEventMessage
static Type MythEventMessage
Definition: mythevent.h:73
myth_system
uint myth_system(const QString &command, uint flags, uint timeout)
Definition: mythsystemlegacy.cpp:501
GENERIC_EXIT_OK
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
RecordingInfo::kFoundProgram
@ kFoundProgram
Definition: recordinginfo.h:181
kMSDontBlockInputDevs
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
RecordingInfo
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:35
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:17
RawSettingsEditor
An editor screen that allows manipulation of raw settings values.
Definition: rawsettingseditor.h:26
MythScreenStack
Definition: mythscreenstack.h:16
arg
arg(title).arg(filename).arg(doDelete))
RawSettingsEditor::m_title
QString m_title
Definition: rawsettingseditor.h:51
LOC
#define LOC
Definition: mythsystemevent.cpp:17
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythSystemEventHandler::SubstituteMatches
static void SubstituteMatches(const QStringList &tokens, QString &command)
Substitutes MATCH% variables in given command line.
Definition: mythsystemevent.cpp:104
mythsystemevent.h
remoteutil.h
MythEvent::Message
const QString & Message() const
Definition: mythevent.h:65
ProgramInfo::GetRecordingStartTime
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:402
SystemEventThread
QRunnable class for running MythSystemEvent handler commands.
Definition: mythsystemevent.cpp:29
mythsystemlegacy.h
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
MythSystemEventHandler::EventNameToSetting
static QString EventNameToSetting(const QString &name)
Convert an MythSystemEvent name to a database setting name.
Definition: mythsystemevent.cpp:225
programinfo.h
mythlogging.h
ProgramInfo::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:445
MythDate::kFilename
@ kFilename
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
MythCoreContext::SendSystemEvent
void SendSystemEvent(const QString &msg)
Definition: mythcorecontext.cpp:1554
SystemEventThread::SystemEventThread
SystemEventThread(QString cmd, QString eventName="")
Constructor for creating a SystemEventThread.
Definition: mythsystemevent.cpp:37
verboseMask
uint64_t verboseMask
Definition: logging.cpp:102
uint
unsigned int uint
Definition: compat.h:141
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
token
return token
Definition: musicutils.cpp:74
RecordingInfo::LoadStatus
LoadStatus
Definition: recordinginfo.h:179
RecordingInfo::kNoProgram
@ kNoProgram
Definition: recordinginfo.h:180
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
SystemEventThread::m_event
QString m_event
Definition: mythsystemevent.cpp:66
ProgramInfo::GetInputID
uint GetInputID(void) const
Definition: programinfo.h:461
mthreadpool.h
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:370
SystemEventThread::m_command
QString m_command
Definition: mythsystemevent.cpp:65
RecordingInfo::SubstituteMatches
void SubstituteMatches(QString &str) override
Replace MATCH% vars in the specified string.
Definition: recordinginfo.cpp:1588
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:68
RawSettingsEditor::m_settings
QMap< QString, QString > m_settings
Definition: rawsettingseditor.h:52
mythcorecontext.h
SendMythSystemPlayEvent
void SendMythSystemPlayEvent(const QString &msg, const ProgramInfo *pginfo)
Definition: mythsystemevent.cpp:360
MythSystemEventHandler::customEvent
void customEvent(QEvent *e) override
Custom Event handler for receiving and processing System Events.
Definition: mythsystemevent.cpp:258
cardutil.h
MythSystemEventEditor::MythSystemEventEditor
MythSystemEventEditor(MythScreenStack *parent, const char *name=nullptr)
Constructor for the MythSystemEvent settings editor.
Definition: mythsystemevent.cpp:390
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:14
CardUtil::GetVBIDevice
static QString GetVBIDevice(uint inputid)
Definition: cardutil.h:287
SendMythSystemRecEvent
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
Definition: mythsystemevent.cpp:332
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:859
SystemEventThread::run
void run(void) override
Runs the System Event handler command.
Definition: mythsystemevent.cpp:45
logPropagateArgs
QString logPropagateArgs
Definition: logging.cpp:87
MythSystemEventHandler::~MythSystemEventHandler
~MythSystemEventHandler() override
Destructor.
Definition: mythsystemevent.cpp:86
exitcodes.h
MThreadPool::globalInstance
static MThreadPool * globalInstance(void)
Definition: mthreadpool.cpp:306
MThreadPool::startReserved
void startReserved(QRunnable *runnable, const QString &debugName, int waitForAvailMS=0)
Definition: mthreadpool.cpp:360
MythObservable::removeListener
void removeListener(QObject *listener)
Remove a listener to the observable.
Definition: mythobservable.cpp:55
CardUtil::GetVideoDevice
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:283
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:919