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