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