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 {
190 recinfo.SubstituteMatches(command);
191 }
192 else
193 {
194 // 2rd Try searching for RecordingInfo
196 RecordingInfo recinfo2(chanid, recstartts, false, 0h, &status);
197 if (status == RecordingInfo::kFoundProgram)
198 {
199 recinfo2.SubstituteMatches(command);
200 }
201 else
202 {
203 // 3th just use what we know
204 command.replace(QString("%CHANID%"), QString::number(chanid));
205 command.replace(QString("%STARTTIME%"),
206 MythDate::toString(recstartts,
208 command.replace(QString("%STARTTIMEISO%"),
209 recstartts.toString(Qt::ISODate));
210 }
211 }
212
213 command.replace(QString("%VERBOSELEVEL%"), QString("%1").arg(verboseMask));
214 command.replace("%VERBOSEMODE%", QString("%1").arg(logPropagateArgs));
215
216 LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: AFTER : %1")
217 .arg(command));
218}
219
230{
231 QString result("EventCmd");
232 QStringList parts = name.toLower().split('_', Qt::SkipEmptyParts);
233 for (const auto & part : std::as_const(parts))
234 {
235 result += part.at(0).toUpper();
236 result += part.mid(1);
237 }
238
239 return result;
240}
241
258{
259 if (e->type() == MythEvent::kMythEventMessage)
260 {
261 auto *me = dynamic_cast<MythEvent *>(e);
262 if (me == nullptr)
263 return;
264 QString msg = me->Message().simplified();
265
266 if (msg == "CLEAR_SETTINGS_CACHE")
267 msg = "SYSTEM_EVENT SETTINGS_CACHE_CLEARED";
268
269 // Listen for any GLOBAL_SYSTEM_EVENT messages and resend to
270 // the master backend as regular SYSTEM_EVENT messages.
271 if (msg.startsWith("GLOBAL_SYSTEM_EVENT "))
272 {
273 gCoreContext->SendMessage(msg.mid(7) +
274 QString(" SENDER %1").arg(gCoreContext->GetHostName()));
275 return;
276 }
277
278 if ((!msg.startsWith("SYSTEM_EVENT ")) &&
279 (!msg.startsWith("LOCAL_SYSTEM_EVENT ")))
280 return;
281
282 QStringList tokens = msg.split(' ', Qt::SkipEmptyParts);
283
284 // Return if this event is for another host
285 if ((tokens.size() >= 4) &&
286 (tokens[2] == "HOST") &&
287 (tokens[3] != gCoreContext->GetHostName()))
288 return;
289
290 QString cmd;
291
292 // See if this system has a command that runs for all system events
293 cmd = gCoreContext->GetSetting("EventCmdAll");
294 if (!cmd.isEmpty())
295 {
296 SubstituteMatches(tokens, cmd);
297
298 auto *eventThread = new SystemEventThread(cmd);
300 eventThread, "SystemEvent");
301 }
302
303 // Check for an EventCmd for this particular event
304 cmd = gCoreContext->GetSetting(EventNameToSetting(tokens[1]));
305 if (!cmd.isEmpty())
306 {
307 SubstituteMatches(tokens, cmd);
308
309 LOG(VB_GENERAL, LOG_INFO, LOC +
310 QString("Starting thread for command '%1'").arg(cmd));
311
312 auto *eventThread = new SystemEventThread(cmd, tokens[1]);
314 eventThread, "SystemEvent");
315 }
316 }
317}
318
319/****************************************************************************/
320
327void 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 CardUtil::GetVBIDevice(cardid)));
341 }
342 else
343 {
344 LOG(VB_GENERAL, LOG_ERR, LOC +
345 "SendMythSystemRecEvent() called with empty RecordingInfo");
346 }
347}
348
355void SendMythSystemPlayEvent(const QString &msg, const ProgramInfo *pginfo)
356{
357 if (pginfo)
358 {
360 QString("%1 HOSTNAME %2 CHANID %3 STARTTIME %4")
361 .arg(msg, gCoreContext->GetHostName())
362 .arg(pginfo->GetChanID())
364 }
365 else
366 {
367 LOG(VB_GENERAL, LOG_ERR, LOC + "SendMythSystemPlayEvent() called with "
368 "empty ProgramInfo");
369 }
370}
371
372/****************************************************************************/
373
386 const char *name)
387 : RawSettingsEditor(parent, name)
388{
389 m_title = tr("System Event Command Editor");
391
392}
393 void MythSystemEventEditor::createSettingList(QMap <QString, QString> &settings)
394{
395 // Event names are programmatically converted to settings names in
396 // EventNameToSetting(). For convenience of searching the code
397 // base, the event names are listed in comments.
398 settings["EventCmdRecPending"] = // REC_PENDING
399 tr("Recording pending");
400 settings["EventCmdRecPreFail"] = // REC_PREFAIL
401 tr("Recording about to fail");
402 settings["EventCmdRecFailing"] = // REC_FAILING
403 tr("Recording failing");
404 settings["EventCmdRecStarted"] = // REC_STARTED
405 tr("Recording started");
406 settings["EventCmdRecStartedWriting"] = // REC_STARTED_WRITING
407 tr("Recording started writing");
408 settings["EventCmdRecFinished"] = // REC_FINISHED
409 tr("Recording finished");
410 settings["EventCmdRecDeleted"] = // REC_DELETED
411 tr("Recording deleted");
412 settings["EventCmdRecExpired"] = // REC_EXPIRED
413 tr("Recording expired");
414 settings["EventCmdLivetvStarted"] = // LIVETV_STARTED
415 tr("LiveTV started");
416 settings["EventCmdLivetvEnded"] = // LIVETV_ENDED
417 tr("LiveTV ended");
418 settings["EventCmdPlayStarted"] = // PLAY_STARTED
419 tr("Playback started");
420 settings["EventCmdPlayStopped"] = // PLAY_STOPPED
421 tr("Playback stopped");
422 settings["EventCmdPlayPaused"] = // PLAY_PAUSED
423 tr("Playback paused");
424 settings["EventCmdPlayUnpaused"] = // PLAY_UNPAUSED
425 tr("Playback unpaused");
426 settings["EventCmdPlayChanged"] = // PLAY_CHANGED
427 tr("Playback program changed");
428 settings["EventCmdTuningSignalTimeout"] = // TUNING_SIGNAL_TIMEOUT
429 tr("Tuning signal waiting");
430 settings["EventCmdMasterStarted"] = // MASTER_STARTED
431 tr("Master backend started");
432 settings["EventCmdMasterShutdown"] = // MASTER_SHUTDOWN
433 tr("Master backend shutdown");
434 settings["EventCmdClientConnected"] = // CLIENT_CONNECTED
435 tr("Client connected to master backend");
436 settings["EventCmdClientDisconnected"] = // CLIENT_DISCONNECTED
437 tr("Client disconnected from master backend");
438 settings["EventCmdSlaveConnected"] = // SLAVE_CONNECTED
439 tr("Slave backend connected to master");
440 settings["EventCmdSlaveDisconnected"] = // SLAVE_DISCONNECTED
441 tr("Slave backend disconnected from master");
442 settings["EventCmdNetCtrlConnected"] = // NET_CTRL_CONNECTED
443 tr("Network Control client connected");
444 settings["EventCmdNetCtrlDisconnected"] = // NET_CTRL_DISCONNECTED
445 tr("Network Control client disconnected");
446 settings["EventCmdMythfilldatabaseRan"] = // MYTHFILLDATABASE_RAN
447 tr("mythfilldatabase ran");
448 settings["EventCmdSchedulerRan"] = // SCHEDULER_RAN
449 tr("Scheduler ran");
450 settings["EventCmdSettingsCacheCleared"] = // SETTINGS_CACHE_CLEARED
451 tr("Settings cache cleared");
452 settings["EventCmdScreenType"] = // SCREEN_TYPE
453 tr("Screen created or destroyed");
454 settings["EventCmdKey01"] = // KEY_%1
455 tr("Keystroke event #1");
456 settings["EventCmdKey02"] = // KEY_%1
457 tr("Keystroke event #2");
458 settings["EventCmdKey03"] = // KEY_%1
459 tr("Keystroke event #3");
460 settings["EventCmdKey04"] = // KEY_%1
461 tr("Keystroke event #4");
462 settings["EventCmdKey05"] = // KEY_%1
463 tr("Keystroke event #5");
464 settings["EventCmdKey06"] = // KEY_%1
465 tr("Keystroke event #6");
466 settings["EventCmdKey07"] = // KEY_%1
467 tr("Keystroke event #7");
468 settings["EventCmdKey08"] = // KEY_%1
469 tr("Keystroke event #8");
470 settings["EventCmdKey09"] = // KEY_%1
471 tr("Keystroke event #9");
472 settings["EventCmdKey10"] = // KEY_%1
473 tr("Keystroke event #10");
474 settings["EventCmdCecCommandReceived"] = // CEC_COMMAND_RECEIVED
475 tr("CEC command received");
476 settings["EventCmdAll"] = // EventCmdAll
477 tr("Any event");
478}
479
480#include "moc_mythsystemevent.cpp"
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:296
static QString GetVBIDevice(uint inputid)
Definition: cardutil.h:300
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:74
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:380
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:412
uint GetInputID(void) const
Definition: programinfo.h:474
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:458
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:60
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
uint64_t verboseMask
Definition: logging.cpp:101
QString logPropagateArgs
Definition: logging.cpp:86
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