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