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