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
12
13#include "cardutil.h"
14#include "mythsystemevent.h"
15#include "programinfo.h"
16
17#define LOC QString("MythSystemEventHandler: ")
18
28class 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")
61 QString::number(result)));
62 }
63
64 private:
65 // Private data storage
66 QString m_command;
67 QString m_event;
68};
69
70
77{
78 setObjectName("MythSystemEventHandler");
80}
81
88{
90}
91
105void MythSystemEventHandler::SubstituteMatches(const QStringList &tokens,
106 QString &command)
107{
108 if (command.isEmpty())
109 return;
110
111 LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: BEFORE: %1")
112 .arg(command));
113 QString args;
114 uint chanid = 0;
115 QDateTime recstartts;
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 // The following string is broken up on purpose to indicate
146 // what we're replacing is the token surrounded by percent signs
147 command.replace(QString("%" "%1" "%").arg(token), *it);
148
149 if (!args.isEmpty())
150 args += " ";
151 args += *it;
152 }
153
154 // Remember any chanid and starttime so we can lookup info about
155 // the recording from the database.
156 if (*it == "CHANID")
157 {
158 if (++it == tokens.end())
159 break;
160
161 chanid = (*it).toUInt();
162
163 if (!args.isEmpty())
164 args += " ";
165 args += *it;
166 }
167
168 if (*it == "STARTTIME")
169 {
170 if (++it == tokens.end())
171 break;
172
173 recstartts = MythDate::fromString(*it);
174
175 if (!args.isEmpty())
176 args += " ";
177 args += *it;
178 }
179
180 ++it;
181 }
182
183 command.replace(QString("%ARGS%"), args);
184
185 // 1st, try loading RecordingInfo / ProgramInfo
186 RecordingInfo recinfo(chanid, recstartts);
187 bool loaded = recinfo.GetChanID() != 0U;
188 if (loaded)
189 recinfo.SubstituteMatches(command);
190 else
191 {
192 // 2rd Try searching for RecordingInfo
194 RecordingInfo recinfo2(chanid, recstartts, false, 0h, &status);
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
226{
227 QString result("EventCmd");
228 QStringList parts = name.toLower().split('_', Qt::SkipEmptyParts);
229 for (const auto & part : std::as_const(parts))
230 {
231 result += part.at(0).toUpper();
232 result += part.mid(1);
233 }
234
235 return result;
236}
237
254{
255 if (e->type() == MythEvent::kMythEventMessage)
256 {
257 auto *me = dynamic_cast<MythEvent *>(e);
258 if (me == nullptr)
259 return;
260 QString msg = me->Message().simplified();
261
262 if (msg == "CLEAR_SETTINGS_CACHE")
263 msg = "SYSTEM_EVENT SETTINGS_CACHE_CLEARED";
264
265 // Listen for any GLOBAL_SYSTEM_EVENT messages and resend to
266 // the master backend as regular SYSTEM_EVENT messages.
267 if (msg.startsWith("GLOBAL_SYSTEM_EVENT "))
268 {
269 gCoreContext->SendMessage(msg.mid(7) +
270 QString(" SENDER %1").arg(gCoreContext->GetHostName()));
271 return;
272 }
273
274 if ((!msg.startsWith("SYSTEM_EVENT ")) &&
275 (!msg.startsWith("LOCAL_SYSTEM_EVENT ")))
276 return;
277
278 QStringList tokens = msg.split(' ', Qt::SkipEmptyParts);
279
280 // Return if this event is for another host
281 if ((tokens.size() >= 4) &&
282 (tokens[2] == "HOST") &&
283 (tokens[3] != gCoreContext->GetHostName()))
284 return;
285
286 QString cmd;
287
288 // See if this system has a command that runs for all system events
289 cmd = gCoreContext->GetSetting("EventCmdAll");
290 if (!cmd.isEmpty())
291 {
292 SubstituteMatches(tokens, cmd);
293
294 auto *eventThread = new SystemEventThread(cmd);
296 eventThread, "SystemEvent");
297 }
298
299 // Check for an EventCmd for this particular event
300 cmd = gCoreContext->GetSetting(EventNameToSetting(tokens[1]));
301 if (!cmd.isEmpty())
302 {
303 SubstituteMatches(tokens, cmd);
304
305 LOG(VB_GENERAL, LOG_INFO, LOC +
306 QString("Starting thread for command '%1'").arg(cmd));
307
308 auto *eventThread = new SystemEventThread(cmd, tokens[1]);
310 eventThread, "SystemEvent");
311 }
312 }
313}
314
315/****************************************************************************/
316
323void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
324{
325 if (pginfo)
326 {
327 uint cardid = pginfo->GetInputID();
329 QString("%1 CARDID %2 CHANID %3 STARTTIME %4 RECSTATUS %5 "
330 "VIDEODEVICE %6 VBIDEVICE %7")
331 .arg(msg).arg(cardid)
332 .arg(pginfo->GetChanID())
334 .arg(pginfo->GetRecordingStatus())
335 .arg(CardUtil::GetVideoDevice(cardid),
336 CardUtil::GetVBIDevice(cardid)));
337 }
338 else
339 {
340 LOG(VB_GENERAL, LOG_ERR, LOC +
341 "SendMythSystemRecEvent() called with empty RecordingInfo");
342 }
343}
344
351void SendMythSystemPlayEvent(const QString &msg, const ProgramInfo *pginfo)
352{
353 if (pginfo)
354 {
356 QString("%1 HOSTNAME %2 CHANID %3 STARTTIME %4")
357 .arg(msg, gCoreContext->GetHostName())
358 .arg(pginfo->GetChanID())
360 }
361 else
362 {
363 LOG(VB_GENERAL, LOG_ERR, LOC + "SendMythSystemPlayEvent() called with "
364 "empty ProgramInfo");
365 }
366}
367
368/****************************************************************************/
369
382 const char *name)
383 : RawSettingsEditor(parent, name)
384{
385 m_title = tr("System Event Command Editor");
387
388}
389 void MythSystemEventEditor::createSettingList(QMap <QString, QString> &settings)
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 settings["EventCmdRecPending"] = // REC_PENDING
395 tr("Recording pending");
396 settings["EventCmdRecPreFail"] = // REC_PREFAIL
397 tr("Recording about to fail");
398 settings["EventCmdRecFailing"] = // REC_FAILING
399 tr("Recording failing");
400 settings["EventCmdRecStarted"] = // REC_STARTED
401 tr("Recording started");
402 settings["EventCmdRecStartedWriting"] = // REC_STARTED_WRITING
403 tr("Recording started writing");
404 settings["EventCmdRecFinished"] = // REC_FINISHED
405 tr("Recording finished");
406 settings["EventCmdRecDeleted"] = // REC_DELETED
407 tr("Recording deleted");
408 settings["EventCmdRecExpired"] = // REC_EXPIRED
409 tr("Recording expired");
410 settings["EventCmdLivetvStarted"] = // LIVETV_STARTED
411 tr("LiveTV started");
412 settings["EventCmdLivetvEnded"] = // LIVETV_ENDED
413 tr("LiveTV ended");
414 settings["EventCmdPlayStarted"] = // PLAY_STARTED
415 tr("Playback started");
416 settings["EventCmdPlayStopped"] = // PLAY_STOPPED
417 tr("Playback stopped");
418 settings["EventCmdPlayPaused"] = // PLAY_PAUSED
419 tr("Playback paused");
420 settings["EventCmdPlayUnpaused"] = // PLAY_UNPAUSED
421 tr("Playback unpaused");
422 settings["EventCmdPlayChanged"] = // PLAY_CHANGED
423 tr("Playback program changed");
424 settings["EventCmdTuningSignalTimeout"] = // TUNING_SIGNAL_TIMEOUT
425 tr("Tuning signal waiting");
426 settings["EventCmdMasterStarted"] = // MASTER_STARTED
427 tr("Master backend started");
428 settings["EventCmdMasterShutdown"] = // MASTER_SHUTDOWN
429 tr("Master backend shutdown");
430 settings["EventCmdClientConnected"] = // CLIENT_CONNECTED
431 tr("Client connected to master backend");
432 settings["EventCmdClientDisconnected"] = // CLIENT_DISCONNECTED
433 tr("Client disconnected from master backend");
434 settings["EventCmdSlaveConnected"] = // SLAVE_CONNECTED
435 tr("Slave backend connected to master");
436 settings["EventCmdSlaveDisconnected"] = // SLAVE_DISCONNECTED
437 tr("Slave backend disconnected from master");
438 settings["EventCmdNetCtrlConnected"] = // NET_CTRL_CONNECTED
439 tr("Network Control client connected");
440 settings["EventCmdNetCtrlDisconnected"] = // NET_CTRL_DISCONNECTED
441 tr("Network Control client disconnected");
442 settings["EventCmdMythfilldatabaseRan"] = // MYTHFILLDATABASE_RAN
443 tr("mythfilldatabase ran");
444 settings["EventCmdSchedulerRan"] = // SCHEDULER_RAN
445 tr("Scheduler ran");
446 settings["EventCmdSettingsCacheCleared"] = // SETTINGS_CACHE_CLEARED
447 tr("Settings cache cleared");
448 settings["EventCmdScreenType"] = // SCREEN_TYPE
449 tr("Screen created or destroyed");
450 settings["EventCmdKey01"] = // KEY_%1
451 tr("Keystroke event #1");
452 settings["EventCmdKey02"] = // KEY_%1
453 tr("Keystroke event #2");
454 settings["EventCmdKey03"] = // KEY_%1
455 tr("Keystroke event #3");
456 settings["EventCmdKey04"] = // KEY_%1
457 tr("Keystroke event #4");
458 settings["EventCmdKey05"] = // KEY_%1
459 tr("Keystroke event #5");
460 settings["EventCmdKey06"] = // KEY_%1
461 tr("Keystroke event #6");
462 settings["EventCmdKey07"] = // KEY_%1
463 tr("Keystroke event #7");
464 settings["EventCmdKey08"] = // KEY_%1
465 tr("Keystroke event #8");
466 settings["EventCmdKey09"] = // KEY_%1
467 tr("Keystroke event #9");
468 settings["EventCmdKey10"] = // KEY_%1
469 tr("Keystroke event #10");
470 settings["EventCmdCecCommandReceived"] = // CEC_COMMAND_RECEIVED
471 tr("CEC command received");
472 settings["EventCmdAll"] = // EventCmdAll
473 tr("Any event");
474}
475
476/* 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:70
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:375
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:407
uint GetInputID(void) const
Definition: programinfo.h:469
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:453
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.
unsigned int uint
Definition: compat.h:68
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
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.