5#include <QRegularExpression>
12#include "mythconfig.h"
43 QString ann = QString(
"ANN Playback %1 0")
47 bool mismatch =
false;
50 addr, port, ann, &mismatch);
70 std::chrono::milliseconds
timeout,
71 const QStringList *possibleAuxiliaryFiles) :
72 m_path(
std::move(url)),
73 m_useReadAhead(usereadahead), m_timeoutMs(
timeout),
81 else if (possibleAuxiliaryFiles)
89 LOG(VB_FILE, LOG_DEBUG, QString(
"RemoteFile(%1)").arg(
m_path));
109 bool is_local = !lpath.isEmpty() &&
110 !lpath.startsWith(
"myth:") &&
125 QString host = qurl.host();
126 int port = qurl.port();
131 dir +=
"?" + QUrl::fromPercentEncoding(
132 qurl.query(QUrl::FullyEncoded).toLocal8Bit());
134 if (qurl.hasFragment())
135 dir +=
"#" + qurl.fragment();
137 QString sgroup = qurl.userName();
140 QString stype = (control) ?
"control socket" :
"file data socket";
142 QString loc = QString(
"RemoteFile::openSocket(%1): ").arg(stype);
149 if (!lsock->ConnectToHost(host, port))
151 LOG(VB_GENERAL, LOG_ERR, loc +
152 QString(
"Could not connect to server %1:%2") .arg(host).arg(port));
161#ifndef IGNORE_PROTO_VER_MISMATCH
164 LOG(VB_GENERAL, LOG_ERR, loc +
165 QString(
"Failed validation to server %1:%2").arg(host).arg(port));
173 strlist.append(QString(
"ANN Playback %1 %2")
174 .arg(
hostname).arg(
static_cast<int>(
false)));
175 if (!lsock->SendReceiveStringList(strlist))
177 LOG(VB_GENERAL, LOG_ERR, loc +
178 QString(
"Could not read string list from server %1:%2")
179 .arg(host).arg(port));
186 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
189 strlist << QString(
"%1").arg(dir);
195 if (!lsock->SendReceiveStringList(strlist))
197 LOG(VB_GENERAL, LOG_ERR, loc +
198 QString(
"Did not get proper response from %1:%2")
199 .arg(host).arg(port));
201 strlist.push_back(
"ERROR");
202 strlist.push_back(
"invalid response");
205 if (strlist.size() >= 3)
207 auto it = strlist.begin(); ++it;
210 for (; it != strlist.end(); ++it)
213 else if (!strlist.isEmpty() && strlist.size() < 3 &&
214 strlist[0] !=
"ERROR")
216 LOG(VB_GENERAL, LOG_ERR, loc +
217 QString(
"Did not get proper response from %1:%2")
218 .arg(host).arg(port));
220 strlist.push_back(
"ERROR");
221 strlist.push_back(
"invalid response");
225 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
229 if (strlist.isEmpty())
231 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
235 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
236 ((strlist.size() >= 2) ?
237 QString(
", error was %1").arg(strlist[1]) :
238 QString(
", remote error")));
259 QMutexLocker locker(&
m_lock);
278 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
281 if (!dir.mkpath(fi.path()))
283 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
297 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
308 LOG(VB_FILE, LOG_ERR,
309 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
316 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
317 .arg(
m_path, strerror(errno)));
351 QMutexLocker locker(&
m_lock);
355 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
361 strlist << newFilename;
368 if (!strlist.isEmpty())
369 retval = (strlist[0].toInt() != 0);
398 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
423 return file.remove();
429 QString sgroup = qurl.userName();
431 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
437 if (
filename.isEmpty() || sgroup.isEmpty())
440 QStringList strlist(
"DELETE_FILE");
446 if (!strlist.isEmpty() && strlist[0] ==
"1")
457 struct stat fileinfo {};
458 return Exists(url, &fileinfo);
468 QString sgroup = qurl.userName();
469 QString host = qurl.host();
473 LOG(VB_FILE, LOG_INFO,
474 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
476 bool fileExists =
false;
477 QString fullFilePath =
"";
479 if (url.startsWith(
"myth:"))
483 if (!fullFilePath.isEmpty())
489 fileExists =
info.exists() ;
495 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
497 LOG(VB_FILE, LOG_ERR,
498 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
505 LOG(VB_FILE, LOG_INFO,
506 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
508 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
517 QStringList strlist(
"QUERY_FILE_EXISTS");
519 if (!sgroup.isEmpty())
525 if ((strlist.size() >= 15) && fileinfo)
527 fileinfo->st_dev = strlist[2].toLongLong();
528 fileinfo->st_ino = strlist[3].toLongLong();
529 fileinfo->st_mode = strlist[4].toLongLong();
530 fileinfo->st_nlink = strlist[5].toLongLong();
531 fileinfo->st_uid = strlist[6].toLongLong();
532 fileinfo->st_gid = strlist[7].toLongLong();
533 fileinfo->st_rdev = strlist[8].toLongLong();
534 fileinfo->st_size = strlist[9].toLongLong();
536 fileinfo->st_blksize = strlist[10].toLongLong();
537 fileinfo->st_blocks = strlist[11].toLongLong();
539 fileinfo->st_atime = strlist[12].toLongLong();
540 fileinfo->st_mtime = strlist[13].toLongLong();
541 fileinfo->st_ctime = strlist[14].toLongLong();
563 QString sgroup = qurl.userName();
565 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
571 if (
filename.isEmpty() || sgroup.isEmpty())
574 QStringList strlist(
"QUERY_FILE_HASH");
588 bool overwrite,
bool verify)
590 LOG(VB_FILE, LOG_INFO,
591 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
596 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
603 LOG(VB_GENERAL, LOG_ERR,
604 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
608 const int readSize = 2 * 1024 * 1024;
609 char *buf =
new char[readSize];
612 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
622 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
630 LOG(VB_GENERAL, LOG_ERR,
631 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
642 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
644 int dstLen = dstFile.
Write(buf, srcLen);
646 if (dstLen == -1 || srcLen != dstLen)
648 LOG(VB_GENERAL, LOG_ERR,
649 "RemoteFile::CopyFile: Error while trying to write to destination file.");
658 if (success && verify)
661 struct stat fileinfo {};
662 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
664 if (dstSize != srcSize)
666 LOG(VB_GENERAL, LOG_ERR,
667 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
668 .arg(dstSize).arg(srcSize));
679 LOG(VB_FILE, LOG_INFO,
680 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
685 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
692 bool ok =
CopyFile(src, dst, overwrite,
true);
696 LOG(VB_FILE, LOG_ERR,
697 "RemoteFile::MoveFile: Failed to delete file after successful copy");
708 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
716 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
719 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
727 if (srcUrl.userName() != dstUrl.userName())
729 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
733 QStringList strlist(
"MOVE_FILE");
734 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
738 if (!strlist.isEmpty() && strlist[0] ==
"1")
741 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
742 .arg(strlist.join(
",")));
750 QMutexLocker locker(&
m_lock);
753 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
761 QMutexLocker locker(&
m_lock);
772 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
778 long long offset = 0LL;
779 if (whence == SEEK_SET)
782 offset = std::min(pos,
info.size());
784 else if (whence == SEEK_END)
787 offset =
info.size() + pos;
789 else if (whence == SEEK_CUR)
791 offset = ((curpos > 0) ? curpos : ::lseek64(
m_localFile, 0, SEEK_CUR)) + pos;
798 off64_t localpos = ::lseek64(
m_localFile, pos, whence);
801 LOG(VB_FILE, LOG_ERR,
802 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
811 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
817 strlist << QString::number(pos);
818 strlist << QString::number(whence);
820 strlist << QString::number(curpos);
826 if (ok && !strlist.isEmpty())
830 return strlist[0].toLongLong();
840 unsigned zerocnt = 0;
842 bool response =
false;
846 LOG(VB_NETWORK, LOG_ERR,
847 "RemoteFile::Write(): Called when not in write mode");
854 LOG(VB_FILE, LOG_ERR,
855 "RemoteFile::Write(): File not opened");
861 QMutexLocker locker(&
m_lock);
865 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
870 strlist <<
"WRITE_BLOCK";
871 strlist << QString::number(size);
875 LOG(VB_NETWORK, LOG_ERR,
876 "RemoteFile::Write(): Block notification failed");
881 while (sent < recv && !
error && zerocnt++ < 50)
883 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
890 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
899 recv = strlist[0].toInt();
904 if (!
error && !response)
909 recv = strlist[0].toInt();
913 LOG(VB_GENERAL, LOG_ERR,
914 "RemoteFile::Write(): No response from control socket.");
919 LOG(VB_NETWORK, LOG_DEBUG,
920 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
921 .arg(size).arg(sent).arg(recv).arg(
error));
926 if (
error || recv != sent)
943 bool response =
false;
945 QMutexLocker locker(&
m_lock);
951 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
958 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
964 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
970 LOG(VB_NETWORK, LOG_ERR,
971 "RemoteFile::Read(): Read socket not empty to start!");
977 LOG(VB_NETWORK, LOG_WARNING,
978 "RemoteFile::Read(): Control socket not empty to start!");
983 strlist <<
"REQUEST_BLOCK";
984 strlist << QString::number(size);
988 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
994 std::chrono::milliseconds waitms { 30ms };
1000 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1007 waitms += (waitms < 200ms) ? 20ms : 0ms;
1013 sent = strlist[0].toInt();
1018 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1027 if (!
error && !response)
1034 sent = strlist[0].toInt();
1038 LOG(VB_GENERAL, LOG_ERR,
1039 "RemoteFile::Read(): No response from control socket.");
1058 LOG(VB_NETWORK, LOG_DEBUG,
1059 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1060 .arg(size).arg(recv).arg(sent).arg(
error));
1065 if (
error || sent != recv)
1067 LOG(VB_GENERAL, LOG_WARNING,
1068 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1069 .arg(sent).arg(recv));
1075 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1079 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1111 QMutexLocker locker(&
m_lock);
1130 QMutexLocker locker(&
m_lock);
1141 struct stat fileinfo {};
1151 strlist <<
"REQUEST_SIZE";
1155 if (ok && !strlist.isEmpty())
1157 bool validate =
false;
1158 long long size = strlist[0].toLongLong(&validate);
1162 if (strlist.count() >= 2)
1170 struct stat fileinfo {};
1192 Read(data.data(), fs);
1207 QMutexLocker locker(&
m_lock);
1218 LOG(VB_NETWORK, LOG_ERR,
1219 "RemoteFile::SetTimeout(): Couldn't connect");
1226 strlist <<
"SET_TIMEOUT";
1227 strlist << QString::number((
int)fast);
1238 QFileInfo
info(url);
1239 return info.lastModified();
1244 QString sgroup = qurl.userName();
1246 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1252 if (
filename.isEmpty() || sgroup.isEmpty())
1255 QStringList strlist(
"QUERY_SG_FILEQUERY");
1256 strlist << qurl.host();
1262 if (strlist.size() > 1) {
1263 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1266 result = QDateTime();;
1287 const QString& storageGroup,
bool useRegex,
1292 if (!files.isEmpty())
1308 const QString& storageGroup,
bool useRegex,
1311 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1312 "(useregex: %4, allowfallback: %5)")
1314 .arg(useRegex).arg(allowFallback));
1316 if (
filename.isEmpty() || storageGroup.isEmpty())
1319 QStringList strList;
1320 QString hostName = host;
1322 if (hostName.isEmpty())
1336 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1338 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1339 .arg(fi.path(), fi.fileName()));
1341 for (
int x = 0; x < files.size(); x++)
1343 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1344 .arg(x).arg(files[x]));
1347 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1348 for (
const QString&
file : std::as_const(filteredFiles))
1352 fi.path() +
'/' +
file,
1366 if (!strList.isEmpty() || !allowFallback)
1373 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1374 << (useRegex ?
"1" :
"0")
1379 if (!strList.empty() && !strList[0].isEmpty() &&
1380 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1448 LOG(VB_FILE, LOG_ERR,
1449 QString(
"RemoteFile::Resume: Enable to re-seek into last known "
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)
static bool RemoteSendReceiveStringList(const QString &host, QStringList &strlist)
static constexpr int8_t O_LARGEFILE
static constexpr std::chrono::milliseconds MAX_FILE_CHECK