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  vector<ProgramInfo *> *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(const 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  result = false;
126  else if (strlist[0].toInt() == -2)
127  result = false;
128 
129  if (!result)
130  {
131  LOG(VB_GENERAL, LOG_ALERT,
132  QString("Failed to delete recording %1:%2")
133  .arg(recInfo.GetChanID())
134  .arg(recInfo.GetRecordingStartTime().toString(Qt::ISODate)));
135  }
136 
137  return result;
138 }
139 
140 bool RemoteUndeleteRecording(uint recordingID)
141 {
142  // FIXME: Remove when UNDELETE_RECORDING has been updated to use recording id
143  ProgramInfo recInfo(recordingID);
144  bool result = false;
145 
146 #if 0
147  if (!gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0))
148  return result;
149 #endif
150 
151  QStringList strlist(QString("UNDELETE_RECORDING"));
152  strlist.push_back(QString::number(recInfo.GetChanID()));
153  strlist.push_back(recInfo.GetRecordingStartTime().toString(Qt::ISODate));
154 
156 
157  if (!strlist.isEmpty() && strlist[0].toInt() == 0)
158  result = true;
159 
160  return result;
161 }
162 
163 void RemoteGetAllScheduledRecordings(vector<ProgramInfo *> &scheduledlist)
164 {
165  QStringList strList(QString("QUERY_GETALLSCHEDULED"));
166  RemoteGetRecordingList(scheduledlist, strList);
167 }
168 
169 void RemoteGetAllExpiringRecordings(vector<ProgramInfo *> &expiringlist)
170 {
171  QStringList strList(QString("QUERY_GETEXPIRING"));
172  RemoteGetRecordingList(expiringlist, strList);
173 }
174 
176  vector<ProgramInfo *> &reclist, QStringList &strList)
177 {
178  if (!gCoreContext->SendReceiveStringList(strList) || strList.isEmpty())
179  return 0;
180 
181  int numrecordings = strList[0].toInt();
182  if (numrecordings <= 0)
183  return 0;
184 
185  if (numrecordings * NUMPROGRAMLINES + 1 > strList.size())
186  {
187  LOG(VB_GENERAL, LOG_ERR,
188  "RemoteGetRecordingList() list size appears to be incorrect.");
189  return 0;
190  }
191 
192  uint reclist_initial_size = (uint) reclist.size();
193  QStringList::const_iterator it = strList.begin() + 1;
194  for (int i = 0; i < numrecordings; i++)
195  {
196  ProgramInfo *pginfo = new ProgramInfo(it, strList.end());
197  reclist.push_back(pginfo);
198  }
199 
200  return ((uint) reclist.size()) - reclist_initial_size;
201 }
202 
203 vector<ProgramInfo *> *RemoteGetConflictList(const ProgramInfo *pginfo)
204 {
205  QString cmd = QString("QUERY_GETCONFLICTING");
206  QStringList strlist( cmd );
207  pginfo->ToStringList(strlist);
208 
209  vector<ProgramInfo *> *retlist = new vector<ProgramInfo *>;
210 
211  RemoteGetRecordingList(*retlist, strlist);
212  return retlist;
213 }
214 
216 {
217  QStringList strlist( "QUERY_PIXMAP_LASTMODIFIED" );
218  pginfo->ToStringList(strlist);
219 
220  if (!gCoreContext->SendReceiveStringList(strlist))
221  return QDateTime();
222 
223  if (!strlist.isEmpty() && !strlist[0].isEmpty() && (strlist[0] != "BAD"))
224  {
225 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
226  uint timet = strlist[0].toUInt();
227  return MythDate::fromTime_t(timet);
228 #else
229  qint64 timet = strlist[0].toLongLong();
230  return MythDate::fromSecsSinceEpoch(timet);
231 #endif
232  }
233 
234  return QDateTime();
235 }
236 
240  const ProgramInfo &pginfo, const QString &cachefile)
241 {
242  QString loc("RemoteGetPreviewIfModified: ");
243  QDateTime cacheLastModified;
244  QFileInfo cachefileinfo(cachefile);
245  if (cachefileinfo.exists())
246  cacheLastModified = cachefileinfo.lastModified();
247 
248  QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED");
249  strlist << ((cacheLastModified.isValid()) ? // unix secs, UTC
250 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
251  QString::number(cacheLastModified.toTime_t()) :
252 #else
253  QString::number(cacheLastModified.toSecsSinceEpoch()) :
254 #endif
255  QString("-1"));
256  strlist << QString::number(200 * 1024); // max size of preview file
257  pginfo.ToStringList(strlist);
258 
259  if (!gCoreContext->SendReceiveStringList(strlist) ||
260  strlist.isEmpty() || strlist[0] == "ERROR")
261  {
262  LOG(VB_GENERAL, LOG_ERR, loc + "Remote error" +
263  ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
264 
265  return QDateTime();
266  }
267 
268  if (strlist[0] == "WARNING")
269  {
270  LOG(VB_NETWORK, LOG_WARNING, loc + "Remote warning" +
271  ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
272 
273  return QDateTime();
274  }
275 
276  QDateTime retdatetime;
277  qlonglong timet = strlist[0].toLongLong();
278  if (timet >= 0)
279 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
280  retdatetime = MythDate::fromTime_t(timet);
281 #else
282  retdatetime = MythDate::fromSecsSinceEpoch(timet);
283 #endif
284 
285  if (strlist.size() < 4)
286  {
287  return retdatetime;
288  }
289 
290  size_t length = strlist[1].toULongLong();
291  quint16 checksum16 = strlist[2].toUInt();
292  QByteArray data = QByteArray::fromBase64(strlist[3].toLatin1());
293  if ((size_t) data.size() < length)
294  { // (note data.size() may be up to 3 bytes longer after decoding
295  LOG(VB_GENERAL, LOG_ERR, loc +
296  QString("Preview size check failed %1 < %2")
297  .arg(data.size()).arg(length));
298  return QDateTime();
299  }
300  data.resize(length);
301 
302  if (checksum16 != qChecksum(data.constData(), data.size()))
303  {
304  LOG(VB_GENERAL, LOG_ERR, loc + "Preview checksum failed");
305  return QDateTime();
306  }
307 
308  QString pdir(cachefile.section("/", 0, -2));
309  QDir cfd(pdir);
310  if (!cfd.exists() && !cfd.mkdir(pdir))
311  {
312  LOG(VB_GENERAL, LOG_ERR, loc +
313  QString("Unable to create remote cache directory '%1'")
314  .arg(pdir));
315 
316  return QDateTime();
317  }
318 
319  QFile file(cachefile);
320  if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
321  {
322  LOG(VB_GENERAL, LOG_ERR, loc +
323  QString("Unable to open cached preview file for writing '%1'")
324  .arg(cachefile));
325 
326  return QDateTime();
327  }
328 
329  off_t offset = 0;
330  size_t remaining = length;
331  uint failure_cnt = 0;
332  while ((remaining > 0) && (failure_cnt < 5))
333  {
334  ssize_t written = file.write(data.data() + offset, remaining);
335  if (written < 0)
336  {
337  failure_cnt++;
338  usleep(50000);
339  continue;
340  }
341 
342  failure_cnt = 0;
343  offset += written;
344  remaining -= written;
345  }
346 
347  if (remaining)
348  {
349  LOG(VB_GENERAL, LOG_ERR, loc +
350  QString("Failed to write cached preview file '%1'")
351  .arg(cachefile));
352 
353  file.resize(0); // in case unlink fails..
354  file.remove(); // closes fd
355  return QDateTime();
356  }
357 
358  file.close();
359 
360  return retdatetime;
361 }
362 
363 bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
364 {
365  QStringList strlist( "FILL_PROGRAM_INFO" );
366  strlist << playbackhost;
367  pginfo.ToStringList(strlist);
368 
369  if (gCoreContext->SendReceiveStringList(strlist))
370  {
371  ProgramInfo tmp(strlist);
372  if (tmp.HasPathname() || tmp.GetChanID())
373  {
374  pginfo = tmp;
375  return true;
376  }
377  }
378 
379  return false;
380 }
381 
382 QStringList RemoteRecordings(void)
383 {
384  QStringList strlist("QUERY_ISRECORDING");
385 
386  if (!gCoreContext->SendReceiveStringList(strlist, false, false))
387  {
388  QStringList empty;
389  empty << "0" << "0";
390  return empty;
391  }
392 
393  return strlist;
394 }
395 
397 {
398  int mask = 0;
399 
400  QString cmd = "QUERY_ISRECORDING";
401 
402  QStringList strlist( cmd );
403 
404  if (!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty())
405  return mask;
406 
407  int recCount = strlist[0].toInt();
408 
409  for (int i = 0, j = 0; j < recCount; i++)
410  {
411  cmd = QString("QUERY_RECORDER %1").arg(i + 1);
412 
413  strlist = QStringList( cmd );
414  strlist << "IS_RECORDING";
415 
416  if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
417  {
418  if (strlist[0].toInt())
419  {
420  mask |= 1<<i;
421  j++; // count active recorder
422  }
423  }
424  else
425  {
426  break;
427  }
428  }
429 
430  return mask;
431 }
432 
433 bool RemoteGetFileList(const QString& host, const QString& path, QStringList* list,
434  QString sgroup, bool fileNamesOnly)
435 {
436 
437  // Make sure the list is empty when we get started
438  list->clear();
439 
440  if (sgroup.isEmpty())
441  sgroup = "Videos";
442 
443  *list << "QUERY_SG_GETFILELIST";
444  *list << host;
445  *list << StorageGroup::GetGroupToUse(host, sgroup);
446  *list << path;
447  *list << QString::number(fileNamesOnly);
448 
449  bool ok = false;
450 
452  {
453  // since the master backend cannot connect back around to
454  // itself, and the libraries do not have access to the list
455  // of connected slave backends to query an existing connection
456  // start up a new temporary connection directly to the slave
457  // backend to query the file list
458  QString ann = QString("ANN Playback %1 0")
459  .arg(gCoreContext->GetHostName());
460  QString addr = gCoreContext->GetBackendServerIP(host);
461  int port = gCoreContext->GetBackendServerPort(host);
462  bool mismatch = false;
463 
465  addr, port, ann, &mismatch);
466  if (sock)
467  {
468  ok = sock->SendReceiveStringList(*list);
469  sock->DecrRef();
470  }
471  else
472  list->clear();
473  }
474  else
475  ok = gCoreContext->SendReceiveStringList(*list);
476 
477 // Should the SLAVE UNREACH test be here ?
478  return ok;
479 }
480 
487 {
488  QStringList strlist( QString("CHECK_RECORDING") );
489  pginfo->ToStringList(strlist);
490 
491  if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
492  return strlist[0].toInt();
493 
494  return 0;
495 }
496 
506  const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
507 {
508  QDateTime curtime = MythDate::current();
509 
510  int retval = 0;
511 
512  if (pginfo)
513  {
514  if (curtime >= pginfo->GetScheduledStartTime().addSecs(-underrecsecs) &&
515  curtime < pginfo->GetScheduledEndTime().addSecs(overrecsecs))
516  {
517  if (curtime >= pginfo->GetScheduledStartTime() &&
518  curtime < pginfo->GetScheduledEndTime())
519  retval = 1;
520  else if (curtime < pginfo->GetScheduledStartTime() &&
521  RemoteCheckForRecording(pginfo) > 0)
522  retval = 2;
523  else if (curtime > pginfo->GetScheduledEndTime() &&
524  RemoteCheckForRecording(pginfo) > 0)
525  retval = 3;
526  }
527  }
528 
529  return retval;
530 }
531 
535 vector<ProgramInfo *> *RemoteGetCurrentlyRecordingList(void)
536 {
537  QString str = "QUERY_RECORDINGS ";
538  str += "Recording";
539  QStringList strlist( str );
540 
541  vector<ProgramInfo *> *reclist = new vector<ProgramInfo *>;
542  vector<ProgramInfo *> *info = new vector<ProgramInfo *>;
543  if (!RemoteGetRecordingList(*info, strlist))
544  {
545  delete info;
546  return reclist;
547  }
548 
549  ProgramInfo *p = nullptr;
550  vector<ProgramInfo *>::iterator it = info->begin();
551  // make sure whatever RemoteGetRecordingList() returned
552  // only has RecStatus::Recording shows
553  for ( ; it != info->end(); ++it)
554  {
555  p = *it;
560  p->GetRecordingGroup() == "LiveTV"))
561  {
562  reclist->push_back(new ProgramInfo(*p));
563  }
564  }
565 
566  while (!info->empty())
567  {
568  delete info->back();
569  info->pop_back();
570  }
571  delete info;
572 
573  return reclist;
574 }
575 
579 bool RemoteGetActiveBackends(QStringList *list)
580 {
581  list->clear();
582  *list << "QUERY_ACTIVE_BACKENDS";
583 
584  if (!gCoreContext->SendReceiveStringList(*list))
585  return false;
586 
587  list->removeFirst();
588  return true;
589 }
590 
591 /* vim: set expandtab tabstop=4 shiftwidth=4: */
int RemoteCheckForRecording(const ProgramInfo *pginfo)
Get recorder for a programme.
Definition: remoteutil.cpp:486
vector< ProgramInfo * > * RemoteGetCurrentlyRecordingList(void)
return list of currently recording shows
Definition: remoteutil.cpp:535
void SetPathname(const QString &) const
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:169
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
Definition: remoteutil.cpp:363
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:579
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:239
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
void RemoteGetAllScheduledRecordings(vector< ProgramInfo * > &scheduledlist)
Definition: remoteutil.cpp:163
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
QString GetRecordingGroup(void) const
Definition: programinfo.h:411
bool RemoteGetFileList(const QString &host, const QString &path, QStringList *list, QString sgroup, bool fileNamesOnly)
Definition: remoteutil.cpp:433
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo)
Definition: remoteutil.cpp:215
vector< ProgramInfo * > * RemoteGetConflictList(const ProgramInfo *pginfo)
Definition: remoteutil.cpp:203
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)
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:439
#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:175
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:140
int RemoteGetRecordingMask(void)
Definition: remoteutil.cpp:396
bool RemoteCheckFile(const ProgramInfo *pginfo, bool checkSlaves)
Definition: remoteutil.cpp:90
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:396
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
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:505
vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
Definition: remoteutil.cpp:16
QString GetHostName(void)
Default UTC.
Definition: mythdate.h:14
QStringList RemoteRecordings(void)
Definition: remoteutil.cpp:382