MythTV  master
remotefile.cpp
Go to the documentation of this file.
1 #include <iostream>
2 
3 #include <QFile>
4 #include <QFileInfo>
5 #include <QRegularExpression>
6 #include <QUrl>
7 
8 // POSIX C headers
9 #include <unistd.h>
10 #include <fcntl.h>
11 
12 #include "mythconfig.h"
13 
14 #ifndef O_LARGEFILE
15 static constexpr int8_t O_LARGEFILE { 0 };
16 #endif
17 
18 #include "mythdb.h"
19 #include "remotefile.h"
20 #include "mythcorecontext.h"
21 #include "mythsocket.h"
22 #include "compat.h"
23 #include "mythtimer.h"
24 #include "mythdate.h"
25 #include "mythmiscutil.h"
26 #include "mythlogging.h"
27 #include "threadedfilewriter.h"
28 #include "storagegroup.h"
29 
30 static constexpr std::chrono::milliseconds MAX_FILE_CHECK { 500ms };
31 
32 static bool RemoteSendReceiveStringList(const QString &host, QStringList &strlist)
33 {
34  bool ok = false;
35 
37  {
38  // since the master backend cannot connect back around to
39  // itself, and the libraries do not have access to the list
40  // of connected slave backends to query an existing connection
41  // start up a new temporary connection directly to the slave
42  // backend to query the file list
43  QString ann = QString("ANN Playback %1 0")
44  .arg(gCoreContext->GetHostName());
45  QString addr = gCoreContext->GetBackendServerIP(host);
46  int port = gCoreContext->GetBackendServerPort(host);
47  bool mismatch = false;
48 
50  addr, port, ann, &mismatch);
51  if (sock)
52  {
53  ok = sock->SendReceiveStringList(strlist);
54  sock->DecrRef();
55  }
56  else
57  {
58  strlist.clear();
59  }
60  }
61  else
62  {
63  ok = gCoreContext->SendReceiveStringList(strlist);
64  }
65 
66  return ok;
67 }
68 
69 RemoteFile::RemoteFile(QString url, bool write, bool usereadahead,
70  std::chrono::milliseconds timeout,
71  const QStringList *possibleAuxiliaryFiles) :
72  m_path(std::move(url)),
73  m_useReadAhead(usereadahead), m_timeoutMs(timeout),
74  m_writeMode(write)
75 {
76  if (m_writeMode)
77  {
78  m_useReadAhead = false;
79  m_timeoutMs = -1ms;
80  }
81  else if (possibleAuxiliaryFiles)
82  {
83  m_possibleAuxFiles = *possibleAuxiliaryFiles;
84  }
85 
86  if (!m_path.isEmpty())
87  Open();
88 
89  LOG(VB_FILE, LOG_DEBUG, QString("RemoteFile(%1)").arg(m_path));
90 }
91 
93 {
94  Close();
95  if (m_controlSock)
96  {
98  m_controlSock = nullptr;
99  }
100  if (m_sock)
101  {
102  m_sock->DecrRef();
103  m_sock = nullptr;
104  }
105 }
106 
107 bool RemoteFile::isLocal(const QString &lpath)
108 {
109  bool is_local = !lpath.isEmpty() &&
110  !lpath.startsWith("myth:") &&
111  (lpath.startsWith("/") || QFile::exists(lpath));
112  return is_local;
113 }
114 
115 bool RemoteFile::isLocal(void) const
116 {
117  return isLocal(m_path);
118 }
119 
121 {
122  QUrl qurl(m_path);
123  QString dir;
124 
125  QString host = qurl.host();
126  int port = qurl.port();
127 
128  dir = qurl.path();
129 
130  if (qurl.hasQuery())
131  dir += "?" + QUrl::fromPercentEncoding(
132  qurl.query(QUrl::FullyEncoded).toLocal8Bit());
133 
134  if (qurl.hasFragment())
135  dir += "#" + qurl.fragment();
136 
137  QString sgroup = qurl.userName();
138 
139  auto *lsock = new MythSocket();
140  QString stype = (control) ? "control socket" : "file data socket";
141 
142  QString loc = QString("RemoteFile::openSocket(%1): ").arg(stype);
143 
144  if (port <= 0)
145  {
146  port = gCoreContext->GetBackendServerPort(host);
147  }
148 
149  if (!lsock->ConnectToHost(host, port))
150  {
151  LOG(VB_GENERAL, LOG_ERR, loc +
152  QString("Could not connect to server %1:%2") .arg(host).arg(port));
153  lsock->DecrRef();
154  return nullptr;
155  }
156 
157  QString hostname = GetMythDB()->GetHostName();
158 
159  QStringList strlist;
160 
161 #ifndef IGNORE_PROTO_VER_MISMATCH
162  if (!gCoreContext->CheckProtoVersion(lsock, 5s))
163  {
164  LOG(VB_GENERAL, LOG_ERR, loc +
165  QString("Failed validation to server %1:%2").arg(host).arg(port));
166  lsock->DecrRef();
167  return nullptr;
168  }
169 #endif
170 
171  if (control)
172  {
173  strlist.append(QString("ANN Playback %1 %2")
174  .arg(hostname).arg(static_cast<int>(false)));
175  if (!lsock->SendReceiveStringList(strlist))
176  {
177  LOG(VB_GENERAL, LOG_ERR, loc +
178  QString("Could not read string list from server %1:%2")
179  .arg(host).arg(port));
180  lsock->DecrRef();
181  return nullptr;
182  }
183  }
184  else
185  {
186  strlist.push_back(QString("ANN FileTransfer %1 %2 %3 %4")
187  .arg(hostname).arg(static_cast<int>(m_writeMode))
188  .arg(static_cast<int>(m_useReadAhead)).arg(m_timeoutMs.count()));
189  strlist << QString("%1").arg(dir);
190  strlist << sgroup;
191 
192  for (const auto& fname : std::as_const(m_possibleAuxFiles))
193  strlist << fname;
194 
195  if (!lsock->SendReceiveStringList(strlist))
196  {
197  LOG(VB_GENERAL, LOG_ERR, loc +
198  QString("Did not get proper response from %1:%2")
199  .arg(host).arg(port));
200  strlist.clear();
201  strlist.push_back("ERROR");
202  strlist.push_back("invalid response");
203  }
204 
205  if (strlist.size() >= 3)
206  {
207  auto it = strlist.begin(); ++it;
208  m_recorderNum = (*it).toInt(); ++it;
209  m_fileSize = (*(it)).toLongLong(); ++it;
210  for (; it != strlist.end(); ++it)
211  m_auxFiles << *it;
212  }
213  else if (!strlist.isEmpty() && strlist.size() < 3 &&
214  strlist[0] != "ERROR")
215  {
216  LOG(VB_GENERAL, LOG_ERR, loc +
217  QString("Did not get proper response from %1:%2")
218  .arg(host).arg(port));
219  strlist.clear();
220  strlist.push_back("ERROR");
221  strlist.push_back("invalid response");
222  }
223  }
224 
225  if (strlist.isEmpty() || strlist[0] == "ERROR")
226  {
227  lsock->DecrRef();
228  lsock = nullptr;
229  if (strlist.isEmpty())
230  {
231  LOG(VB_GENERAL, LOG_ERR, loc + "Failed to open socket, timeout");
232  }
233  else
234  {
235  LOG(VB_GENERAL, LOG_ERR, loc + "Failed to open socket" +
236  ((strlist.size() >= 2) ?
237  QString(", error was %1").arg(strlist[1]) :
238  QString(", remote error")));
239  }
240  }
241 
242  return lsock;
243 }
244 
245 bool RemoteFile::isOpen() const
246 {
247  if (isLocal())
248  {
249  return m_writeMode ? (m_fileWriter != nullptr) : (m_localFile != -1);
250  }
251  return m_sock && m_controlSock;
252 }
253 
255 {
256  if (isOpen())
257  return true;
258 
259  QMutexLocker locker(&m_lock);
260  return OpenInternal();
261 }
262 
268 {
269  if (isLocal())
270  {
271  if (m_writeMode)
272  {
273  // make sure the directories are created if necessary
274  QFileInfo fi(m_path);
275  QDir dir(fi.path());
276  if (!dir.exists())
277  {
278  LOG(VB_FILE, LOG_WARNING, QString("RemoteFile::Open(%1) creating directories")
279  .arg(m_path));
280 
281  if (!dir.mkpath(fi.path()))
282  {
283  LOG(VB_GENERAL, LOG_ERR, QString("RemoteFile::Open(%1) failed to create the directories")
284  .arg(m_path));
285  return false;
286  }
287  }
288 
290  O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE,
291  0644);
292 
293  if (!m_fileWriter->Open())
294  {
295  delete m_fileWriter;
296  m_fileWriter = nullptr;
297  LOG(VB_FILE, LOG_ERR, QString("RemoteFile::Open(%1) write mode error")
298  .arg(m_path));
299  return false;
300  }
301  SetBlocking();
302  return true;
303  }
304 
305  // local mode, read only
306  if (!Exists(m_path))
307  {
308  LOG(VB_FILE, LOG_ERR,
309  QString("RemoteFile::Open(%1) Error: Does not exist").arg(m_path));
310  return false;
311  }
312 
313  m_localFile = ::open(m_path.toLocal8Bit().constData(), O_RDONLY);
314  if (m_localFile == -1)
315  {
316  LOG(VB_FILE, LOG_ERR, QString("RemoteFile::Open(%1) Error: %2")
317  .arg(m_path, strerror(errno)));
318  return false;
319  }
320  return true;
321  }
322  m_controlSock = openSocket(true);
323  if (!m_controlSock)
324  return false;
325 
326  m_sock = openSocket(false);
327  if (!m_sock)
328  {
329  // Close the sockets if we received an error so that isOpen() will
330  // return false if the caller tries to use the RemoteFile.
331  Close(true);
332  return false;
333  }
334  m_canResume = true;
335 
336  return true;
337 }
338 
339 bool RemoteFile::ReOpen(const QString& newFilename)
340 {
341  if (isLocal())
342  {
343  if (isOpen())
344  {
345  Close();
346  }
347  m_path = newFilename;
348  return Open();
349  }
350 
351  QMutexLocker locker(&m_lock);
352 
353  if (!CheckConnection(false))
354  {
355  LOG(VB_NETWORK, LOG_ERR, "RemoteFile::ReOpen(): Couldn't connect");
356  return false;
357  }
358 
359  QStringList strlist( m_query.arg(m_recorderNum) );
360  strlist << "REOPEN";
361  strlist << newFilename;
362 
364 
365  m_lock.unlock();
366 
367  bool retval = false;
368  if (!strlist.isEmpty())
369  retval = (strlist[0].toInt() != 0);
370 
371  return retval;
372 }
373 
374 void RemoteFile::Close(bool haslock)
375 {
376  if (isLocal())
377  {
378  if (m_localFile >= 0)
380  m_localFile = -1;
381  delete m_fileWriter;
382  m_fileWriter = nullptr;
383  return;
384  }
385  if (!m_controlSock)
386  return;
387 
388  QStringList strlist( m_query.arg(m_recorderNum) );
389  strlist << "DONE";
390 
391  if (!haslock)
392  {
393  m_lock.lock();
394  }
396  strlist, 0, MythSocket::kShortTimeout))
397  {
398  LOG(VB_GENERAL, LOG_ERR, "Remote file timeout.");
399  }
400 
401  if (m_sock)
402  {
403  m_sock->DecrRef();
404  m_sock = nullptr;
405  }
406  if (m_controlSock)
407  {
409  m_controlSock = nullptr;
410  }
411 
412  if (!haslock)
413  {
414  m_lock.unlock();
415  }
416 }
417 
418 bool RemoteFile::DeleteFile(const QString &url)
419 {
420  if (isLocal(url))
421  {
422  QFile file(url);
423  return file.remove();
424  }
425 
426  bool result = false;
427  QUrl qurl(url);
428  QString filename = qurl.path();
429  QString sgroup = qurl.userName();
430 
431  if (!qurl.fragment().isEmpty() || url.endsWith("#"))
432  filename = filename + "#" + qurl.fragment();
433 
434  if (filename.startsWith("/"))
435  filename = filename.right(filename.length()-1);
436 
437  if (filename.isEmpty() || sgroup.isEmpty())
438  return false;
439 
440  QStringList strlist("DELETE_FILE");
441  strlist << filename;
442  strlist << sgroup;
443 
445 
446  if (!strlist.isEmpty() && strlist[0] == "1")
447  result = true;
448 
449  return result;
450 }
451 
452 bool RemoteFile::Exists(const QString &url)
453 {
454  if (url.isEmpty())
455  return false;
456 
457  struct stat fileinfo {};
458  return Exists(url, &fileinfo);
459 }
460 
461 bool RemoteFile::Exists(const QString &url, struct stat *fileinfo)
462 {
463  if (url.isEmpty())
464  return false;
465 
466  QUrl qurl(url);
467  QString filename = qurl.path();
468  QString sgroup = qurl.userName();
469  QString host = qurl.host();
470 
471  if (isLocal(url) || gCoreContext->IsThisBackend(host))
472  {
473  LOG(VB_FILE, LOG_INFO,
474  QString("RemoteFile::Exists(): looking for local file: %1").arg(url));
475 
476  bool fileExists = false;
477  QString fullFilePath = "";
478 
479  if (url.startsWith("myth:"))
480  {
481  StorageGroup sGroup(sgroup, gCoreContext->GetHostName());
482  fullFilePath = sGroup.FindFile(filename);
483  if (!fullFilePath.isEmpty())
484  fileExists = true;
485  }
486  else
487  {
488  QFileInfo info(url);
489  fileExists = info.exists() /*&& info.isFile()*/;
490  fullFilePath = url;
491  }
492 
493  if (fileExists)
494  {
495  if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
496  {
497  LOG(VB_FILE, LOG_ERR,
498  QString("RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) + ENO);
499  }
500  }
501 
502  return fileExists;
503  }
504 
505  LOG(VB_FILE, LOG_INFO,
506  QString("RemoteFile::Exists(): looking for remote file: %1").arg(url));
507 
508  if (!qurl.fragment().isEmpty() || url.endsWith("#"))
509  filename = filename + "#" + qurl.fragment();
510 
511  if (filename.startsWith("/"))
512  filename = filename.right(filename.length()-1);
513 
514  if (filename.isEmpty())
515  return false;
516 
517  QStringList strlist("QUERY_FILE_EXISTS");
518  strlist << filename;
519  if (!sgroup.isEmpty())
520  strlist << sgroup;
521 
522  bool result = false;
523  if (RemoteSendReceiveStringList(host, strlist) && strlist[0] == "1")
524  {
525  if ((strlist.size() >= 15) && fileinfo)
526  {
527  fileinfo->st_dev = strlist[2].toLongLong();
528  fileinfo->st_ino = strlist[3].toLongLong();
529  fileinfo->st_mode = strlist[4].toLongLong();
530  fileinfo->st_nlink = strlist[5].toLongLong();
531  fileinfo->st_uid = strlist[6].toLongLong();
532  fileinfo->st_gid = strlist[7].toLongLong();
533  fileinfo->st_rdev = strlist[8].toLongLong();
534  fileinfo->st_size = strlist[9].toLongLong();
535 #ifndef _WIN32
536  fileinfo->st_blksize = strlist[10].toLongLong();
537  fileinfo->st_blocks = strlist[11].toLongLong();
538 #endif
539  fileinfo->st_atime = strlist[12].toLongLong();
540  fileinfo->st_mtime = strlist[13].toLongLong();
541  fileinfo->st_ctime = strlist[14].toLongLong();
542  result = true;
543  }
544  else if (!fileinfo)
545  {
546  result = true;
547  }
548  }
549 
550  return result;
551 }
552 
553 QString RemoteFile::GetFileHash(const QString &url)
554 {
555  if (isLocal(url))
556  {
557  return FileHash(url);
558  }
559  QString result;
560  QUrl qurl(url);
561  QString filename = qurl.path();
562  QString hostname = qurl.host();
563  QString sgroup = qurl.userName();
564 
565  if (!qurl.fragment().isEmpty() || url.endsWith("#"))
566  filename = filename + "#" + qurl.fragment();
567 
568  if (filename.startsWith("/"))
569  filename = filename.right(filename.length()-1);
570 
571  if (filename.isEmpty() || sgroup.isEmpty())
572  return {};
573 
574  QStringList strlist("QUERY_FILE_HASH");
575  strlist << filename;
576  strlist << sgroup;
577  strlist << hostname;
578 
580  {
581  result = strlist[0];
582  }
583 
584  return result;
585 }
586 
587 bool RemoteFile::CopyFile (const QString& src, const QString& dst,
588  bool overwrite, bool verify)
589 {
590  LOG(VB_FILE, LOG_INFO,
591  QString("RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
592 
593  // sanity check
594  if (src == dst)
595  {
596  LOG(VB_GENERAL, LOG_ERR, "RemoteFile::CopyFile: Cannot copy a file to itself");
597  return false;
598  }
599 
600  RemoteFile srcFile(src, false);
601  if (!srcFile.isOpen())
602  {
603  LOG(VB_GENERAL, LOG_ERR,
604  QString("RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
605  return false;
606  }
607 
608  const int readSize = 2 * 1024 * 1024;
609  char *buf = new char[readSize];
610  if (!buf)
611  {
612  LOG(VB_GENERAL, LOG_ERR, "RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
613  return false;
614  }
615 
616  if (overwrite)
617  {
618  DeleteFile(dst);
619  }
620  else if (Exists(dst))
621  {
622  LOG(VB_GENERAL, LOG_ERR, "RemoteFile::CopyFile: File already exists");
623  delete[] buf;
624  return false;
625  }
626 
627  RemoteFile dstFile(dst, true);
628  if (!dstFile.isOpen())
629  {
630  LOG(VB_GENERAL, LOG_ERR,
631  QString("RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
632  srcFile.Close();
633  delete[] buf;
634  return false;
635  }
636 
637  dstFile.SetBlocking(true);
638 
639  bool success = true;
640  int srcLen = 0;
641 
642  while ((srcLen = srcFile.Read(buf, readSize)) > 0)
643  {
644  int dstLen = dstFile.Write(buf, srcLen);
645 
646  if (dstLen == -1 || srcLen != dstLen)
647  {
648  LOG(VB_GENERAL, LOG_ERR,
649  "RemoteFile::CopyFile: Error while trying to write to destination file.");
650  success = false;
651  }
652  }
653 
654  srcFile.Close();
655  dstFile.Close();
656  delete[] buf;
657 
658  if (success && verify)
659  {
660  // Check written file is correct size
661  struct stat fileinfo {};
662  long long dstSize = Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
663  long long srcSize = srcFile.GetFileSize();
664  if (dstSize != srcSize)
665  {
666  LOG(VB_GENERAL, LOG_ERR,
667  QString("RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
668  .arg(dstSize).arg(srcSize));
669  success = false;
670  DeleteFile(dst);
671  }
672  }
673 
674  return success;
675 }
676 
677 bool RemoteFile::MoveFile (const QString& src, const QString& dst, bool overwrite)
678 {
679  LOG(VB_FILE, LOG_INFO,
680  QString("RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
681 
682  // sanity check
683  if (src == dst)
684  {
685  LOG(VB_GENERAL, LOG_ERR, "RemoteFile::MoveFile: Cannot move a file to itself");
686  return false;
687  }
688 
689  if (isLocal(src) != isLocal(dst))
690  {
691  // Moving between local & remote requires a copy & delete
692  bool ok = CopyFile(src, dst, overwrite, true);
693  if (ok)
694  {
695  if (!DeleteFile(src))
696  LOG(VB_FILE, LOG_ERR,
697  "RemoteFile::MoveFile: Failed to delete file after successful copy");
698  }
699  return ok;
700  }
701 
702  if (overwrite)
703  {
704  DeleteFile(dst);
705  }
706  else if (Exists(dst))
707  {
708  LOG(VB_GENERAL, LOG_ERR, "RemoteFile::MoveFile: File already exists");
709  return false;
710  }
711 
712  if (isLocal(src))
713  {
714  // Moving local -> local
715  QFileInfo fi(dst);
716  if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
717  return true;
718 
719  LOG(VB_FILE, LOG_ERR, "RemoteFile::MoveFile: Rename failed");
720  return false;
721  }
722 
723  // Moving remote -> remote
724  QUrl srcUrl(src);
725  QUrl dstUrl(dst);
726 
727  if (srcUrl.userName() != dstUrl.userName())
728  {
729  LOG(VB_FILE, LOG_ERR, "RemoteFile::MoveFile: Cannot change a file's Storage Group");
730  return false;
731  }
732 
733  QStringList strlist("MOVE_FILE");
734  strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
735 
737 
738  if (!strlist.isEmpty() && strlist[0] == "1")
739  return true;
740 
741  LOG(VB_FILE, LOG_ERR, QString("RemoteFile::MoveFile: MOVE_FILE failed with: %1")
742  .arg(strlist.join(",")));
743  return false;
744 }
745 
747 {
748  if (isLocal())
749  return;
750  QMutexLocker locker(&m_lock);
751  if (!m_sock)
752  {
753  LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Reset(): Called with no socket");
754  return;
755  }
756  m_sock->Reset();
757 }
758 
759 long long RemoteFile::Seek(long long pos, int whence, long long curpos)
760 {
761  QMutexLocker locker(&m_lock);
762 
763  return SeekInternal(pos, whence, curpos);
764 }
765 
766 long long RemoteFile::SeekInternal(long long pos, int whence, long long curpos)
767 {
768  if (isLocal())
769  {
770  if (!isOpen())
771  {
772  LOG(VB_FILE, LOG_ERR, "RemoteFile::Seek(): Called with no file opened");
773  return -1;
774  }
775  if (m_writeMode)
776  return m_fileWriter->Seek(pos, whence);
777 
778  long long offset = 0LL;
779  if (whence == SEEK_SET)
780  {
781  QFileInfo info(m_path);
782  offset = std::min(pos, info.size());
783  }
784  else if (whence == SEEK_END)
785  {
786  QFileInfo info(m_path);
787  offset = info.size() + pos;
788  }
789  else if (whence == SEEK_CUR)
790  {
791  offset = ((curpos > 0) ? curpos : ::lseek64(m_localFile, 0, SEEK_CUR)) + pos;
792  }
793  else
794  {
795  return -1;
796  }
797 
798  off64_t localpos = ::lseek64(m_localFile, pos, whence);
799  if (localpos != pos)
800  {
801  LOG(VB_FILE, LOG_ERR,
802  QString("RemoteFile::Seek(): Couldn't seek to offset %1")
803  .arg(offset));
804  return -1;
805  }
806  return localpos;
807  }
808 
809  if (!CheckConnection(false))
810  {
811  LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Seek(): Couldn't connect");
812  return -1;
813  }
814 
815  QStringList strlist( m_query.arg(m_recorderNum) );
816  strlist << "SEEK";
817  strlist << QString::number(pos);
818  strlist << QString::number(whence);
819  if (curpos > 0)
820  strlist << QString::number(curpos);
821  else
822  strlist << QString::number(m_readPosition);
823 
824  bool ok = m_controlSock->SendReceiveStringList(strlist);
825 
826  if (ok && !strlist.isEmpty())
827  {
828  m_lastPosition = m_readPosition = strlist[0].toLongLong();
829  m_sock->Reset();
830  return strlist[0].toLongLong();
831  }
832  m_lastPosition = 0LL;
833  return -1;
834 }
835 
836 int RemoteFile::Write(const void *data, int size)
837 {
838  int recv = 0;
839  int sent = 0;
840  unsigned zerocnt = 0;
841  bool error = false;
842  bool response = false;
843 
844  if (!m_writeMode)
845  {
846  LOG(VB_NETWORK, LOG_ERR,
847  "RemoteFile::Write(): Called when not in write mode");
848  return -1;
849  }
850  if (isLocal())
851  {
852  if (!isOpen())
853  {
854  LOG(VB_FILE, LOG_ERR,
855  "RemoteFile::Write(): File not opened");
856  return -1;
857  }
858  return m_fileWriter->Write(data, size);
859  }
860 
861  QMutexLocker locker(&m_lock);
862 
863  if (!CheckConnection())
864  {
865  LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Write(): Couldn't connect");
866  return -1;
867  }
868 
869  QStringList strlist( m_query.arg(m_recorderNum) );
870  strlist << "WRITE_BLOCK";
871  strlist << QString::number(size);
872  bool ok = m_controlSock->WriteStringList(strlist);
873  if (!ok)
874  {
875  LOG(VB_NETWORK, LOG_ERR,
876  "RemoteFile::Write(): Block notification failed");
877  return -1;
878  }
879 
880  recv = size;
881  while (sent < recv && !error && zerocnt++ < 50)
882  {
883  int ret = m_sock->Write((char*)data + sent, recv - sent);
884  if (ret > 0)
885  {
886  sent += ret;
887  }
888  else
889  {
890  LOG(VB_GENERAL, LOG_ERR, "RemoteFile::Write(): socket error");
891  error = true;
892  break;
893  }
894 
897  !strlist.isEmpty())
898  {
899  recv = strlist[0].toInt(); // -1 on backend error
900  response = true;
901  }
902  }
903 
904  if (!error && !response)
905  {
907  !strlist.isEmpty())
908  {
909  recv = strlist[0].toInt(); // -1 on backend error
910  }
911  else
912  {
913  LOG(VB_GENERAL, LOG_ERR,
914  "RemoteFile::Write(): No response from control socket.");
915  recv = -1;
916  }
917  }
918 
919  LOG(VB_NETWORK, LOG_DEBUG,
920  QString("RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
921  .arg(size).arg(sent).arg(recv).arg(error));
922 
923  if (recv < 0)
924  return recv;
925 
926  if (error || recv != sent)
927  {
928  sent = -1;
929  }
930  else
931  {
932  m_lastPosition += sent;
933  }
934 
935  return sent;
936 }
937 
938 int RemoteFile::Read(void *data, int size)
939 {
940  int recv = 0;
941  int sent = 0;
942  bool error = false;
943  bool response = false;
944 
945  QMutexLocker locker(&m_lock);
946 
947  if (isLocal())
948  {
949  if (m_writeMode)
950  {
951  LOG(VB_FILE, LOG_ERR, "RemoteFile:Read() called in writing mode");
952  return -1;
953  }
954  if (isOpen())
955  {
956  return ::read(m_localFile, data, size);
957  }
958  LOG(VB_FILE, LOG_ERR, "RemoteFile:Read() called when local file not opened");
959  return -1;
960  }
961 
962  if (!CheckConnection())
963  {
964  LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Read(): Couldn't connect");
965  return -1;
966  }
967 
968  if (m_sock->IsDataAvailable())
969  {
970  LOG(VB_NETWORK, LOG_ERR,
971  "RemoteFile::Read(): Read socket not empty to start!");
972  m_sock->Reset();
973  }
974 
975  while (m_controlSock->IsDataAvailable())
976  {
977  LOG(VB_NETWORK, LOG_WARNING,
978  "RemoteFile::Read(): Control socket not empty to start!");
979  m_controlSock->Reset();
980  }
981 
982  QStringList strlist( m_query.arg(m_recorderNum) );
983  strlist << "REQUEST_BLOCK";
984  strlist << QString::number(size);
985  bool ok = m_controlSock->WriteStringList(strlist);
986  if (!ok)
987  {
988  LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Read(): Block request failed");
989  return -1;
990  }
991 
992  sent = size;
993 
994  std::chrono::milliseconds waitms { 30ms };
995  MythTimer mtimer;
996  mtimer.start();
997 
998  while (recv < sent && !error && mtimer.elapsed() < 10s)
999  {
1000  int ret = m_sock->Read(((char *)data) + recv, sent - recv, waitms);
1001 
1002  if (ret > 0)
1003  recv += ret;
1004  else if (ret < 0)
1005  error = true;
1006 
1007  waitms += (waitms < 200ms) ? 20ms : 0ms;
1008 
1009  if (m_controlSock->IsDataAvailable() &&
1011  !strlist.isEmpty())
1012  {
1013  sent = strlist[0].toInt(); // -1 on backend error
1014  response = true;
1015  if (ret < sent)
1016  {
1017  // We have received less than what the server sent, retry immediately
1018  ret = m_sock->Read(((char *)data) + recv, sent - recv, waitms);
1019  if (ret > 0)
1020  recv += ret;
1021  else if (ret < 0)
1022  error = true;
1023  }
1024  }
1025  }
1026 
1027  if (!error && !response)
1028  {
1029  // Wait up to 1.5s for the backend to send the size
1030  // MythSocket::ReadString will drop the connection
1031  if (m_controlSock->ReadStringList(strlist, 1500ms) &&
1032  !strlist.isEmpty())
1033  {
1034  sent = strlist[0].toInt(); // -1 on backend error
1035  }
1036  else
1037  {
1038  LOG(VB_GENERAL, LOG_ERR,
1039  "RemoteFile::Read(): No response from control socket.");
1040  // If no data was received from control socket, and we got what we asked for
1041  // assume everything is okay
1042  if (recv == size)
1043  {
1044  sent = recv;
1045  }
1046  else
1047  {
1048  sent = -1;
1049  }
1050  // The TCP socket is dropped if there's a timeout, so we reconnect
1051  if (!Resume())
1052  {
1053  sent = -1;
1054  }
1055  }
1056  }
1057 
1058  LOG(VB_NETWORK, LOG_DEBUG,
1059  QString("Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1060  .arg(size).arg(recv).arg(sent).arg(error));
1061 
1062  if (sent < 0)
1063  return sent;
1064 
1065  if (error || sent != recv)
1066  {
1067  LOG(VB_GENERAL, LOG_WARNING,
1068  QString("RemoteFile::Read(): sent %1 != recv %2")
1069  .arg(sent).arg(recv));
1070  recv = -1;
1071 
1072  // The TCP socket is dropped if there's a timeout, so we reconnect
1073  if (!Resume())
1074  {
1075  LOG(VB_GENERAL, LOG_WARNING, "RemoteFile::Read(): Resume failed.");
1076  }
1077  else
1078  {
1079  LOG(VB_GENERAL, LOG_NOTICE, "RemoteFile::Read(): Resume success.");
1080  }
1081  }
1082  else
1083  {
1084  m_lastPosition += recv;
1085  }
1086 
1087  return recv;
1088 }
1089 
1095 long long RemoteFile::GetFileSize(void) const
1096 {
1097  if (isLocal())
1098  {
1099  if (isOpen() && m_writeMode)
1100  {
1101  m_fileWriter->Flush();
1102  }
1103  if (Exists(m_path))
1104  {
1105  QFileInfo info(m_path);
1106  return info.size();
1107  }
1108  return -1;
1109  }
1110 
1111  QMutexLocker locker(&m_lock);
1112  return m_fileSize;
1113 }
1114 
1124 {
1125  if (isLocal())
1126  {
1127  return GetFileSize();
1128  }
1129 
1130  QMutexLocker locker(&m_lock);
1131 
1132  if (m_completed ||
1134  {
1135  return m_fileSize;
1136  }
1137 
1138  if (!CheckConnection())
1139  {
1140  // Can't establish a new connection, using system one
1141  struct stat fileinfo {};
1142 
1143  if (Exists(m_path, &fileinfo))
1144  {
1145  m_fileSize = fileinfo.st_size;
1146  }
1147  return m_fileSize;
1148  }
1149 
1150  QStringList strlist(m_query.arg(m_recorderNum));
1151  strlist << "REQUEST_SIZE";
1152 
1153  bool ok = m_controlSock->SendReceiveStringList(strlist);
1154 
1155  if (ok && !strlist.isEmpty())
1156  {
1157  bool validate = false;
1158  long long size = strlist[0].toLongLong(&validate);
1159 
1160  if (validate)
1161  {
1162  if (strlist.count() >= 2)
1163  {
1164  m_completed = (strlist[1].toInt() != 0);
1165  }
1166  m_fileSize = size;
1167  }
1168  else
1169  {
1170  struct stat fileinfo {};
1171 
1172  if (Exists(m_path, &fileinfo))
1173  {
1174  m_fileSize = fileinfo.st_size;
1175  }
1176  }
1178  return m_fileSize;
1179  }
1180 
1181  return -1;
1182 }
1183 
1184 bool RemoteFile::SaveAs(QByteArray &data)
1185 {
1186  long long fs = GetRealFileSize();
1187 
1188  if (fs < 0)
1189  return false;
1190 
1191  data.resize(fs);
1192  Read(data.data(), fs);
1193 
1194  return true;
1195 }
1196 
1197 void RemoteFile::SetTimeout(bool fast)
1198 {
1199  if (isLocal())
1200  {
1201  // not much we can do with local accesses
1202  return;
1203  }
1204  if (m_timeoutIsFast == fast)
1205  return;
1206 
1207  QMutexLocker locker(&m_lock);
1208 
1209  // The m_controlSock variable is valid if the CheckConnection
1210  // function returns true. The local case has already been
1211  // handled. The CheckConnection function can call Resume which
1212  // calls Close, which deletes m_controlSock. However, the
1213  // subsequent call to OpenInternal is guaranteed to recreate the
1214  // socket or return false for a non-local connection, and this must
1215  // be a non-local connection if this line of code is executed.
1216  if (!CheckConnection())
1217  {
1218  LOG(VB_NETWORK, LOG_ERR,
1219  "RemoteFile::SetTimeout(): Couldn't connect");
1220  return;
1221  }
1222  if (m_controlSock == nullptr)
1223  return;
1224 
1225  QStringList strlist( m_query.arg(m_recorderNum) );
1226  strlist << "SET_TIMEOUT";
1227  strlist << QString::number((int)fast);
1228 
1230 
1231  m_timeoutIsFast = fast;
1232 }
1233 
1234 QDateTime RemoteFile::LastModified(const QString &url)
1235 {
1236  if (isLocal(url))
1237  {
1238  QFileInfo info(url);
1239  return info.lastModified();
1240  }
1241  QDateTime result;
1242  QUrl qurl(url);
1243  QString filename = qurl.path();
1244  QString sgroup = qurl.userName();
1245 
1246  if (!qurl.fragment().isEmpty() || url.endsWith("#"))
1247  filename = filename + "#" + qurl.fragment();
1248 
1249  if (filename.startsWith("/"))
1250  filename = filename.right(filename.length()-1);
1251 
1252  if (filename.isEmpty() || sgroup.isEmpty())
1253  return result;
1254 
1255  QStringList strlist("QUERY_SG_FILEQUERY");
1256  strlist << qurl.host();
1257  strlist << sgroup;
1258  strlist << filename;
1259 
1261 
1262  if (strlist.size() > 1) {
1263  if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1264  result = MythDate::fromSecsSinceEpoch(strlist[1].toLongLong());
1265  else
1266  result = QDateTime();;
1267  }
1268 
1269  return result;
1270 }
1271 
1272 QDateTime RemoteFile::LastModified(void) const
1273 {
1274  return LastModified(m_path);
1275 }
1276 
1286 QString RemoteFile::FindFile(const QString& filename, const QString& host,
1287  const QString& storageGroup, bool useRegex,
1288  bool allowFallback)
1289 {
1290  QStringList files = RemoteFile::FindFileList(filename, host, storageGroup, useRegex, allowFallback);
1291 
1292  if (!files.isEmpty())
1293  return files[0];
1294 
1295  return {};
1296 }
1297 
1307 QStringList RemoteFile::FindFileList(const QString& filename, const QString& host,
1308  const QString& storageGroup, bool useRegex,
1309  bool allowFallback)
1310 {
1311  LOG(VB_FILE, LOG_INFO, QString("RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1312  "(useregex: %4, allowfallback: %5)")
1313  .arg(filename, host, storageGroup)
1314  .arg(useRegex).arg(allowFallback));
1315 
1316  if (filename.isEmpty() || storageGroup.isEmpty())
1317  return {};
1318 
1319  QStringList strList;
1320  QString hostName = host;
1321 
1322  if (hostName.isEmpty())
1323  hostName = gCoreContext->GetMasterHostName();
1324 
1325  // if we are looking for the file on this host just search the local storage group first
1326  if (gCoreContext->IsThisBackend(hostName))
1327  {
1328  // We could have made it this far with an IP when we really want
1329  // a hostname
1330  hostName = gCoreContext->GetHostName();
1331  StorageGroup sgroup(storageGroup, hostName);
1332 
1333  if (useRegex)
1334  {
1335  QFileInfo fi(filename);
1336  QStringList files = sgroup.GetFileList('/' + fi.path());
1337 
1338  LOG(VB_FILE, LOG_INFO, QString("RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1339  .arg(fi.path(), fi.fileName()));
1340 
1341  for (int x = 0; x < files.size(); x++)
1342  {
1343  LOG(VB_FILE, LOG_INFO, QString("RemoteFile::FindFileList: Found '%1 - %2'")
1344  .arg(x).arg(files[x]));
1345  }
1346 
1347  QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1348  for (const QString& file : std::as_const(filteredFiles))
1349  {
1352  fi.path() + '/' + file,
1353  storageGroup);
1354  }
1355  }
1356  else
1357  {
1358  if (!sgroup.FindFile(filename).isEmpty())
1359  {
1360  strList << MythCoreContext::GenMythURL(hostName,
1362  filename, storageGroup);
1363  }
1364  }
1365 
1366  if (!strList.isEmpty() || !allowFallback)
1367  return strList;
1368  }
1369 
1370  // if we didn't find any files ask the master BE to find it
1371  if (strList.isEmpty() && !gCoreContext->IsMasterBackend())
1372  {
1373  strList << "QUERY_FINDFILE" << hostName << storageGroup << filename
1374  << (useRegex ? "1" : "0")
1375  << "1";
1376 
1377  if (gCoreContext->SendReceiveStringList(strList))
1378  {
1379  if (!strList.empty() && !strList[0].isEmpty() &&
1380  strList[0] != "NOT FOUND" && !strList[0].startsWith("ERROR: "))
1381  return strList;
1382  }
1383  }
1384 
1385  return {};
1386 }
1387 
1393 bool RemoteFile::SetBlocking(bool block)
1394 {
1395  if (m_fileWriter)
1396  {
1397  return m_fileWriter->SetBlocking(block);
1398  }
1399  return true;
1400 }
1401 
1409 {
1410  if (IsConnected())
1411  {
1412  return true;
1413  }
1414  if (!m_canResume)
1415  {
1416  return false;
1417  }
1418  return Resume(repos);
1419 }
1420 
1426 {
1427  return m_sock && m_controlSock &&
1429 }
1430 
1436 bool RemoteFile::Resume(bool repos)
1437 {
1438  Close(true);
1439  if (!OpenInternal())
1440  return false;
1441 
1442  if (repos)
1443  {
1445  if (SeekInternal(m_lastPosition, SEEK_SET) < 0)
1446  {
1447  Close(true);
1448  LOG(VB_FILE, LOG_ERR,
1449  QString("RemoteFile::Resume: Enable to re-seek into last known "
1450  "position (%1").arg(m_lastPosition));
1451  return false;
1452  }
1453  }
1455  return true;
1456 }
1457 
1458 /* vim: set expandtab tabstop=4 shiftwidth=4: */
RemoteFile::LastModified
QDateTime LastModified(void) const
Definition: remotefile.cpp:1272
RemoteFile::isLocal
bool isLocal(void) const
Definition: remotefile.cpp:115
O_LARGEFILE
static constexpr int8_t O_LARGEFILE
Definition: remotefile.cpp:15
MythTimer::elapsed
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
RemoteFile::OpenInternal
bool OpenInternal(void)
Attempts to resume from a disconnected step.
Definition: remotefile.cpp:267
RemoteFile::SaveAs
bool SaveAs(QByteArray &data)
Definition: remotefile.cpp:1184
ThreadedFileWriter::Seek
long long Seek(long long pos, int whence)
Seek to a position within stream; May be unsafe.
Definition: threadedfilewriter.cpp:297
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:101
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:813
RemoteFile::m_path
QString m_path
Definition: remotefile.h:78
RemoteFile::GetFileSize
long long GetFileSize(void) const
GetFileSize: returns the remote file's size at the time it was first opened Will query the server in ...
Definition: remotefile.cpp:1095
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:124
mythdb.h
MythCoreContext::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Definition: mythcorecontext.cpp:1381
RemoteFile::ReOpen
bool ReOpen(const QString &newFilename)
Definition: remotefile.cpp:339
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:461
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:597
MythSocket::Reset
void Reset(void)
Definition: mythsocket.cpp:541
mythburn.write
def write(text, progress=True)
Definition: mythburn.py:307
RemoteFile::Resume
bool Resume(bool repos=true)
Attempts to resume from a disconnected step.
Definition: remotefile.cpp:1436
xbmcvfs.exists
bool exists(str path)
Definition: xbmcvfs.py:51
MythTimer::isRunning
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
Definition: mythtimer.cpp:135
RemoteFile::m_lastSizeCheck
MythTimer m_lastSizeCheck
Definition: remotefile.h:95
RemoteFile
Definition: remotefile.h:17
RemoteFile::Open
bool Open(void)
Definition: remotefile.cpp:254
ThreadedFileWriter::Flush
void Flush(void)
Allow DiskLoop() to flush buffer completely ignoring low watermark.
Definition: threadedfilewriter.cpp:318
MythCoreContext::CheckProtoVersion
bool CheckProtoVersion(MythSocket *socket, std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
Definition: mythcorecontext.cpp:1671
MythTimer::start
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
RemoteFile::Write
int Write(const void *data, int size)
Definition: remotefile.cpp:836
RemoteFile::m_fileWriter
ThreadedFileWriter * m_fileWriter
Definition: remotefile.h:100
GetMythDB
MythDB * GetMythDB(void)
Definition: mythdb.cpp:51
RemoteFile::isOpen
bool isOpen(void) const
Definition: remotefile.cpp:245
build_compdb.file
file
Definition: build_compdb.py:55
RemoteFile::m_controlSock
MythSocket * m_controlSock
Definition: remotefile.h:89
RemoteFile::FindFileList
static QStringList FindFileList(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for files in the give storage group.
Definition: remotefile.cpp:1307
MythSocket::IsConnected
bool IsConnected(void) const
Definition: mythsocket.cpp:551
RemoteFile::SetBlocking
bool SetBlocking(bool m_block=true)
Set write blocking mode for the ThreadedFileWriter instance.
Definition: remotefile.cpp:1393
MAX_FILE_CHECK
static constexpr std::chrono::milliseconds MAX_FILE_CHECK
Definition: remotefile.cpp:30
close
#define close
Definition: compat.h:43
RemoteFile::m_canResume
bool m_canResume
Definition: remotefile.h:85
MythSocket
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:25
MythCoreContext::IsMasterBackend
bool IsMasterBackend(void)
is this the actual MBE process
Definition: mythcorecontext.cpp:701
threadedfilewriter.h
mythdate.h
StorageGroup::GetFileList
QStringList GetFileList(const QString &Path, bool recursive=false)
Definition: storagegroup.cpp:271
MythDate::fromSecsSinceEpoch
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(int64_t seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
Definition: mythdate.cpp:81
mythlogging.h
RemoteFile::Seek
long long Seek(long long pos, int whence, long long curpos=-1)
Definition: remotefile.cpp:759
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:766
MythSocket::WriteStringList
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:301
remotefile.h
MythCoreContext::GetBackendServerPort
int GetBackendServerPort(void)
Returns the locally defined backend control port.
Definition: mythcorecontext.cpp:1070
RemoteFile::m_sock
MythSocket * m_sock
Definition: remotefile.h:90
compat.h
MythCoreContext::GetBackendServerIP
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
Definition: mythcorecontext.cpp:1010
MythTimer::restart
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
RemoteFile::Reset
void Reset(void)
Definition: remotefile.cpp:746
RemoteFile::SeekInternal
long long SeekInternal(long long pos, int whence, long long curpos=-1)
Definition: remotefile.cpp:766
RemoteFile::GetFileHash
static QString GetFileHash(const QString &url)
Definition: remotefile.cpp:553
RemoteFile::FindFile
static QString FindFile(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for a file in the give storage group.
Definition: remotefile.cpp:1286
storagegroup.h
RemoteFile::m_lock
QMutex m_lock
Definition: remotefile.h:88
RemoteFile::m_auxFiles
QStringList m_auxFiles
Definition: remotefile.h:98
RemoteFile::m_readPosition
long long m_readPosition
Definition: remotefile.h:83
MythSocket::kShortTimeout
static constexpr std::chrono::milliseconds kShortTimeout
Definition: mythsocket.h:70
MythCoreContext::ConnectCommandSocket
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, std::chrono::milliseconds setup_timeout=-1ms)
Definition: mythcorecontext.cpp:433
RemoteFile::DeleteFile
static bool DeleteFile(const QString &url)
Definition: remotefile.cpp:418
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
RemoteFile::SetTimeout
void SetTimeout(bool fast)
Definition: remotefile.cpp:1197
MythSocket::Read
int Read(char *data, int size, std::chrono::milliseconds max_wait)
Definition: mythsocket.cpp:527
RemoteFile::CopyFile
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:587
hardwareprofile.smolt.error
def error(message)
Definition: smolt.py:409
ThreadedFileWriter::Write
int Write(const void *data, uint count)
Writes data to the end of the write buffer.
Definition: threadedfilewriter.cpp:190
RemoteFile::m_lastPosition
long long m_lastPosition
Definition: remotefile.h:84
ThreadedFileWriter
This class supports the writing of recordings to disk.
Definition: threadedfilewriter.h:42
RemoteFile::m_timeoutMs
std::chrono::milliseconds m_timeoutMs
Definition: remotefile.h:80
MythSocket::Write
int Write(const char *data, int size)
Definition: mythsocket.cpp:514
mythmiscutil.h
mythcorecontext.h
RemoteFile::m_completed
bool m_completed
Definition: remotefile.h:94
RemoteFile::m_recorderNum
int m_recorderNum
Definition: remotefile.h:86
MythSocket::IsDataAvailable
bool IsDataAvailable(void)
Definition: mythsocket.cpp:557
RemoteFile::MoveFile
static bool MoveFile(const QString &src, const QString &dst, bool overwrite=false)
Definition: remotefile.cpp:677
StorageGroup
Definition: storagegroup.h:11
RemoteFile::m_fileSize
long long m_fileSize
Definition: remotefile.h:81
mythtimer.h
RemoteFile::m_timeoutIsFast
bool m_timeoutIsFast
Definition: remotefile.h:82
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:844
RemoteFile::Close
void Close(bool haslock=false)
Definition: remotefile.cpp:374
ThreadedFileWriter::Open
bool Open(void)
Opens the file we will be writing to.
Definition: threadedfilewriter.cpp:92
azlyrics.info
dictionary info
Definition: azlyrics.py:7
RemoteFile::m_writeMode
bool m_writeMode
Definition: remotefile.h:93
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
RemoteFile::m_possibleAuxFiles
QStringList m_possibleAuxFiles
Definition: remotefile.h:97
RemoteFile::IsConnected
bool IsConnected(void)
Check if both the control and data sockets are currently connected.
Definition: remotefile.cpp:1425
ThreadedFileWriter::SetBlocking
bool SetBlocking(bool block=true)
Set write blocking mode While in blocking mode, ThreadedFileWriter::Write will wait for buffers to be...
Definition: threadedfilewriter.cpp:613
RemoteFile::m_query
QString m_query
Definition: remotefile.h:91
build_compdb.filename
filename
Definition: build_compdb.py:21
FileHash
QString FileHash(const QString &filename)
Definition: mythmiscutil.cpp:548
RemoteFile::m_localFile
int m_localFile
Definition: remotefile.h:99
RemoteFile::m_useReadAhead
bool m_useReadAhead
Definition: remotefile.h:79
mythsocket.h
RemoteFile::~RemoteFile
~RemoteFile()
Definition: remotefile.cpp:92
MythSocket::ReadStringList
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:313
RemoteSendReceiveStringList
static bool RemoteSendReceiveStringList(const QString &host, QStringList &strlist)
Definition: remotefile.cpp:32
MythCoreContext::IsThisBackend
bool IsThisBackend(const QString &addr)
is this address mapped to this backend host
Definition: mythcorecontext.cpp:722
RemoteFile::RemoteFile
RemoteFile(QString url="", bool write=false, bool usereadahead=true, std::chrono::milliseconds timeout=2s, const QStringList *possibleAuxiliaryFiles=nullptr)
Definition: remotefile.cpp:69
MythSocket::SendReceiveStringList
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, std::chrono::milliseconds timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:326
RemoteFile::openSocket
MythSocket * openSocket(bool control)
Definition: remotefile.cpp:120
RemoteFile::CheckConnection
bool CheckConnection(bool repos=true)
Check current connection and re-establish it if lost.
Definition: remotefile.cpp:1408
RemoteFile::Read
int Read(void *data, int size)
Definition: remotefile.cpp:938
RemoteFile::GetRealFileSize
long long GetRealFileSize(void)
GetRealFileSize: returns the current remote file's size.
Definition: remotefile.cpp:1123