MythTV  master
mythsystemevent.cpp
Go to the documentation of this file.
1 
2 #include <QApplication>
3 #include <QFileInfo>
4 #include <QRunnable>
5 
6 #include "mythcorecontext.h"
7 #include "mthreadpool.h"
8 #include "mythsystemlegacy.h"
9 #include "mythsystemevent.h"
10 #include "programinfo.h"
11 #include "cardutil.h"
12 #include "remoteutil.h"
13 #include "exitcodes.h"
14 #include "mythlogging.h"
15 
16 #define LOC QString("MythSystemEventHandler: ")
17 
27 class SystemEventThread : public QRunnable
28 {
29  public:
36  SystemEventThread(const QString &cmd, QString eventName = "")
37  : m_command(cmd), m_event(eventName) {};
38 
44  void run(void) override // QRunnable
45  {
47  uint result = myth_system(m_command, flags);
48 
49  LOG(VB_GENERAL,
50  (result == GENERIC_EXIT_OK ? LOG_INFO : LOG_WARNING), LOC +
51  QString("Finished '%1' result %2")
52  .arg(m_command).arg(result));
53 
54  if (m_event.isEmpty())
55  return;
56 
58  QString("SYSTEM_EVENT_RESULT %1 SENDER %2 RESULT %3")
59  .arg(m_event).arg(gCoreContext->GetHostName()).arg(result));
60  }
61 
62  private:
63  // Private data storage
64  QString m_command;
65  QString m_event;
66 };
67 
68 
75 {
76  setObjectName("MythSystemEventHandler");
78 }
79 
86 {
88 }
89 
103 void MythSystemEventHandler::SubstituteMatches(const QStringList &tokens,
104  QString &command)
105 {
106  if (command.isEmpty())
107  return;
108 
109  LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: BEFORE: %1")
110  .arg(command));
111  QString args;
112  uint chanid = 0;
113  QDateTime recstartts;
114  QString sender;
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  if (token == "SENDER")
145  sender = *it;
146 
147  // The following string is broken up on purpose to indicate
148  // what we're replacing is the token surrounded by percent signs
149  command.replace(QString("%" "%1" "%").arg(token), *it);
150 
151  if (!args.isEmpty())
152  args += " ";
153  args += *it;
154  }
155 
156  // Remember any chanid and starttime so we can lookup info about
157  // the recording from the database.
158  if (*it == "CHANID")
159  {
160  if (++it == tokens.end())
161  break;
162 
163  chanid = (*it).toUInt();
164 
165  if (!args.isEmpty())
166  args += " ";
167  args += *it;
168  }
169 
170  if (*it == "STARTTIME")
171  {
172  if (++it == tokens.end())
173  break;
174 
175  recstartts = MythDate::fromString(*it);
176 
177  if (!args.isEmpty())
178  args += " ";
179  args += *it;
180  }
181 
182  ++it;
183  }
184 
185  command.replace(QString("%ARGS%"), args);
186 
187  // 1st, try loading RecordingInfo / ProgramInfo
188  RecordingInfo recinfo(chanid, recstartts);
189  bool loaded = recinfo.GetChanID();
190  if (loaded)
191  recinfo.SubstituteMatches(command);
192  else
193  {
194  // 2rd Try searching for RecordingInfo
196  RecordingInfo recinfo2(chanid, recstartts, false, 0, &status);
197  if (status == RecordingInfo::kFoundProgram)
198  recinfo2.SubstituteMatches(command);
199  else
200  {
201  // 3th just use what we know
202  command.replace(QString("%CHANID%"), QString::number(chanid));
203  command.replace(QString("%STARTTIME%"),
204  MythDate::toString(recstartts,
206  command.replace(QString("%STARTTIMEISO%"),
207  recstartts.toString(Qt::ISODate));
208  }
209  }
210 
211  command.replace(QString("%VERBOSELEVEL%"), QString("%1").arg(verboseMask));
212 
213  LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: AFTER : %1")
214  .arg(command));
215 }
216 
227 {
228  QString result("EventCmd");
229  QStringList parts = name.toLower().split('_', QString::SkipEmptyParts);
230 
231  QStringList::Iterator it = parts.begin();
232  while (it != parts.end())
233  {
234  result += (*it).left(1).toUpper();
235  result += (*it).mid(1);
236 
237  ++it;
238  }
239 
240  return result;
241 }
242 
259 {
260  if (e->type() == MythEvent::MythEventMessage)
261  {
262  MythEvent *me = static_cast<MythEvent *>(e);
263  QString msg = me->Message().simplified();
264 
265  if (msg == "CLEAR_SETTINGS_CACHE")
266  msg = "SYSTEM_EVENT SETTINGS_CACHE_CLEARED";
267 
268  // Listen for any GLOBAL_SYSTEM_EVENT messages and resend to
269  // the master backend as regular SYSTEM_EVENT messages.
270  if (msg.startsWith("GLOBAL_SYSTEM_EVENT "))
271  {
272  gCoreContext->SendMessage(msg.mid(7) +
273  QString(" SENDER %1").arg(gCoreContext->GetHostName()));
274  return;
275  }
276 
277  if ((!msg.startsWith("SYSTEM_EVENT ")) &&
278  (!msg.startsWith("LOCAL_SYSTEM_EVENT ")))
279  return;
280 
281  QStringList tokens = msg.split(' ', QString::SkipEmptyParts);
282 
283  // Return if this event is for another host
284  if ((tokens.size() >= 4) &&
285  (tokens[2] == "HOST") &&
286  (tokens[3] != gCoreContext->GetHostName()))
287  return;
288 
289  QString cmd;
290 
291  // See if this system has a command that runs for all system events
292  cmd = gCoreContext->GetSetting("EventCmdAll");
293  if (!cmd.isEmpty())
294  {
295  SubstituteMatches(tokens, cmd);
296 
297  SystemEventThread *eventThread = new SystemEventThread(cmd);
299  eventThread, "SystemEvent");
300  }
301 
302  // Check for an EventCmd for this particular event
303  cmd = gCoreContext->GetSetting(EventNameToSetting(tokens[1]));
304  if (!cmd.isEmpty())
305  {
306  SubstituteMatches(tokens, cmd);
307 
308  LOG(VB_GENERAL, LOG_INFO, LOC +
309  QString("Starting thread for command '%1'").arg(cmd));
310 
311  SystemEventThread *eventThread =
312  new SystemEventThread(cmd, tokens[1]);
314  eventThread, "SystemEvent");
315  }
316  }
317 }
318 
319 /****************************************************************************/
320 
327 void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
328 {
329  if (pginfo)
330  {
331  uint cardid = pginfo->GetInputID();
333  QString("%1 CARDID %2 CHANID %3 STARTTIME %4 RECSTATUS %5 "
334  "VIDEODEVICE %6 VBIDEVICE %7")
335  .arg(msg).arg(cardid)
336  .arg(pginfo->GetChanID())
338  .arg(pginfo->GetRecordingStatus())
339  .arg(CardUtil::GetVideoDevice(cardid))
340  .arg(CardUtil::GetVBIDevice(cardid)));
341  }
342  else
343  {
344  LOG(VB_GENERAL, LOG_ERR, LOC +
345  "SendMythSystemRecEvent() called with empty RecordingInfo");
346  }
347 }
348 
355 void SendMythSystemPlayEvent(const QString &msg, const ProgramInfo *pginfo)
356 {
357  if (pginfo)
359  QString("%1 HOSTNAME %2 CHANID %3 STARTTIME %4")
360  .arg(msg).arg(gCoreContext->GetHostName())
361  .arg(pginfo->GetChanID())
363  else
364  LOG(VB_GENERAL, LOG_ERR, LOC + "SendMythSystemPlayEvent() called with "
365  "empty ProgramInfo");
366 }
367 
368 /****************************************************************************/
369 
382  const char *name)
383  : RawSettingsEditor(parent, name)
384 {
385  m_title = tr("System Event Command Editor");
386 
387  // Event names are programmatically converted to settings names in
388  // EventNameToSetting(). For convenience of searching the code
389  // base, the event names are listed in comments.
390  m_settings["EventCmdRecPending"] = // REC_PENDING
391  tr("Recording pending");
392  m_settings["EventCmdRecPreFail"] = // REC_PREFAIL
393  tr("Recording about to fail");
394  m_settings["EventCmdRecFailing"] = // REC_FAILING
395  tr("Recording failing");
396  m_settings["EventCmdRecStarted"] = // REC_STARTED
397  tr("Recording started");
398  m_settings["EventCmdRecStartedWriting"] = // REC_STARTED_WRITING
399  tr("Recording started writing");
400  m_settings["EventCmdRecFinished"] = // REC_FINISHED
401  tr("Recording finished");
402  m_settings["EventCmdRecDeleted"] = // REC_DELETED
403  tr("Recording deleted");
404  m_settings["EventCmdRecExpired"] = // REC_EXPIRED
405  tr("Recording expired");
406  m_settings["EventCmdLivetvStarted"] = // LIVETV_STARTED
407  tr("LiveTV started");
408  m_settings["EventCmdLivetvEnded"] = // LIVETV_ENDED
409  tr("LiveTV ended");
410  m_settings["EventCmdPlayStarted"] = // PLAY_STARTED
411  tr("Playback started");
412  m_settings["EventCmdPlayStopped"] = // PLAY_STOPPED
413  tr("Playback stopped");
414  m_settings["EventCmdPlayPaused"] = // PLAY_PAUSED
415  tr("Playback paused");
416  m_settings["EventCmdPlayUnpaused"] = // PLAY_UNPAUSED
417  tr("Playback unpaused");
418  m_settings["EventCmdPlayChanged"] = // PLAY_CHANGED
419  tr("Playback program changed");
420  m_settings["EventCmdTuningSignalTimeout"] = // TUNING_SIGNAL_TIMEOUT
421  tr("Tuning signal waiting");
422  m_settings["EventCmdMasterStarted"] = // MASTER_STARTED
423  tr("Master backend started");
424  m_settings["EventCmdMasterShutdown"] = // MASTER_SHUTDOWN
425  tr("Master backend shutdown");
426  m_settings["EventCmdClientConnected"] = // CLIENT_CONNECTED
427  tr("Client connected to master backend");
428  m_settings["EventCmdClientDisconnected"] = // CLIENT_DISCONNECTED
429  tr("Client disconnected from master backend");
430  m_settings["EventCmdSlaveConnected"] = // SLAVE_CONNECTED
431  tr("Slave backend connected to master");
432  m_settings["EventCmdSlaveDisconnected"] = // SLAVE_DISCONNECTED
433  tr("Slave backend disconnected from master");
434  m_settings["EventCmdNetCtrlConnected"] = // NET_CTRL_CONNECTED
435  tr("Network Control client connected");
436  m_settings["EventCmdNetCtrlDisconnected"] = // NET_CTRL_DISCONNECTED
437  tr("Network Control client disconnected");
438  m_settings["EventCmdMythfilldatabaseRan"] = // MYTHFILLDATABASE_RAN
439  tr("mythfilldatabase ran");
440  m_settings["EventCmdSchedulerRan"] = // SCHEDULER_RAN
441  tr("Scheduler ran");
442  m_settings["EventCmdSettingsCacheCleared"] = // SETTINGS_CACHE_CLEARED
443  tr("Settings cache cleared");
444  m_settings["EventCmdScreenType"] = // SCREEN_TYPE
445  tr("Screen created or destroyed");
446  m_settings["EventCmdKey01"] = // KEY_%1
447  tr("Keystroke event #1");
448  m_settings["EventCmdKey02"] = // KEY_%1
449  tr("Keystroke event #2");
450  m_settings["EventCmdKey03"] = // KEY_%1
451  tr("Keystroke event #3");
452  m_settings["EventCmdKey04"] = // KEY_%1
453  tr("Keystroke event #4");
454  m_settings["EventCmdKey05"] = // KEY_%1
455  tr("Keystroke event #5");
456  m_settings["EventCmdKey06"] = // KEY_%1
457  tr("Keystroke event #6");
458  m_settings["EventCmdKey07"] = // KEY_%1
459  tr("Keystroke event #7");
460  m_settings["EventCmdKey08"] = // KEY_%1
461  tr("Keystroke event #8");
462  m_settings["EventCmdKey09"] = // KEY_%1
463  tr("Keystroke event #9");
464  m_settings["EventCmdKey10"] = // KEY_%1
465  tr("Keystroke event #10");
466  m_settings["EventCmdCecCommandReceived"] = // CEC_COMMAND_RECEIVED
467  tr("CEC command received");
468  m_settings["EventCmdAll"] = // EventCmdAll
469  tr("Any event");
470 }
471 
472 /* 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
void startReserved(QRunnable *runnable, QString debugName, int waitForAvailMS=0)
#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:65
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.
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:106
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:15
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:272
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
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:396
static QString GetVBIDevice(uint inputid)
Definition: cardutil.h:276
const QString & Message() const
Definition: mythevent.h:57
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