MythTV  master
remoteutil.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 
3 #include <QFileInfo>
4 #include <QFile>
5 #include <QDir>
6 #include <QList>
7 
8 #include "compat.h"
9 #include "remoteutil.h"
10 #include "programinfo.h"
11 #include "mythcorecontext.h"
12 #include "storagegroup.h"
13 #include "mythevent.h"
14 #include "mythsocket.h"
15 
16 vector<ProgramInfo *> *RemoteGetRecordedList(int sort)
17 {
18  QString str = "QUERY_RECORDINGS ";
19  if (sort < 0)
20  str += "Descending";
21  else if (sort > 0)
22  str += "Ascending";
23  else
24  str += "Unsorted";
25 
26  QStringList strlist(str);
27 
28  auto *info = new vector<ProgramInfo *>;
29 
30  if (!RemoteGetRecordingList(*info, strlist))
31  {
32  delete info;
33  return nullptr;
34  }
35 
36  return info;
37 }
38 
39 bool RemoteGetLoad(float load[3])
40 {
41  QStringList strlist(QString("QUERY_LOAD"));
42 
43  if (gCoreContext->SendReceiveStringList(strlist) && strlist.size() >= 3)
44  {
45  load[0] = strlist[0].toFloat();
46  load[1] = strlist[1].toFloat();
47  load[2] = strlist[2].toFloat();
48  return true;
49  }
50 
51  return false;
52 }
53 
54 bool RemoteGetUptime(time_t &uptime)
55 {
56  QStringList strlist(QString("QUERY_UPTIME"));
57 
58  if (!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty())
59  return false;
60 
61  if (strlist[0].isEmpty() || !strlist[0].at(0).isNumber())
62  return false;
63 
64  if (sizeof(time_t) == sizeof(int))
65  uptime = strlist[0].toUInt();
66  else if (sizeof(time_t) == sizeof(long))
67  uptime = strlist[0].toULong();
68  else if (sizeof(time_t) == sizeof(long long))
69  uptime = strlist[0].toULongLong();
70 
71  return true;
72 }
73 
74 bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
75 {
76  QStringList strlist(QString("QUERY_MEMSTATS"));
77 
78  if (gCoreContext->SendReceiveStringList(strlist) && strlist.size() >= 4)
79  {
80  totalMB = strlist[0].toInt();
81  freeMB = strlist[1].toInt();
82  totalVM = strlist[2].toInt();
83  freeVM = strlist[3].toInt();
84  return true;
85  }
86 
87  return false;
88 }
89 
90 bool RemoteCheckFile(ProgramInfo *pginfo, bool checkSlaves)
91 {
92  QStringList strlist("QUERY_CHECKFILE");
93  strlist << QString::number((int)checkSlaves);
94  pginfo->ToStringList(strlist);
95 
96  if (!gCoreContext->SendReceiveStringList(strlist) ||
97  (strlist.size() < 2) || !strlist[0].toInt())
98  return false;
99 
100  // Only modify the pathname if the recording file is available locally on
101  // this host
102  QString localpath = strlist[1];
103  QFile checkFile(localpath);
104  if (checkFile.exists())
105  pginfo->SetPathname(localpath);
106 
107  return true;
108 }
109 
110 bool RemoteDeleteRecording(uint recordingID, bool forceMetadataDelete,
111  bool forgetHistory)
112 {
113  // FIXME: Remove when DELETE_RECORDING has been updated to use recording id
114  ProgramInfo recInfo(recordingID);
115  bool result = true;
116  QString cmd =
117  QString("DELETE_RECORDING %1 %2 %3 %4")
118  .arg(QString::number(recInfo.GetChanID()))
119  .arg(recInfo.GetRecordingStartTime().toString(Qt::ISODate))
120  .arg(forceMetadataDelete ? "FORCE" : "NO_FORCE")
121  .arg(forgetHistory ? "FORGET" : "NO_FORGET");
122  QStringList strlist(cmd);
123 
124  if ((!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty()) ||
125  (strlist[0].toInt() == -2))
126  result = false;
127 
128  if (!result)
129  {
130  LOG(VB_GENERAL, LOG_ALERT,
131  QString("Failed to delete recording %1:%2")
132  .arg(recInfo.GetChanID())
133  .arg(recInfo.GetRecordingStartTime().toString(Qt::ISODate)));
134  }
135 
136  return result;
137 }
138 
139 bool RemoteUndeleteRecording(uint recordingID)
140 {
141  // FIXME: Remove when UNDELETE_RECORDING has been updated to use recording id
142  ProgramInfo recInfo(recordingID);
143  bool result = false;
144 
145 #if 0
146  if (!gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0))
147  return result;
148 #endif
149 
150  QStringList strlist(QString("UNDELETE_RECORDING"));
151  strlist.push_back(QString::number(recInfo.GetChanID()));
152  strlist.push_back(recInfo.GetRecordingStartTime().toString(Qt::ISODate));
153 
155 
156  if (!strlist.isEmpty() && strlist[0].toInt() == 0)
157  result = true;
158 
159  return result;
160 }
161 
162 void RemoteGetAllScheduledRecordings(vector<ProgramInfo *> &scheduledlist)
163 {
164  QStringList strList(QString("QUERY_GETALLSCHEDULED"));
165  RemoteGetRecordingList(scheduledlist, strList);
166 }
167 
168 void RemoteGetAllExpiringRecordings(vector<ProgramInfo *> &expiringlist)
169 {
170  QStringList strList(QString("QUERY_GETEXPIRING"));
171  RemoteGetRecordingList(expiringlist, strList);
172 }
173 
175  vector<ProgramInfo *> &reclist, QStringList &strList)
176 {
177  if (!gCoreContext->SendReceiveStringList(strList) || strList.isEmpty())
178  return 0;
179 
180  int numrecordings = strList[0].toInt();
181  if (numrecordings <= 0)
182  return 0;
183 
184  if (numrecordings * NUMPROGRAMLINES + 1 > strList.size())
185  {
186  LOG(VB_GENERAL, LOG_ERR,
187  "RemoteGetRecordingList() list size appears to be incorrect.");
188  return 0;
189  }
190 
191  uint reclist_initial_size = (uint) reclist.size();
192  QStringList::const_iterator it = strList.begin() + 1;
193  for (int i = 0; i < numrecordings; i++)
194  {
195  auto *pginfo = new ProgramInfo(it, strList.end());
196  reclist.push_back(pginfo);
197  }
198 
199  return ((uint) reclist.size()) - reclist_initial_size;
200 }
201 
202 vector<ProgramInfo *> *RemoteGetConflictList(const ProgramInfo *pginfo)
203 {
204  QString cmd = QString("QUERY_GETCONFLICTING");
205  QStringList strlist( cmd );
206  pginfo->ToStringList(strlist);
207 
208  auto *retlist = new vector<ProgramInfo *>;
209 
210  RemoteGetRecordingList(*retlist, strlist);
211  return retlist;
212 }
213 
215 {
216  QStringList strlist( "QUERY_PIXMAP_LASTMODIFIED" );
217  pginfo->ToStringList(strlist);
218 
219  if (!gCoreContext->SendReceiveStringList(strlist))
220  return QDateTime();
221 
222  if (!strlist.isEmpty() && !strlist[0].isEmpty() && (strlist[0] != "BAD"))
223  {
224 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
225  uint timet = strlist[0].toUInt();
226  return MythDate::fromTime_t(timet);
227 #else
228  qint64 timet = strlist[0].toLongLong();
229  return MythDate::fromSecsSinceEpoch(timet);
230 #endif
231  }
232 
233  return QDateTime();
234 }
235 
239  const ProgramInfo &pginfo, const QString &cachefile)
240 {
241  QString loc("RemoteGetPreviewIfModified: ");
242  QDateTime cacheLastModified;
243  QFileInfo cachefileinfo(cachefile);
244  if (cachefileinfo.exists())
245  cacheLastModified = cachefileinfo.lastModified();
246 
247  QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED");
248  strlist << ((cacheLastModified.isValid()) ? // unix secs, UTC
249 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
250  QString::number(cacheLastModified.toTime_t()) :
251 #else
252  QString::number(cacheLastModified.toSecsSinceEpoch()) :
253 #endif
254  QString("-1"));
255  strlist << QString::number(200 * 1024); // max size of preview file
256  pginfo.ToStringList(strlist);
257 
258  if (!gCoreContext->SendReceiveStringList(strlist) ||
259  strlist.isEmpty() || strlist[0] == "ERROR")
260  {
261  LOG(VB_GENERAL, LOG_ERR, loc + "Remote error" +
262  ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
263 
264  return QDateTime();
265  }
266 
267  if (strlist[0] == "WARNING")
268  {
269  LOG(VB_NETWORK, LOG_WARNING, loc + "Remote warning" +
270  ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
271 
272  return QDateTime();
273  }
274 
275  QDateTime retdatetime;
276  qlonglong timet = strlist[0].toLongLong();
277  if (timet >= 0)
278 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
279  retdatetime = MythDate::fromTime_t(timet);
280 #else
281  retdatetime = MythDate::fromSecsSinceEpoch(timet);
282 #endif
283 
284  if (strlist.size() < 4)
285  {
286  return retdatetime;
287  }
288 
289  size_t length = strlist[1].toULongLong();
290  quint16 checksum16 = strlist[2].toUInt();
291  QByteArray data = QByteArray::fromBase64(strlist[3].toLatin1());
292  if ((size_t) data.size() < length)
293  { // (note data.size() may be up to 3 bytes longer after decoding
294  LOG(VB_GENERAL, LOG_ERR, loc +
295  QString("Preview size check failed %1 < %2")
296  .arg(data.size()).arg(length));
297  return QDateTime();
298  }
299  data.resize(length);
300 
301  if (checksum16 != qChecksum(data.constData(), data.size()))
302  {
303  LOG(VB_GENERAL, LOG_ERR, loc + "Preview checksum failed");
304  return QDateTime();
305  }
306 
307  QString pdir(cachefile.section("/", 0, -2));
308  QDir cfd(pdir);
309  if (!cfd.exists() && !cfd.mkdir(pdir))
310  {
311  LOG(VB_GENERAL, LOG_ERR, loc +
312  QString("Unable to create remote cache directory '%1'")
313  .arg(pdir));
314 
315  return QDateTime();
316  }
317 
318  QFile file(cachefile);
319  if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
320  {
321  LOG(VB_GENERAL, LOG_ERR, loc +
322  QString("Unable to open cached preview file for writing '%1'")
323  .arg(cachefile));
324 
325  return QDateTime();
326  }
327 
328  off_t offset = 0;
329  size_t remaining = length;
330  uint failure_cnt = 0;
331  while ((remaining > 0) && (failure_cnt < 5))
332  {
333  ssize_t written = file.write(data.data() + offset, remaining);
334  if (written < 0)
335  {
336  failure_cnt++;
337  usleep(50000);
338  continue;
339  }
340 
341  failure_cnt = 0;
342  offset += written;
343  remaining -= written;
344  }
345 
346  if (remaining)
347  {
348  LOG(VB_GENERAL, LOG_ERR, loc +
349  QString("Failed to write cached preview file '%1'")
350  .arg(cachefile));
351 
352  file.resize(0); // in case unlink fails..
353  file.remove(); // closes fd
354  return QDateTime();
355  }
356 
357  file.close();
358 
359  return retdatetime;
360 }
361 
362 bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
363 {
364  QStringList strlist( "FILL_PROGRAM_INFO" );
365  strlist << playbackhost;
366  pginfo.ToStringList(strlist);
367 
368  if (gCoreContext->SendReceiveStringList(strlist))
369  {
370  ProgramInfo tmp(strlist);
371  if (tmp.HasPathname() || tmp.GetChanID())
372  {
373  pginfo = tmp;
374  return true;
375  }
376  }
377 
378  return false;
379 }
380 
381 QStringList RemoteRecordings(void)
382 {
383  QStringList strlist("QUERY_ISRECORDING");
384 
385  if (!gCoreContext->SendReceiveStringList(strlist, false, false))
386  {
387  QStringList empty;
388  empty << "0" << "0";
389  return empty;
390  }
391 
392  return strlist;
393 }
394 
396 {
397  int mask = 0;
398 
399  QString cmd = "QUERY_ISRECORDING";
400 
401  QStringList strlist( cmd );
402 
403  if (!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty())
404  return mask;
405 
406  int recCount = strlist[0].toInt();
407 
408  for (int i = 0, j = 0; j < recCount; i++)
409  {
410  cmd = QString("QUERY_RECORDER %1").arg(i + 1);
411 
412  strlist = QStringList( cmd );
413  strlist << "IS_RECORDING";
414 
415  if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
416  {
417  if (strlist[0].toInt())
418  {
419  mask |= 1<<i;
420  j++; // count active recorder
421  }
422  }
423  else
424  {
425  break;
426  }
427  }
428 
429  return mask;
430 }
431 
432 bool RemoteGetFileList(const QString& host, const QString& path, QStringList* list,
433  QString sgroup, bool fileNamesOnly)
434 {
435 
436  // Make sure the list is empty when we get started
437  list->clear();
438 
439  if (sgroup.isEmpty())
440  sgroup = "Videos";
441 
442  *list << "QUERY_SG_GETFILELIST";
443  *list << host;
444  *list << StorageGroup::GetGroupToUse(host, sgroup);
445  *list << path;
446  *list << QString::number(fileNamesOnly);
447 
448  bool ok = false;
449 
451  {
452  // since the master backend cannot connect back around to
453  // itself, and the libraries do not have access to the list
454  // of connected slave backends to query an existing connection
455  // start up a new temporary connection directly to the slave
456  // backend to query the file list
457  QString ann = QString("ANN Playback %1 0")
458  .arg(gCoreContext->GetHostName());
459  QString addr = gCoreContext->GetBackendServerIP(host);
460  int port = gCoreContext->GetBackendServerPort(host);
461  bool mismatch = false;
462 
464  addr, port, ann, &mismatch);
465  if (sock)
466  {
467  ok = sock->SendReceiveStringList(*list);
468  sock->DecrRef();
469  }
470  else
471  list->clear();
472  }
473  else
474  ok = gCoreContext->SendReceiveStringList(*list);
475 
476 // Should the SLAVE UNREACH test be here ?
477  return ok;
478 }
479 
486 {
487  QStringList strlist( QString("CHECK_RECORDING") );
488  pginfo->ToStringList(strlist);
489 
490  if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
491  return strlist[0].toInt();
492 
493  return 0;
494 }
495 
505  const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
506 {
507  QDateTime curtime = MythDate::current();
508 
509  int retval = 0;
510 
511  if (pginfo)
512  {
513  if (curtime >= pginfo->GetScheduledStartTime().addSecs(-underrecsecs) &&
514  curtime < pginfo->GetScheduledEndTime().addSecs(overrecsecs))
515  {
516  if (curtime >= pginfo->GetScheduledStartTime() &&
517  curtime < pginfo->GetScheduledEndTime())
518  retval = 1;
519  else if (curtime < pginfo->GetScheduledStartTime() &&
520  RemoteCheckForRecording(pginfo) > 0)
521  retval = 2;
522  else if (curtime > pginfo->GetScheduledEndTime() &&
523  RemoteCheckForRecording(pginfo) > 0)
524  retval = 3;
525  }
526  }
527 
528  return retval;
529 }
530 
534 vector<ProgramInfo *> *RemoteGetCurrentlyRecordingList(void)
535 {
536  QString str = "QUERY_RECORDINGS ";
537  str += "Recording";
538  QStringList strlist( str );
539 
540  auto *reclist = new vector<ProgramInfo *>;
541  auto *info = new vector<ProgramInfo *>;
542  if (!RemoteGetRecordingList(*info, strlist))
543  {
544  delete info;
545  return reclist;
546  }
547 
548  ProgramInfo *p = nullptr;
549  auto it = info->begin();
550  // make sure whatever RemoteGetRecordingList() returned
551  // only has RecStatus::Recording shows
552  for ( ; it != info->end(); ++it)
553  {
554  p = *it;
555  if (p->GetRecordingStatus() == RecStatus::Recording ||
556  p->GetRecordingStatus() == RecStatus::Tuning ||
557  p->GetRecordingStatus() == RecStatus::Failing ||
558  (p->GetRecordingStatus() == RecStatus::Recorded &&
559  p->GetRecordingGroup() == "LiveTV"))
560  {
561  reclist->push_back(new ProgramInfo(*p));
562  }
563  }
564 
565  while (!info->empty())
566  {
567  delete info->back();
568  info->pop_back();
569  }
570  delete info;
571 
572  return reclist;
573 }
574 
578 bool RemoteGetActiveBackends(QStringList *list)
579 {
580  list->clear();
581  *list << "QUERY_ACTIVE_BACKENDS";
582 
583  if (!gCoreContext->SendReceiveStringList(*list))
584  return false;
585 
586  list->removeFirst();
587  return true;
588 }
589 
590 /* vim: set expandtab tabstop=4 shiftwidth=4: */
int RemoteCheckForRecording(const ProgramInfo *pginfo)
Get recorder for a programme.
Definition: remoteutil.cpp:485
vector< ProgramInfo * > * RemoteGetCurrentlyRecordingList(void)
return list of currently recording shows
Definition: remoteutil.cpp:534
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
Definition: remoteutil.cpp:74
void RemoteGetAllExpiringRecordings(vector< ProgramInfo * > &expiringlist)
Definition: remoteutil.cpp:168
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
Definition: remoteutil.cpp:362
static guint32 * tmp
Definition: goom_core.c:35
#define off_t
bool RemoteDeleteRecording(uint recordingID, bool forceMetadataDelete, bool forgetHistory)
Definition: remoteutil.cpp:110
int GetBackendServerPort(void)
Returns the locally defined backend control port.
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
Holds information on recordings and videos.
Definition: programinfo.h:66
bool RemoteGetActiveBackends(QStringList *list)
return list of backends currently connected to the master
Definition: remoteutil.cpp:578
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, int setup_timeout=-1)
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(uint seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
Definition: mythdate.cpp:88
QDateTime RemoteGetPreviewIfModified(const ProgramInfo &pginfo, const QString &cachefile)
Download preview & get timestamp if newer than cachefile's last modified time, otherwise just get the...
Definition: remoteutil.cpp:238
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
void RemoteGetAllScheduledRecordings(vector< ProgramInfo * > &scheduledlist)
Definition: remoteutil.cpp:162
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
bool RemoteGetFileList(const QString &host, const QString &path, QStringList *list, QString sgroup, bool fileNamesOnly)
Definition: remoteutil.cpp:432
unsigned int uint
Definition: compat.h:140
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo)
Definition: remoteutil.cpp:214
vector< ProgramInfo * > * RemoteGetConflictList(const ProgramInfo *pginfo)
Definition: remoteutil.cpp:202
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static QString GetGroupToUse(const QString &host, const QString &sgroup)
#define NUMPROGRAMLINES
Definition: programinfo.h:27
bool RemoteGetLoad(float load[3])
Definition: remoteutil.cpp:39
uint RemoteGetRecordingList(vector< ProgramInfo * > &reclist, QStringList &strList)
Definition: remoteutil.cpp:174
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
bool RemoteUndeleteRecording(uint recordingID)
Definition: remoteutil.cpp:139
int RemoteGetRecordingMask(void)
Definition: remoteutil.cpp:395
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:396
bool RemoteCheckFile(ProgramInfo *pginfo, bool checkSlaves)
Definition: remoteutil.cpp:90
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, uint timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:336
Default UTC.
Definition: mythdate.h:14
void SetPathname(const QString &)
bool RemoteGetUptime(time_t &uptime)
Definition: remoteutil.cpp:54
bool IsMasterBackend(void)
is this the actual MBE process
int RemoteGetRecordingStatus(const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
Get status of an individual programme (with pre-post roll?).
Definition: remoteutil.cpp:504
vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
Definition: remoteutil.cpp:16
QString GetHostName(void)
QStringList RemoteRecordings(void)
Definition: remoteutil.cpp:381