4#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
5#include <QtSystemDetection>
9#include <QRegularExpression>
16#include "mythconfig.h"
47 QString ann = QString(
"ANN Playback %1 0")
51 bool mismatch =
false;
54 addr, port, ann, &mismatch);
74 std::chrono::milliseconds
timeout,
75 const QStringList *possibleAuxiliaryFiles) :
76 m_path(
std::move(url)),
77 m_useReadAhead(usereadahead), m_timeoutMs(
timeout),
85 else if (possibleAuxiliaryFiles)
93 LOG(VB_FILE, LOG_DEBUG, QString(
"RemoteFile(%1)").arg(
m_path));
113 bool is_local = !lpath.isEmpty() &&
114 !lpath.startsWith(
"myth:") &&
129 QString host = qurl.host();
130 int port = qurl.port();
135 dir +=
"?" + QUrl::fromPercentEncoding(
136 qurl.query(QUrl::FullyEncoded).toLocal8Bit());
138 if (qurl.hasFragment())
139 dir +=
"#" + qurl.fragment();
141 QString sgroup = qurl.userName();
144 QString stype = (control) ?
"control socket" :
"file data socket";
146 QString loc = QString(
"RemoteFile::openSocket(%1): ").arg(stype);
153 if (!lsock->ConnectToHost(host, port))
155 LOG(VB_GENERAL, LOG_ERR, loc +
156 QString(
"Could not connect to server %1:%2") .arg(host).arg(port));
165#ifndef IGNORE_PROTO_VER_MISMATCH
168 LOG(VB_GENERAL, LOG_ERR, loc +
169 QString(
"Failed validation to server %1:%2").arg(host).arg(port));
177 strlist.append(QString(
"ANN Playback %1 %2")
178 .arg(
hostname).arg(
static_cast<int>(
false)));
179 if (!lsock->SendReceiveStringList(strlist))
181 LOG(VB_GENERAL, LOG_ERR, loc +
182 QString(
"Could not read string list from server %1:%2")
183 .arg(host).arg(port));
190 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
193 strlist << QString(
"%1").arg(dir);
199 if (!lsock->SendReceiveStringList(strlist))
201 LOG(VB_GENERAL, LOG_ERR, loc +
202 QString(
"Did not get proper response from %1:%2")
203 .arg(host).arg(port));
205 strlist.push_back(
"ERROR");
206 strlist.push_back(
"invalid response");
209 if (strlist.size() >= 3)
211 auto it = strlist.begin(); ++it;
214 for (; it != strlist.end(); ++it)
217 else if (!strlist.isEmpty() && strlist.size() < 3 &&
218 strlist[0] !=
"ERROR")
220 LOG(VB_GENERAL, LOG_ERR, loc +
221 QString(
"Did not get proper response from %1:%2")
222 .arg(host).arg(port));
224 strlist.push_back(
"ERROR");
225 strlist.push_back(
"invalid response");
229 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
233 if (strlist.isEmpty())
235 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
239 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
240 ((strlist.size() >= 2) ?
241 QString(
", error was %1").arg(strlist[1]) :
242 QString(
", remote error")));
263 QMutexLocker locker(&
m_lock);
282 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
285 if (!dir.mkpath(fi.path()))
287 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
301 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
312 LOG(VB_FILE, LOG_ERR,
313 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
320 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
321 .arg(
m_path, strerror(errno)));
355 QMutexLocker locker(&
m_lock);
359 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
365 strlist << newFilename;
372 if (!strlist.isEmpty())
373 retval = (strlist[0].toInt() != 0);
402 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
427 return file.remove();
433 QString sgroup = qurl.userName();
435 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
441 if (
filename.isEmpty() || sgroup.isEmpty())
444 QStringList strlist(
"DELETE_FILE");
450 if (!strlist.isEmpty() && strlist[0] ==
"1")
461 struct stat fileinfo {};
462 return Exists(url, &fileinfo);
472 QString sgroup = qurl.userName();
473 QString host = qurl.host();
477 LOG(VB_FILE, LOG_INFO,
478 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
480 bool fileExists =
false;
481 QString fullFilePath =
"";
483 if (url.startsWith(
"myth:"))
487 if (!fullFilePath.isEmpty())
493 fileExists =
info.exists() ;
499 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
501 LOG(VB_FILE, LOG_ERR,
502 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
509 LOG(VB_FILE, LOG_INFO,
510 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
512 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
521 QStringList strlist(
"QUERY_FILE_EXISTS");
523 if (!sgroup.isEmpty())
529 if ((strlist.size() >= 15) && fileinfo)
531 fileinfo->st_dev = strlist[2].toLongLong();
532 fileinfo->st_ino = strlist[3].toLongLong();
533 fileinfo->st_mode = strlist[4].toLongLong();
534 fileinfo->st_nlink = strlist[5].toLongLong();
535 fileinfo->st_uid = strlist[6].toLongLong();
536 fileinfo->st_gid = strlist[7].toLongLong();
537 fileinfo->st_rdev = strlist[8].toLongLong();
538 fileinfo->st_size = strlist[9].toLongLong();
540 fileinfo->st_blksize = strlist[10].toLongLong();
541 fileinfo->st_blocks = strlist[11].toLongLong();
543 fileinfo->st_atime = strlist[12].toLongLong();
544 fileinfo->st_mtime = strlist[13].toLongLong();
545 fileinfo->st_ctime = strlist[14].toLongLong();
567 QString sgroup = qurl.userName();
569 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
575 if (
filename.isEmpty() || sgroup.isEmpty())
578 QStringList strlist(
"QUERY_FILE_HASH");
592 bool overwrite,
bool verify)
594 LOG(VB_FILE, LOG_INFO,
595 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
600 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
607 LOG(VB_GENERAL, LOG_ERR,
608 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
612 const int readSize = 2 * 1024 * 1024;
613 char *buf =
new char[readSize];
616 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
626 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
634 LOG(VB_GENERAL, LOG_ERR,
635 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
646 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
648 int dstLen = dstFile.
Write(buf, srcLen);
650 if (dstLen == -1 || srcLen != dstLen)
652 LOG(VB_GENERAL, LOG_ERR,
653 "RemoteFile::CopyFile: Error while trying to write to destination file.");
662 if (success && verify)
665 struct stat fileinfo {};
666 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
668 if (dstSize != srcSize)
670 LOG(VB_GENERAL, LOG_ERR,
671 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
672 .arg(dstSize).arg(srcSize));
683 LOG(VB_FILE, LOG_INFO,
684 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
689 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
696 bool ok =
CopyFile(src, dst, overwrite,
true);
700 LOG(VB_FILE, LOG_ERR,
701 "RemoteFile::MoveFile: Failed to delete file after successful copy");
712 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
720 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
723 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
731 if (srcUrl.userName() != dstUrl.userName())
733 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
737 QStringList strlist(
"MOVE_FILE");
738 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
742 if (!strlist.isEmpty() && strlist[0] ==
"1")
745 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
746 .arg(strlist.join(
",")));
754 QMutexLocker locker(&
m_lock);
757 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
765 QMutexLocker locker(&
m_lock);
776 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
782 long long offset = 0LL;
783 if (whence == SEEK_SET)
786 offset = std::min(pos,
info.size());
788 else if (whence == SEEK_END)
791 offset =
info.size() + pos;
793 else if (whence == SEEK_CUR)
795 offset = ((curpos > 0) ? curpos : lseek(
m_localFile, 0, SEEK_CUR)) + pos;
805 LOG(VB_FILE, LOG_ERR,
806 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
815 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
821 strlist << QString::number(pos);
822 strlist << QString::number(whence);
824 strlist << QString::number(curpos);
830 if (ok && !strlist.isEmpty())
834 return strlist[0].toLongLong();
844 unsigned zerocnt = 0;
846 bool response =
false;
850 LOG(VB_NETWORK, LOG_ERR,
851 "RemoteFile::Write(): Called when not in write mode");
858 LOG(VB_FILE, LOG_ERR,
859 "RemoteFile::Write(): File not opened");
865 QMutexLocker locker(&
m_lock);
869 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
874 strlist <<
"WRITE_BLOCK";
875 strlist << QString::number(size);
879 LOG(VB_NETWORK, LOG_ERR,
880 "RemoteFile::Write(): Block notification failed");
885 while (sent < recv && !
error && zerocnt++ < 50)
887 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
894 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
903 recv = strlist[0].toInt();
908 if (!
error && !response)
913 recv = strlist[0].toInt();
917 LOG(VB_GENERAL, LOG_ERR,
918 "RemoteFile::Write(): No response from control socket.");
923 LOG(VB_NETWORK, LOG_DEBUG,
924 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
925 .arg(size).arg(sent).arg(recv).arg(
error));
930 if (
error || recv != sent)
947 bool response =
false;
949 QMutexLocker locker(&
m_lock);
955 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
962 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
968 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
974 LOG(VB_NETWORK, LOG_ERR,
975 "RemoteFile::Read(): Read socket not empty to start!");
981 LOG(VB_NETWORK, LOG_WARNING,
982 "RemoteFile::Read(): Control socket not empty to start!");
987 strlist <<
"REQUEST_BLOCK";
988 strlist << QString::number(size);
992 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
998 std::chrono::milliseconds waitms { 30ms };
1002 while (recv < sent && !
error && mtimer.
elapsed() < 10s)
1004 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1011 waitms += (waitms < 200ms) ? 20ms : 0ms;
1017 sent = strlist[0].toInt();
1022 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1031 if (!
error && !response)
1038 sent = strlist[0].toInt();
1042 LOG(VB_GENERAL, LOG_ERR,
1043 "RemoteFile::Read(): No response from control socket.");
1062 LOG(VB_NETWORK, LOG_DEBUG,
1063 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1064 .arg(size).arg(recv).arg(sent).arg(
error));
1069 if (
error || sent != recv)
1071 LOG(VB_GENERAL, LOG_WARNING,
1072 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1073 .arg(sent).arg(recv));
1079 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1083 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1115 QMutexLocker locker(&
m_lock);
1134 QMutexLocker locker(&
m_lock);
1145 struct stat fileinfo {};
1155 strlist <<
"REQUEST_SIZE";
1159 if (ok && !strlist.isEmpty())
1161 bool validate =
false;
1162 long long size = strlist[0].toLongLong(&validate);
1166 if (strlist.count() >= 2)
1174 struct stat fileinfo {};
1196 Read(data.data(), fs);
1211 QMutexLocker locker(&
m_lock);
1222 LOG(VB_NETWORK, LOG_ERR,
1223 "RemoteFile::SetTimeout(): Couldn't connect");
1230 strlist <<
"SET_TIMEOUT";
1231 strlist << QString::number((
int)fast);
1242 QFileInfo
info(url);
1243 return info.lastModified();
1248 QString sgroup = qurl.userName();
1250 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1256 if (
filename.isEmpty() || sgroup.isEmpty())
1259 QStringList strlist(
"QUERY_SG_FILEQUERY");
1260 strlist << qurl.host();
1266 if (strlist.size() > 1) {
1267 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1270 result = QDateTime();;
1291 const QString& storageGroup,
bool useRegex,
1296 if (!files.isEmpty())
1312 const QString& storageGroup,
bool useRegex,
1315 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1316 "(useregex: %4, allowfallback: %5)")
1318 .arg(useRegex).arg(allowFallback));
1320 if (
filename.isEmpty() || storageGroup.isEmpty())
1323 QStringList strList;
1324 QString hostName = host;
1326 if (hostName.isEmpty())
1340 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1342 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1343 .arg(fi.path(), fi.fileName()));
1345 for (
int x = 0; x < files.size(); x++)
1347 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1348 .arg(x).arg(files[x]));
1351 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1352 for (
const QString&
file : std::as_const(filteredFiles))
1356 fi.path() +
'/' +
file,
1370 if (!strList.isEmpty() || !allowFallback)
1377 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1378 << (useRegex ?
"1" :
"0")
1383 if (!strList.empty() && !strList[0].isEmpty() &&
1384 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1452 LOG(VB_FILE, LOG_ERR,
1453 QString(
"RemoteFile::Resume: Enable to re-seek into last known "
1463 const QString &storageGroup,
1466 QStringList strlist(cmd);
1468 strlist << storageGroup;
1473 if (!ok || strlist.size() < 2 || strlist[0] !=
"OK")
1475 LOG(VB_GENERAL, LOG_ERR,
1476 "downloadRemoteFile(): " + cmd +
" returned ERROR!");
1484 const QString &storageGroup,
1491 const QString &storageGroup,
QString GetHostName(void)
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, std::chrono::milliseconds setup_timeout=-1ms)
bool CheckProtoVersion(MythSocket *socket, std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
int GetBackendServerPort(void)
Returns the locally defined backend control port.
bool IsThisBackend(const QString &addr)
is this address mapped to this backend host
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
QString GetMasterHostName(void)
bool IsMasterBackend(void)
is this the actual MBE process
Class for communcating between myth backends and frontends.
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, std::chrono::milliseconds timeoutMS=kLongTimeout)
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
bool IsConnected(void) const
bool IsDataAvailable(void)
static constexpr std::chrono::milliseconds kShortTimeout
int Read(char *data, int size, std::chrono::milliseconds max_wait)
int Write(const char *data, int size)
bool WriteStringList(const QStringList &list)
A QElapsedTimer based timer to replace use of QTime as a timer.
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
void start(void)
starts measuring elapsed time.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
MythSocket * m_controlSock
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
bool ReOpen(const QString &newFilename)
int Read(void *data, int size)
bool OpenInternal(void)
Attempts to resume from a disconnected step.
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.
static QString GetFileHash(const QString &url)
QStringList m_possibleAuxFiles
void Close(bool haslock=false)
static bool MoveFile(const QString &src, const QString &dst, bool overwrite=false)
long long Seek(long long pos, int whence, long long curpos=-1)
long long GetRealFileSize(void)
GetRealFileSize: returns the current remote file's size.
ThreadedFileWriter * m_fileWriter
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.
bool CheckConnection(bool repos=true)
Check current connection and re-establish it if lost.
QDateTime LastModified(void) const
MythTimer m_lastSizeCheck
std::chrono::milliseconds m_timeoutMs
bool SaveAs(QByteArray &data)
bool SetBlocking(bool m_block=true)
Set write blocking mode for the ThreadedFileWriter instance.
MythSocket * openSocket(bool control)
static bool DeleteFile(const QString &url)
static bool Exists(const QString &url, struct stat *fileinfo)
long long SeekInternal(long long pos, int whence, long long curpos=-1)
int Write(const void *data, int size)
RemoteFile(QString url="", bool write=false, bool usereadahead=true, std::chrono::milliseconds timeout=2s, const QStringList *possibleAuxiliaryFiles=nullptr)
bool IsConnected(void)
Check if both the control and data sockets are currently connected.
bool Resume(bool repos=true)
Attempts to resume from a disconnected step.
void SetTimeout(bool fast)
long long GetFileSize(void) const
GetFileSize: returns the remote file's size at the time it was first opened Will query the server in ...
QString FindFile(const QString &filename)
QStringList GetFileList(const QString &Path, bool recursive=false)
This class supports the writing of recordings to disk.
bool SetBlocking(bool block=true)
Set write blocking mode While in blocking mode, ThreadedFileWriter::Write will wait for buffers to be...
long long Seek(long long pos, int whence)
Seek to a position within stream; May be unsafe.
bool Open(void)
Opens the file we will be writing to.
void Flush(void)
Allow DiskLoop() to flush buffer completely ignoring low watermark.
int Write(const void *data, uint count)
Writes data to the end of the write buffer.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QString FileHash(const QString &filename)
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...
def read(device=None, features=[])
def write(text, progress=True)
QString RemoteDownloadFile(const QString &url, const QString &storageGroup, const QString &filename)
QString RemoteDownloadFileNow(const QString &url, const QString &storageGroup, const QString &filename)
static bool RemoteSendReceiveStringList(const QString &host, QStringList &strlist)
static constexpr int8_t O_LARGEFILE
static constexpr std::chrono::milliseconds MAX_FILE_CHECK
static QString downloadRemoteFile(const QString &cmd, const QString &url, const QString &storageGroup, const QString &filename)