12 #include "mythconfig.h"
15 #include <sys/ioctl.h>
17 #if CONFIG_SYSTEMD_NOTIFY
18 #include <systemd/sd-daemon.h>
24 #else // if !__linux__
25 # include <sys/param.h>
27 # include <sys/mount.h>
31 #include <QCoreApplication>
35 #include <QWaitCondition>
36 #include <QWriteLocker>
41 #include <QNetworkInterface>
42 #include <QNetworkProxy>
43 #include <QHostAddress>
89 static constexpr std::chrono::milliseconds
PRT_TIMEOUT { 10ms };
91 #define PRT_STARTUP_THREAD_COUNT 5
93 #define LOC QString("MainServer: ")
94 #define LOC_WARN QString("MainServer, Warning: ")
95 #define LOC_ERR QString("MainServer, Error: ")
99 bool delete_file_immediately(
const QString &
filename,
100 bool followLinks,
bool checkexists)
104 bool success1 =
true;
105 bool success2 =
true;
107 LOG(VB_FILE, LOG_INFO,
LOC +
108 QString(
"About to delete file: %1").
arg(
filename));
112 if (finfo.isSymLink())
116 QFile target(linktext);
117 if (!(success1 = target.remove()))
119 LOG(VB_GENERAL, LOG_ERR,
LOC +
120 QString(
"Error deleting '%1' -> '%2'")
125 if ((!checkexists || checkFile.exists()) &&
126 !(success2 = checkFile.remove()))
128 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting '%1': %2")
131 return success1 && success2;
196 QMutexLocker locker(&
m_lock);
206 m_wait.wait(locker.mutex(), left.count());
212 QMutexLocker locker(&
m_lock);
237 QMap<int, EncoderLink *> *_tvList,
239 m_encoderList(_tvList),
240 m_ismaster(master), m_threadPool(
"ProcessRequestPool"),
241 m_sched(
sched), m_expirer(_expirer)
264 bool v4IsSet = !config_v4.isNull();
269 bool v6IsSet = !config_v6.isNull();
271 if (v6IsSet && !listenAddrs.contains(config_v6))
272 LOG(VB_GENERAL, LOG_WARNING,
LOC +
273 "Unable to find IPv6 address to bind");
275 if (v4IsSet && !listenAddrs.contains(config_v4))
276 LOG(VB_GENERAL, LOG_WARNING,
LOC +
277 "Unable to find IPv4 address to bind");
279 if ((v4IsSet && !listenAddrs.contains(config_v4))
280 && (v6IsSet && !listenAddrs.contains(config_v6))
283 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to find either IPv4 or IPv6 "
284 "address we can bind to, exiting");
317 QList<FileSystemInfo> m_fsInfos;
434 auto *ms =
new MythSocket(socketDescriptor,
this);
435 if (ms->IsConnected())
447 QCoreApplication::processEvents();
455 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"No data on sock %1")
469 QStringList listline;
472 if (!
pbs->ReadStringList(listline) || listline.empty())
475 LOG(VB_GENERAL, LOG_INFO,
"No data in ProcessRequestWork()");
480 else if (!bIsControl)
487 LOG(VB_GENERAL, LOG_INFO,
LOC +
"No data in ProcessRequestWork()");
491 QString line = listline[0];
493 line = line.simplified();
494 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
495 QStringList tokens = line.split(
' ', QString::SkipEmptyParts);
497 QStringList tokens = line.split(
' ', Qt::SkipEmptyParts);
499 QString command = tokens[0];
501 if (command ==
"MYTH_PROTO_VERSION")
503 if (tokens.size() < 2)
509 if (command ==
"ANN")
514 if (command ==
"DONE")
525 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ProcessRequest unknown socket");
531 if (command ==
"QUERY_FILETRANSFER")
533 if (tokens.size() != 2)
538 else if (command ==
"QUERY_RECORDINGS")
540 if (tokens.size() != 2)
545 else if (command ==
"QUERY_RECORDING")
549 else if (command ==
"GO_TO_SLEEP")
553 else if (command ==
"QUERY_FREE_SPACE")
557 else if (command ==
"QUERY_FREE_SPACE_LIST")
561 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
565 else if (command ==
"QUERY_LOAD")
569 else if (command ==
"QUERY_UPTIME")
573 else if (command ==
"QUERY_HOSTNAME")
577 else if (command ==
"QUERY_MEMSTATS")
581 else if (command ==
"QUERY_TIME_ZONE")
585 else if (command ==
"QUERY_CHECKFILE")
589 else if (command ==
"QUERY_FILE_EXISTS")
591 if (listline.size() < 2)
596 else if (command ==
"QUERY_FINDFILE")
598 if (listline.size() < 4)
603 else if (command ==
"QUERY_FILE_HASH")
605 if (listline.size() < 3)
610 else if (command ==
"QUERY_GUIDEDATATHROUGH")
614 else if (command ==
"DELETE_FILE")
616 if (listline.size() < 3)
621 else if (command ==
"MOVE_FILE")
623 if (listline.size() < 4)
628 else if (command ==
"STOP_RECORDING")
632 else if (command ==
"CHECK_RECORDING")
636 else if (command ==
"DELETE_RECORDING")
638 if (3 <= tokens.size() && tokens.size() <= 5)
640 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
641 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
647 else if (command ==
"FORCE_DELETE_RECORDING")
651 else if (command ==
"UNDELETE_RECORDING")
655 else if (command ==
"ADD_CHILD_INPUT")
660 LOG(VB_GENERAL, LOG_ERR,
LOC +
661 "ADD_CHILD_INPUT command received in master context");
662 reslist << QString(
"ERROR: Called in master context");
664 else if (tokens.size() != 2)
665 reslist <<
"ERROR: Bad ADD_CHILD_INPUT request";
669 reslist << QString(
"ERROR: Failed to add child input");
672 else if (command ==
"RESCHEDULE_RECORDINGS")
674 listline.pop_front();
677 else if (command ==
"FORGET_RECORDING")
681 else if (command ==
"QUERY_GETALLPENDING")
683 if (tokens.size() == 1)
685 else if (tokens.size() == 2)
690 else if (command ==
"QUERY_GETALLSCHEDULED")
694 else if (command ==
"QUERY_GETCONFLICTING")
698 else if (command ==
"QUERY_GETEXPIRING")
702 else if (command ==
"QUERY_SG_GETFILELIST")
706 else if (command ==
"QUERY_SG_FILEQUERY")
710 else if (command ==
"GET_FREE_INPUT_INFO")
712 if (tokens.size() != 2)
717 else if (command ==
"QUERY_RECORDER")
719 if (tokens.size() != 2)
724 else if ((command ==
"QUERY_RECORDING_DEVICE") ||
725 (command ==
"QUERY_RECORDING_DEVICES"))
729 else if (command ==
"SET_NEXT_LIVETV_DIR")
731 if (tokens.size() != 3)
736 else if (command ==
"SET_CHANNEL_INFO")
740 else if (command ==
"QUERY_REMOTEENCODER")
742 if (tokens.size() != 2)
747 else if (command ==
"GET_RECORDER_FROM_NUM")
751 else if (command ==
"GET_RECORDER_NUM")
755 else if (command ==
"QUERY_GENPIXMAP2")
759 else if (command ==
"QUERY_PIXMAP_LASTMODIFIED")
763 else if (command ==
"QUERY_PIXMAP_GET_IF_MODIFIED")
767 else if (command ==
"QUERY_ISRECORDING")
771 else if (command ==
"MESSAGE")
773 if ((listline.size() >= 2) && (listline[1].startsWith(
"SET_VERBOSE")))
775 else if ((listline.size() >= 2) &&
776 (listline[1].startsWith(
"SET_LOG_LEVEL")))
781 else if (command ==
"FILL_PROGRAM_INFO")
785 else if (command ==
"LOCK_TUNER")
787 if (tokens.size() == 1)
789 else if (tokens.size() == 2)
794 else if (command ==
"FREE_TUNER")
796 if (tokens.size() != 2)
801 else if (command ==
"QUERY_ACTIVE_BACKENDS")
805 else if (command ==
"QUERY_IS_ACTIVE_BACKEND")
807 if (tokens.size() != 1)
812 else if (command ==
"QUERY_COMMBREAK")
814 if (tokens.size() != 3)
819 else if (command ==
"QUERY_CUTLIST")
821 if (tokens.size() != 3)
826 else if (command ==
"QUERY_BOOKMARK")
828 if (tokens.size() != 3)
833 else if (command ==
"SET_BOOKMARK")
835 if (tokens.size() != 4)
840 else if (command ==
"QUERY_SETTING")
842 if (tokens.size() != 3)
847 else if (command ==
"SET_SETTING")
849 if (tokens.size() != 4)
854 else if (command ==
"SCAN_VIDEOS")
858 else if (command ==
"SCAN_MUSIC")
862 else if (command ==
"MUSIC_TAG_UPDATE_VOLATILE")
864 if (listline.size() != 6)
869 else if (command ==
"MUSIC_CALC_TRACK_LENGTH")
871 if (listline.size() != 3)
876 else if (command ==
"MUSIC_TAG_UPDATE_METADATA")
878 if (listline.size() != 3)
883 else if (command ==
"MUSIC_FIND_ALBUMART")
885 if (listline.size() != 4)
890 else if (command ==
"MUSIC_TAG_GETIMAGE")
892 if (listline.size() < 4)
897 else if (command ==
"MUSIC_TAG_ADDIMAGE")
899 if (listline.size() < 5)
904 else if (command ==
"MUSIC_TAG_REMOVEIMAGE")
906 if (listline.size() < 4)
911 else if (command ==
"MUSIC_TAG_CHANGEIMAGE")
913 if (listline.size() < 5)
918 else if (command ==
"MUSIC_LYRICS_FIND")
920 if (listline.size() < 3)
925 else if (command ==
"MUSIC_LYRICS_GETGRABBERS")
929 else if (command ==
"MUSIC_LYRICS_SAVE")
931 if (listline.size() < 3)
936 else if (command ==
"IMAGE_SCAN")
939 QStringList reply = (listline.size() == 2)
941 : QStringList(
"ERROR") <<
"Bad: " << listline;
945 else if (command ==
"IMAGE_COPY")
948 QStringList reply = (listline.size() >= 2)
950 : QStringList(
"ERROR") <<
"Bad: " << listline;
954 else if (command ==
"IMAGE_MOVE")
957 QStringList reply = (listline.size() == 4)
959 HandleDbMove(listline[1], listline[2], listline[3])
960 : QStringList(
"ERROR") <<
"Bad: " << listline;
964 else if (command ==
"IMAGE_DELETE")
967 QStringList reply = (listline.size() == 2)
969 : QStringList(
"ERROR") <<
"Bad: " << listline;
973 else if (command ==
"IMAGE_HIDE")
976 QStringList reply = (listline.size() == 3)
978 HandleHide(listline[1].toInt() != 0, listline[2])
979 : QStringList(
"ERROR") <<
"Bad: " << listline;
983 else if (command ==
"IMAGE_TRANSFORM")
986 QStringList reply = (listline.size() == 3)
988 HandleTransform(listline[1].toInt(), listline[2])
989 : QStringList(
"ERROR") <<
"Bad: " << listline;
993 else if (command ==
"IMAGE_RENAME")
996 QStringList reply = (listline.size() == 3)
998 : QStringList(
"ERROR") <<
"Bad: " << listline;
1002 else if (command ==
"IMAGE_CREATE_DIRS")
1005 QStringList reply = (listline.size() >= 4)
1007 HandleDirs(listline[1], listline[2].toInt() != 0, listline.mid(3))
1008 : QStringList(
"ERROR") <<
"Bad: " << listline;
1012 else if (command ==
"IMAGE_COVER")
1015 QStringList reply = (listline.size() == 3)
1017 HandleCover(listline[1].toInt(), listline[2].toInt())
1018 : QStringList(
"ERROR") <<
"Bad: " << listline;
1022 else if (command ==
"IMAGE_IGNORE")
1025 QStringList reply = (listline.size() == 2)
1027 : QStringList(
"ERROR") <<
"Bad: " << listline;
1031 else if (command ==
"ALLOW_SHUTDOWN")
1033 if (tokens.size() != 1)
1038 else if (command ==
"BLOCK_SHUTDOWN")
1040 if (tokens.size() != 1)
1045 else if (command ==
"SHUTDOWN_NOW")
1047 if (tokens.size() != 1)
1052 if (listline.size() >= 2)
1053 halt_cmd = listline[1];
1055 if (!halt_cmd.isEmpty())
1057 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
1058 "Going down now as of Mainserver request!");
1065 else if (command ==
"BACKEND_MESSAGE")
1067 QString message = listline[1];
1068 QStringList extra( listline[2] );
1069 for (
int i = 3; i < listline.size(); i++)
1070 extra << listline[i];
1074 else if ((command ==
"DOWNLOAD_FILE") ||
1075 (command ==
"DOWNLOAD_FILE_NOW"))
1077 if (listline.size() != 4)
1082 else if (command ==
"REFRESH_BACKEND")
1084 LOG(VB_GENERAL, LOG_INFO ,
LOC +
"Reloading backend settings");
1087 else if (command ==
"OK")
1089 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'OK' out of sequence.");
1091 else if (command ==
"UNKNOWN_COMMAND")
1093 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'UNKNOWN_COMMAND' out of sequence.");
1097 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unknown command: " + command);
1101 QStringList strlist;
1102 strlist <<
"UNKNOWN_COMMAND";
1115 QStringList broadcast;
1116 QSet<QString> receivers;
1138 QString message = me->
Message();
1140 if ((message ==
"PREVIEW_SUCCESS" || message ==
"PREVIEW_QUEUED") &&
1141 me->ExtraDataCount() >= 5)
1144 uint recordingID = me->ExtraData(0).toUInt();
1145 const QString&
filename = me->ExtraData(1);
1146 const QString& msg = me->ExtraData(2);
1147 const QString& datetime = me->ExtraData(3);
1149 if (message ==
"PREVIEW_QUEUED")
1151 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1152 QString(
"Preview Queued: '%1' '%2'")
1158 ok = ok &&
file.open(QIODevice::ReadOnly);
1162 QByteArray data =
file.readAll();
1163 QStringList extra(
"OK");
1164 extra.push_back(QString::number(recordingID));
1165 extra.push_back(msg);
1166 extra.push_back(datetime);
1167 extra.push_back(QString::number(data.size()));
1169 QString::number(qChecksum(data.constData(), data.size())));
1170 extra.push_back(QString(data.toBase64()));
1172 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1174 const QString&
token = me->ExtraData(i);
1175 extra.push_back(
token);
1179 receivers.insert(*it);
1184 if (receivers.empty())
1186 LOG(VB_GENERAL, LOG_ERR,
LOC +
1187 "PREVIEW_SUCCESS but no receivers.");
1191 broadcast.push_back(
"BACKEND_MESSAGE");
1192 broadcast.push_back(
"GENERATED_PIXMAP");
1197 message =
"PREVIEW_FAILED";
1203 if (message ==
"PREVIEW_FAILED" && me->ExtraDataCount() >= 5)
1205 const QString& pginfokey = me->ExtraData(0);
1206 const QString& msg = me->ExtraData(2);
1208 QStringList extra(
"ERROR");
1209 extra.push_back(pginfokey);
1210 extra.push_back(msg);
1211 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1213 const QString&
token = me->ExtraData(i);
1214 extra.push_back(
token);
1218 receivers.insert(*it);
1223 if (receivers.empty())
1225 LOG(VB_GENERAL, LOG_ERR,
LOC +
1226 "PREVIEW_FAILED but no receivers.");
1230 broadcast.push_back(
"BACKEND_MESSAGE");
1231 broadcast.push_back(
"GENERATED_PIXMAP");
1235 if (me->Message().startsWith(
"AUTO_EXPIRE"))
1237 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1238 QStringList tokens = me->Message()
1239 .split(
" ", QString::SkipEmptyParts);
1241 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1244 if (tokens.size() != 3)
1246 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad AUTO_EXPIRE message");
1270 QString msg = QString(
"Cannot find program info for '%1', "
1271 "while attempting to Auto-Expire.")
1272 .arg(me->Message());
1273 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
1279 if (me->Message().startsWith(
"QUERY_NEXT_LIVETV_DIR") &&
m_sched)
1281 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1282 QStringList tokens = me->Message()
1283 .split(
" ", QString::SkipEmptyParts);
1285 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1288 if (tokens.size() != 2)
1290 LOG(VB_GENERAL, LOG_ERR,
LOC +
1291 QString(
"Bad %1 message").
arg(tokens[0]));
1299 if (me->Message().startsWith(
"STOP_RECORDING"))
1301 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1302 QStringList tokens = me->Message().split(
" ",
1303 QString::SkipEmptyParts);
1305 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1309 if (tokens.size() < 3 || tokens.size() > 3)
1311 LOG(VB_GENERAL, LOG_ERR,
LOC +
1312 QString(
"Bad STOP_RECORDING message: %1")
1313 .
arg(me->Message()));
1326 LOG(VB_GENERAL, LOG_ERR,
LOC +
1327 QString(
"Cannot find program info for '%1' while "
1328 "attempting to stop recording.").
arg(me->Message()));
1334 if ((me->Message().startsWith(
"DELETE_RECORDING")) ||
1335 (me->Message().startsWith(
"FORCE_DELETE_RECORDING")))
1337 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1338 QStringList tokens = me->Message()
1339 .split(
" ", QString::SkipEmptyParts);
1341 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1345 if (tokens.size() < 3 || tokens.size() > 5)
1347 LOG(VB_GENERAL, LOG_ERR,
LOC +
1348 QString(
"Bad %1 message").
arg(tokens[0]));
1352 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
1353 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
1360 if (tokens[0] ==
"FORCE_DELETE_RECORDING")
1367 LOG(VB_GENERAL, LOG_ERR,
LOC +
1368 QString(
"Cannot find program info for '%1' while "
1369 "attempting to delete.").
arg(me->Message()));
1375 if (me->Message().startsWith(
"UNDELETE_RECORDING"))
1377 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1378 QStringList tokens = me->Message().split(
" ",
1379 QString::SkipEmptyParts);
1381 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1385 if (tokens.size() < 3 || tokens.size() > 3)
1387 LOG(VB_GENERAL, LOG_ERR,
LOC +
1388 QString(
"Bad UNDELETE_RECORDING message: %1")
1389 .
arg(me->Message()));
1402 LOG(VB_GENERAL, LOG_ERR,
LOC +
1403 QString(
"Cannot find program info for '%1' while "
1404 "attempting to undelete.").
arg(me->Message()));
1410 if (me->Message().startsWith(
"ADD_CHILD_INPUT"))
1412 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1413 QStringList tokens = me->Message()
1414 .split(
" ", QString::SkipEmptyParts);
1416 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1420 LOG(VB_GENERAL, LOG_ERR,
LOC +
1421 "ADD_CHILD_INPUT event received in slave context");
1423 else if (tokens.size() != 2)
1425 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad ADD_CHILD_INPUT message");
1434 if (me->Message().startsWith(
"RESCHEDULE_RECORDINGS") &&
m_sched)
1436 const QStringList& request = me->ExtraDataList();
1441 if (me->Message().startsWith(
"SCHEDULER_ADD_RECORDING") &&
m_sched)
1446 LOG(VB_GENERAL, LOG_ERR,
LOC +
1447 "Bad SCHEDULER_ADD_RECORDING message");
1455 if (me->Message().startsWith(
"UPDATE_RECORDING_STATUS") &&
m_sched)
1457 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1458 QStringList tokens = me->Message()
1459 .split(
" ", QString::SkipEmptyParts);
1461 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1464 if (tokens.size() != 6)
1466 LOG(VB_GENERAL, LOG_ERR,
LOC +
1467 "Bad UPDATE_RECORDING_STATUS message");
1471 uint cardid = tokens[1].toUInt();
1472 uint chanid = tokens[2].toUInt();
1477 recstatus, recendts);
1483 if (me->Message().startsWith(
"LIVETV_EXITED"))
1485 const QString& chainid = me->ExtraData();
1493 if (me->Message() ==
"CLEAR_SETTINGS_CACHE")
1496 if (me->Message().startsWith(
"RESET_IDLETIME") &&
m_sched)
1499 if (me->Message() ==
"LOCAL_RECONNECT_TO_MASTER")
1502 if (me->Message() ==
"LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE")
1505 if (me->Message().startsWith(
"LOCAL_"))
1508 if (me->Message() ==
"CREATE_THUMBNAILS")
1511 if (me->Message() ==
"IMAGE_GET_METADATA")
1515 if (me->Message().startsWith(
"MASTER_UPDATE_REC_INFO"))
1517 QStringList tokens = me->Message().simplified().split(
" ");
1518 uint recordedid = 0;
1519 if (tokens.size() >= 2)
1520 recordedid = tokens[1].toUInt();
1533 mod_me =
MythEvent(
"RECORDING_LIST_CHANGE UPDATE", list);
1542 if (me->Message().startsWith(
"DOWNLOAD_FILE"))
1545 QString localFile = extraDataList[1];
1546 QFile
file(localFile);
1547 QStringList tokens = me->Message().simplified().split(
" ");
1555 if ((tokens.size() >= 2) && (tokens[1] ==
"FINISHED"))
1558 mod_me =
MythEvent(me->Message(), extraDataList);
1562 if (broadcast.empty())
1564 broadcast.push_back(
"BACKEND_MESSAGE");
1565 broadcast.push_back(me->Message());
1570 if (!broadcast.empty())
1573 vector<PlaybackSock *> localPBSList;
1578 localPBSList.push_back(
pbs);
1582 bool sendGlobal =
false;
1583 if (
m_ismaster && broadcast[1].startsWith(
"GLOBAL_"))
1585 broadcast[1].replace(
"GLOBAL_",
"LOCAL_");
1586 MythEvent me(broadcast[1], broadcast[2]);
1592 QSet<PlaybackSock*> sentSet;
1594 bool isSystemEvent = broadcast[1].startsWith(
"SYSTEM_EVENT ");
1597 vector<PlaybackSock*>::const_iterator iter;
1598 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1602 if (sentSet.contains(
pbs) ||
pbs->IsDisconnected())
1605 if (!receivers.empty() && !receivers.contains(
pbs->getHostname()))
1608 sentSet.insert(
pbs);
1610 bool reallysendit =
false;
1612 if (broadcast[1] ==
"CLEAR_SETTINGS_CACHE")
1615 (
pbs->isSlaveBackend() ||
pbs->wantsEvents()))
1616 reallysendit =
true;
1618 else if (sendGlobal)
1620 if (
pbs->isSlaveBackend())
1621 reallysendit =
true;
1623 else if (
pbs->wantsEvents())
1625 reallysendit =
true;
1632 if (!
pbs->wantsSystemEvents())
1636 if (!
pbs->wantsOnlySystemEvents())
1638 if (sentSetSystemEvent.contains(
pbs->getHostname()))
1641 sentSetSystemEvent <<
pbs->getHostname();
1644 else if (
pbs->wantsOnlySystemEvents())
1654 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1672 QStringList retlist;
1676 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1677 "MainServer::HandleVersion - Client speaks protocol version " +
1685 if (slist.size() < 3)
1687 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1688 "MainServer::HandleVersion - Client did not pass protocol "
1689 "token. Refusing connection!");
1696 QString
token = slist[2];
1699 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1700 QString(
"MainServer::HandleVersion - Client sent incorrect "
1701 "protocol token \"%1\" for protocol version. Refusing "
1738 QStringList retlist(
"OK" );
1739 QStringList errlist(
"ERROR" );
1741 if (commands.size() < 3 || commands.size() > 6)
1744 if (commands.size() == 2)
1745 info = QString(
" %1").arg(commands[1]);
1747 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Received malformed ANN%1 query")
1750 errlist <<
"malformed_ann_query";
1758 if (
pbs->getSocket() == socket)
1760 LOG(VB_GENERAL, LOG_WARNING,
LOC +
1761 QString(
"Client %1 is trying to announce a socket "
1771 if (commands[1] ==
"Playback" || commands[1] ==
"Monitor" ||
1772 commands[1] ==
"Frontend")
1774 if (commands.size() < 4)
1776 LOG(VB_GENERAL, LOG_ERR,
LOC +
1777 QString(
"Received malformed ANN %1 query")
1780 errlist <<
"malformed_ann_query";
1797 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"MainServer::ANN %1")
1799 LOG(VB_GENERAL, LOG_INFO,
LOC +
1800 QString(
"adding: %1(%2) as a client (events: %3)")
1802 .
arg(quintptr(socket),0,16)
1804 pbs->setBlockShutdown((commands[1] ==
"Playback") ||
1805 (commands[1] ==
"Frontend"));
1807 if (commands[1] ==
"Frontend")
1809 pbs->SetAsFrontend();
1811 frontend->m_name = commands[2];
1826 else if (commands[1] ==
"MediaServer")
1828 if (commands.size() < 3)
1830 LOG(VB_GENERAL, LOG_ERR,
LOC +
1831 "Received malformed ANN MediaServer query");
1832 errlist <<
"malformed_ann_query";
1842 pbs->setAsMediaServer();
1843 pbs->setBlockShutdown(
false);
1848 QString(
"CLIENT_CONNECTED HOSTNAME %1").
arg(commands[2]));
1850 else if (commands[1] ==
"SlaveBackend")
1852 if (commands.size() < 4)
1854 LOG(VB_GENERAL, LOG_ERR,
LOC +
1855 QString(
"Received malformed ANN %1 query")
1857 errlist <<
"malformed_ann_query";
1870 LOG(VB_GENERAL, LOG_INFO,
LOC +
1871 QString(
"adding: %1 as a slave backend server")
1873 pbs->setAsSlaveBackend();
1874 pbs->setIP(commands[3]);
1879 QStringList::const_iterator sit = slist.
cbegin()+1;
1880 while (sit != slist.cend())
1883 if (!recinfo->GetChanID())
1893 bool wasAsleep =
true;
1897 if (elink->GetHostName() == commands[2])
1899 if (! (elink->IsWaking() || elink->IsAsleep()))
1901 elink->SetSocket(
pbs);
1909 QString message = QString(
"LOCAL_SLAVE_BACKEND_ONLINE %2")
1914 pbs->setBlockShutdown(
false);
1919 QString(
"SLAVE_CONNECTED HOSTNAME %1").
arg(commands[2]));
1921 else if (commands[1] ==
"FileTransfer")
1923 if (slist.size() < 3)
1925 LOG(VB_GENERAL, LOG_ERR,
LOC +
1926 "Received malformed FileTransfer command");
1927 errlist <<
"malformed_filetransfer_command";
1932 LOG(VB_NETWORK, LOG_INFO,
LOC +
1933 "MainServer::HandleAnnounce FileTransfer");
1934 LOG(VB_NETWORK, LOG_INFO,
LOC +
1935 QString(
"adding: %1 as a remote file transfer") .
arg(commands[2]));
1936 QStringList::const_iterator it = slist.cbegin();
1937 QString path = *(++it);
1938 QString wantgroup = *(++it);
1940 QStringList checkfiles;
1942 for (++it; it != slist.cend(); ++it)
1946 bool writemode =
false;
1947 bool usereadahead =
true;
1948 std::chrono::milliseconds timeout_ms = 2s;
1949 if (commands.size() > 3)
1950 writemode = (commands[3].toInt() != 0);
1952 if (commands.size() > 4)
1953 usereadahead = (commands[4].toInt() != 0);
1955 if (commands.size() > 5)
1956 timeout_ms = std::chrono::milliseconds(commands[5].toInt());
1960 if (wantgroup.isEmpty())
1961 wantgroup =
"Default";
1967 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to determine directory "
1968 "to write to in FileTransfer write command");
1969 errlist <<
"filetransfer_directory_not_found";
1976 LOG(VB_GENERAL, LOG_ERR,
LOC +
1977 QString(
"FileTransfer write filename is empty in path '%1'.")
1979 errlist <<
"filetransfer_filename_empty";
1984 if ((path.contains(
"/../")) ||
1985 (path.startsWith(
"../")))
1987 LOG(VB_GENERAL, LOG_ERR,
LOC +
1988 QString(
"FileTransfer write filename '%1' does not pass "
1989 "sanity checks.") .
arg(path));
1990 errlist <<
"filetransfer_filename_dangerous";
2002 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Empty filename, cowardly aborting!");
2003 errlist <<
"filetransfer_filename_empty";
2012 LOG(VB_GENERAL, LOG_ERR,
LOC +
2013 QString(
"FileTransfer filename '%1' is actually a directory, "
2015 errlist <<
"filetransfer_filename_is_a_directory";
2022 QString dirPath = finfo.absolutePath();
2026 if (!qdir.mkpath(dirPath))
2028 LOG(VB_GENERAL, LOG_ERR,
LOC +
2029 QString(
"FileTransfer filename '%1' is in a "
2030 "subdirectory which does not exist, and can "
2032 errlist <<
"filetransfer_unable_to_create_subdirectory";
2052 LOG(VB_GENERAL, LOG_ERR,
LOC +
2054 errlist <<
"filetransfer_unable_to_open_file";
2061 LOG(VB_GENERAL, LOG_INFO,
LOC +
2062 QString(
"adding: %1(%2) as a file transfer")
2064 .
arg(quintptr(socket),0,16));
2074 if (!checkfiles.empty())
2077 QDir
dir = fi.absoluteDir();
2078 for (
const auto &
file : qAsConst(checkfiles))
2081 ((
file).endsWith(
".srt") ||
2114 QStringList strList(
"ERROR");
2129 bool do_write =
false;
2144 LOG(VB_GENERAL, LOG_ERR,
LOC +
2145 "SendResponse: Unable to write to client socket, as it's no "
2161 QString playbackhost =
pbs->getHostname();
2163 QMap<QString,ProgramInfo*> recMap;
2168 QMap<QString,bool> isJobRunning =
2174 if ((
type ==
"Ascending") || (
type ==
"Play"))
2176 else if ((
type ==
"Descending") || (
type ==
"Delete"))
2181 destination, (
type ==
"Recording"),
2182 inUseMap, isJobRunning, recMap, sort);
2184 QMap<QString,ProgramInfo*>::iterator mit = recMap.begin();
2185 for (; mit != recMap.end(); mit = recMap.erase(mit))
2188 QStringList outputlist(QString::number(destination.
size()));
2189 QMap<QString, int> backendPortMap;
2193 for (
auto* proginfo : destination)
2204 proginfo->GetBasename()));
2205 if (!proginfo->GetFilesize())
2208 if (tmpURL.startsWith(
'/'))
2210 QFile checkFile(tmpURL);
2211 if (!tmpURL.isEmpty() && checkFile.exists())
2213 proginfo->SetFilesize(checkFile.size());
2214 if (proginfo->GetRecordingEndTime() <
2217 proginfo->SaveFilesize(proginfo->GetFilesize());
2226 if (proginfo->GetPathname().isEmpty())
2228 LOG(VB_GENERAL, LOG_ERR,
LOC +
2229 QString(
"HandleQueryRecordings() "
2230 "Couldn't find backend for:\n\t\t\t%1")
2233 proginfo->SetFilesize(0);
2234 proginfo->SetPathname(
"file not found");
2239 if (!proginfo->GetFilesize())
2243 LOG(VB_GENERAL, LOG_ERR,
LOC +
2244 "MainServer::HandleQueryRecordings()"
2245 "\n\t\t\tCould not fill program info "
2250 if (proginfo->GetRecordingEndTime() <
2253 proginfo->SaveFilesize(proginfo->GetFilesize());
2262 if (!backendPortMap.contains(
hostname))
2274 proginfo->ToStringList(outputlist);
2287 if (slist.size() < 3)
2289 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2294 QString command = slist[1].toUpper();
2297 if (command ==
"BASENAME")
2301 else if (command ==
"TIMESLOT")
2303 if (slist.size() < 4)
2305 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2310 pginfo =
new ProgramInfo(slist[2].toUInt(), recstartts);
2313 QStringList strlist;
2334 QString playbackhost = slist[1];
2336 QStringList::const_iterator it = slist.cbegin() + 2;
2351 const QFileInfo info(lpath);
2355 QStringList strlist;
2366 m_ms->DoDeleteThread(
this);
2373 std::this_thread::sleep_for(3s);
2374 std::this_thread::sleep_for(std::chrono::milliseconds(
MythRandom()%2));
2379 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2384 QString name = QString(
"deleteThread%1%2").arg(getpid()).arg(
random());
2390 QString msg = QString(
"ERROR opening database connection for Delete "
2391 "Thread for chanid %1 recorded at %2. Program "
2392 "will NOT be deleted.")
2395 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2405 QString msg = QString(
"ERROR retrieving program info when trying to "
2406 "delete program for chanid %1 recorded at %2. "
2407 "Recording will NOT be deleted.")
2410 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2420 if ((!checkFile.exists()) && pginfo.
GetFilesize() &&
2423 LOG(VB_GENERAL, LOG_ERR,
LOC +
2424 QString(
"ERROR when trying to delete file: %1. File "
2425 "doesn't exist. Database metadata will not be removed.")
2443 bool errmsg =
false;
2458 if ((fd < 0) && checkFile.exists())
2463 delete_file_immediately(ds->
m_filename, followLinks,
false);
2464 std::this_thread::sleep_for(2s);
2465 if (checkFile.exists())
2471 LOG(VB_GENERAL, LOG_ERR,
LOC +
2472 QString(
"Error deleting file: %1. Keeping metadata in database.")
2487 QStringList nameFilters;
2488 nameFilters.push_back(fInfo.fileName() +
"*.png");
2489 nameFilters.push_back(fInfo.fileName() +
"*.jpg");
2490 nameFilters.push_back(fInfo.fileName() +
".tmp");
2491 nameFilters.push_back(fInfo.fileName() +
".old");
2492 nameFilters.push_back(fInfo.fileName() +
".map");
2493 nameFilters.push_back(fInfo.fileName() +
".tmp.map");
2494 nameFilters.push_back(fInfo.baseName() +
".srt");
2496 QDir
dir (fInfo.path());
2497 QFileInfoList miscFiles =
dir.entryInfoList(nameFilters);
2499 for (
const auto &
file : qAsConst(miscFiles))
2501 QString sFileName =
file.absoluteFilePath();
2502 delete_file_immediately( sFileName, followLinks,
true);
2513 if (slowDeletes && fd >= 0)
2519 QString logInfo = QString(
"recording id %1 filename %2")
2522 LOG(VB_GENERAL, LOG_NOTICE,
"DeleteRecordedFiles - " + logInfo);
2526 query.
prepare(
"SELECT basename, hostname, storagegroup FROM recordedfile "
2527 "WHERE recordedid = :RECORDEDID;");
2533 LOG(VB_GENERAL, LOG_ERR,
LOC +
2534 QString(
"Error querying recordedfiles for %1.") .
arg(logInfo));
2539 QString basename =
query.
value(0).toString();
2542 bool deleteInDB =
false;
2544 if (basename == QFileInfo(ds->
m_filename).fileName())
2573 update.
prepare(
"DELETE FROM recordedfile "
2574 "WHERE recordedid = :RECORDEDID "
2575 "AND basename = :BASENAME ;");
2577 update.
bindValue(
":BASENAME", basename);
2581 LOG(VB_GENERAL, LOG_ERR,
LOC +
2582 QString(
"Error querying recordedfile (%1) for %2.")
2592 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2596 LOG(VB_GENERAL, LOG_NOTICE,
"DoDeleteINDB - " + logInfo);
2599 query.
prepare(
"DELETE FROM recorded WHERE recordedid = :RECORDEDID AND "
2607 LOG(VB_GENERAL, LOG_ERR,
LOC +
2608 QString(
"Error deleting recorded entry for %1.") .
arg(logInfo));
2611 std::this_thread::sleep_for(1s);
2614 QString msg = QString(
"RECORDING_LIST_CHANGE DELETE %1")
2619 std::this_thread::sleep_for(3s);
2622 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2629 LOG(VB_GENERAL, LOG_ERR,
LOC +
2630 QString(
"Error deleting recordedmarkup for %1.") .
arg(logInfo));
2634 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2641 LOG(VB_GENERAL, LOG_ERR,
LOC +
2642 QString(
"Error deleting recordedseek for %1.")
2657 bool deleteBrokenSymlinks)
2661 QString linktext =
"";
2662 QByteArray fname =
filename.toLocal8Bit();
2664 LOG(VB_FILE, LOG_INFO,
LOC +
2665 QString(
"About to unlink/delete file: '%1'")
2666 .
arg(fname.constData()));
2668 QString errmsg = QString(
"Delete Error '%1'").arg(fname.constData());
2669 if (finfo.isSymLink())
2672 QByteArray alink = linktext.toLocal8Bit();
2673 errmsg += QString(
" -> '%2'").arg(alink.constData());
2676 if (followLinks && finfo.isSymLink())
2678 if (!finfo.exists() && deleteBrokenSymlinks)
2679 unlink(fname.constData());
2684 unlink(fname.constData());
2687 else if (!finfo.isSymLink())
2693 int err = unlink(fname.constData());
2699 LOG(VB_GENERAL, LOG_ERR,
LOC + errmsg +
ENO);
2715 QByteArray fname =
filename.toLocal8Bit();
2716 QString msg = QString(
"Error deleting '%1'").arg(fname.constData());
2717 int fd = open(fname.constData(), O_WRONLY);
2721 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
" could not open " +
ENO);
2725 if (unlink(fname.constData()))
2727 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
" could not unlink " +
ENO);
2757 query.
prepare(
"SELECT COUNT(cardid) FROM capturecard;");
2763 constexpr std::chrono::milliseconds sleep_time = 500ms;
2764 const size_t min_tps = 8 * 1024 * 1024;
2765 const auto calc_tps = (size_t) (cards * 1.2 * (22200000LL / 8.0));
2766 const size_t tps = std::max(min_tps, calc_tps);
2767 const auto increment = (size_t) (tps * (sleep_time.count() * 0.001F));
2769 LOG(VB_FILE, LOG_INFO,
LOC +
2770 QString(
"Truncating '%1' by %2 MB every %3 milliseconds")
2772 .
arg(increment / (1024.0 * 1024.0), 0,
'f', 2)
2773 .
arg(sleep_time.count()));
2775 GetMythDB()->GetDBManager()->PurgeIdleConnections(
false);
2781 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Truncating '%1' to %2 MB")
2785 int err = ftruncate(fd, fsize);
2788 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error truncating '%1'")
2792 return 0 ==
close(fd);
2797 if (pginfo && ((count % 100) == 0))
2802 std::this_thread::sleep_for(sleep_time);
2805 bool ok = (0 ==
close(fd));
2810 LOG(VB_FILE, LOG_INFO,
LOC +
2811 QString(
"Finished truncating '%1'").
arg(
filename));
2821 pbssock =
pbs->getSocket();
2823 QStringList::const_iterator it = slist.cbegin() + 1;
2845 result = iter.key();
2850 QStringList outputlist( QString::number(result) );
2857 QStringList::const_iterator it = slist.cbegin() + 1;
2869 bool hasConflicts =
false;
2871 for (
auto *pInfo : schedList)
2889 pbssock =
pbs->getSocket();
2912 (*m_encoderList)[num]->StopRecording();
2920 QStringList outputlist(
"0" );
2945 recnum = iter.key();
2952 std::this_thread::sleep_for(100us);
2968 QStringList outputlist( QString::number(recnum) );
2975 bool forceMetadataDelete,
2983 qDebug() <<
"HandleDeleteRecording(chanid, starttime) Empty Recording ID";
2990 pbssock =
pbs->getSocket();
2992 QStringList outputlist( QString::number(0) );
3002 bool forceMetadataDelete)
3004 QStringList::const_iterator it = slist.cbegin() + 1;
3009 qDebug() <<
"HandleDeleteRecording(QStringList) Empty Recording ID";
3018 bool forceMetadataDelete,
bool lexpirer,
bool forgetHistory)
3020 int resultCode = -1;
3023 pbssock =
pbs->getSocket();
3025 bool justexpire = lexpirer ?
false :
3033 LOG(VB_GENERAL, LOG_ERR,
LOC +
3034 QString(
"ERROR when trying to delete file for %1. Unable "
3035 "to determine filename of recording.")
3041 QStringList outputlist(QString::number(resultCode));
3051 if (justexpire && !forceMetadataDelete &&
3060 QStringList outputlist( QString::number(0) );
3084 QStringList outputlist( QString::number(num) );
3094 bool fileExists = checkFile.exists();
3097 QFile checkFileUTF8(QString::fromUtf8(
filename.toLatin1().constData()));
3098 fileExists = checkFileUTF8.exists();
3107 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3113 qDebug() <<
"DoHandleDeleteRecording() Empty Recording ID";
3120 forceMetadataDelete);
3121 deleteThread->start();
3126 QString logInfo = QString(
"chanid %1")
3130 LOG(VB_GENERAL, LOG_ERR,
LOC +
3131 QString(
"ERROR when trying to delete file: %1. File doesn't "
3132 "exist. Database metadata will not be removed.")
3139 QStringList outputlist( QString::number(resultCode) );
3151 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3154 QString(
"REC_DELETED CHANID %1 STARTTIME %2")
3164 if (slist.size() == 3)
3173 QStringList::const_iterator it = slist.cbegin()+1;
3187 pbssock =
pbs->getSocket();
3201 QStringList outputlist( QString::number(ret) );
3238 result = QStringList(QString::number(1));
3241 result = QStringList(QString::number(0));
3259 LOG(VB_GENERAL, LOG_INFO,
LOC +
"HandleAddChildInput: Already locked");
3263 LOG(VB_GENERAL, LOG_INFO,
LOC +
3264 QString(
"HandleAddChildInput: Handling input %1").
arg(inputid));
3274 LOG(VB_GENERAL, LOG_ERR,
LOC +
3275 QString(
"HandleAddChildInput: "
3276 "Failed to add child to input %1").
arg(inputid));
3282 LOG(VB_GENERAL, LOG_INFO,
LOC +
3283 QString(
"HandleAddChildInput: Added child input %1").
arg(childid));
3291 auto *tv =
new TVRec(childid);
3292 if (!tv || !tv->Init())
3294 LOG(VB_GENERAL, LOG_ERR,
LOC +
3295 QString(
"HandleAddChildInput: "
3296 "Failed to initialize input %1").
arg(childid));
3305 (*m_encoderList)[childid] = enc;
3312 LOG(VB_GENERAL, LOG_ERR,
LOC +
3313 QString(
"HandleAddChildInput: "
3314 "Failed to add remote input %1").
arg(childid));
3324 (*m_encoderList)[childid] = enc;
3333 auto *tv =
new TVRec(inputid);
3334 if (!tv || !tv->Init())
3336 LOG(VB_GENERAL, LOG_ERR,
LOC +
3337 QString(
"HandleAddChildInput: "
3338 "Failed to initialize input %1").
arg(inputid));
3346 (*m_encoderList)[inputid] = enc;
3352 LOG(VB_GENERAL, LOG_INFO,
LOC +
3353 QString(
"HandleAddChildInput: "
3354 "Successfully handled input %1").
arg(inputid));
3361 QStringList::const_iterator it = slist.cbegin() + 1;
3368 pbssock =
pbs->getSocket();
3371 QStringList outputlist( QString::number(0) );
3383 QStringList strlist;
3386 if (!sleepCmd.isEmpty())
3390 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
3391 "Received GO_TO_SLEEP command from master, running SleepCommand.");
3396 strlist <<
"ERROR: SleepCommand is empty";
3397 LOG(VB_GENERAL, LOG_ERR,
LOC +
3398 "ERROR: in HandleGoToSleep(), but no SleepCommand found!");
3414 QStringList strlist;
3446 QStringList strlist;
3465 QStringList shortlist;
3466 if (strlist.size() < 4)
3468 shortlist << QString(
"0");
3469 shortlist << QString(
"0");
3473 unsigned int index = (
uint)(strlist.size()) - 2;
3474 shortlist << strlist[index++];
3475 shortlist << strlist[index++];
3491 QStringList strlist;
3493 #if defined(_WIN32) || defined(Q_OS_ANDROID)
3494 strlist <<
"0" <<
"0" <<
"0";
3500 strlist <<
"getloadavg() failed";
3504 strlist << QString::number(loads[0])
3505 << QString::number(loads[1])
3506 << QString::number(loads[2]);
3521 QStringList strlist;
3522 std::chrono::seconds uptime = 0s;
3525 strlist << QString::number(uptime.count());
3529 strlist <<
"Could not determine uptime.";
3543 QStringList strlist;
3558 QStringList strlist;
3564 if (
getMemStats(totalMB, freeMB, totalVM, freeVM))
3566 strlist << QString::number(totalMB) << QString::number(freeMB)
3567 << QString::number(totalVM) << QString::number(freeVM);
3572 strlist <<
"Could not determine memory stats.";
3586 QStringList strlist;
3601 bool checkSlaves = slist[1].toInt() != 0;
3603 QStringList::const_iterator it = slist.cbegin() + 2;
3606 bool exists =
false;
3619 QStringList outputlist( QString::number(
static_cast<int>(exists)) );
3634 exists = QFileInfo::exists(pburl);
3639 QStringList strlist( QString::number(
static_cast<int>(exists)) );
3651 QString storageGroup =
"Default";
3656 switch (slist.size()) {
3658 if (!slist[3].isEmpty())
3660 [[clang::fallthrough]];
3662 if (slist[2].isEmpty())
3663 storageGroup = slist[2];
3664 [[clang::fallthrough]];
3671 LOG(VB_GENERAL, LOG_ERR,
LOC +
3672 QString(
"ERROR checking for file, filename '%1' "
3680 LOG(VB_GENERAL, LOG_ERR,
LOC +
3681 "ERROR, invalid input count for QUERY_FILE_HASH");
3719 QString storageGroup =
"Default";
3720 QStringList retlist;
3722 if (slist.size() > 2)
3723 storageGroup = slist[2];
3729 LOG(VB_GENERAL, LOG_ERR,
LOC +
3730 QString(
"ERROR checking for file, filename '%1' "
3737 if (storageGroup.isEmpty())
3738 storageGroup =
"Default";
3744 if (!fullname.isEmpty())
3747 retlist << fullname;
3749 struct stat fileinfo {};
3750 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
3752 retlist << QString::number(fileinfo.st_dev);
3753 retlist << QString::number(fileinfo.st_ino);
3754 retlist << QString::number(fileinfo.st_mode);
3755 retlist << QString::number(fileinfo.st_nlink);
3756 retlist << QString::number(fileinfo.st_uid);
3757 retlist << QString::number(fileinfo.st_gid);
3758 retlist << QString::number(fileinfo.st_rdev);
3759 retlist << QString::number(fileinfo.st_size);
3764 retlist << QString::number(fileinfo.st_blksize);
3765 retlist << QString::number(fileinfo.st_blocks);
3767 retlist << QString::number(fileinfo.st_atime);
3768 retlist << QString::number(fileinfo.st_mtime);
3769 retlist << QString::number(fileinfo.st_ctime);
3781 query.
prepare(
"SELECT MAX(endtime) FROM program WHERE manualid = 0;");
3791 QDateTime GuideDataThrough;
3793 QStringList strlist;
3797 if (GuideDataThrough.isNull())
3798 strlist << QString(
"0000-00-00 00:00");
3800 strlist << GuideDataThrough.toString(
"yyyy-MM-dd hh:mm");
3806 const QString& tmptable,
int recordid)
3810 QStringList strList;
3814 if (tmptable.isEmpty())
3827 "WHERE recordid = :RECID;");
3833 record->m_recordID = recordid;
3834 if (record->Load() &&
3840 query.
prepare(
"DELETE FROM program WHERE manualid = :RECID;");
3850 strList << QString::number(0);
3851 strList << QString::number(0);
3861 QStringList strList;
3866 strList << QString::number(0);
3876 QStringList::const_iterator it = slist.cbegin() + 1;
3879 QStringList strlist;
3884 strlist << QString::number(0);
3893 QStringList strList;
3898 strList << QString::number(0);
3907 QStringList strList;
3909 if ((sList.size() < 4) || (sList.size() > 5))
3911 LOG(VB_GENERAL, LOG_ERR,
LOC +
3912 QString(
"HandleSGGetFileList: Invalid Request. %1")
3913 .
arg(sList.join(
"[]:[]")));
3914 strList <<
"EMPTY LIST";
3920 const QString& wantHost = sList.at(1);
3921 QHostAddress wantHostaddr(wantHost);
3922 const QString& groupname = sList.at(2);
3923 const QString& path = sList.at(3);
3924 bool fileNamesOnly =
false;
3926 if (sList.size() >= 5)
3927 fileNamesOnly = (sList.at(4).toInt() != 0);
3929 bool slaveUnreachable =
false;
3931 LOG(VB_FILE, LOG_INFO,
LOC +
3932 QString(
"HandleSGGetFileList: group = %1 host = %2 "
3933 " path = %3 wanthost = %4")
3938 if ((host.toLower() == wantHost.toLower()) ||
3939 (!addr.isEmpty() && addr == wantHostaddr.toString()))
3942 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGGetFileList: Getting local info");
3953 LOG(VB_FILE, LOG_INFO,
LOC +
3954 "HandleSGGetFileList: Getting remote info");
3958 slaveUnreachable =
false;
3962 LOG(VB_FILE, LOG_INFO,
LOC +
3963 QString(
"HandleSGGetFileList: Failed to grab slave socket "
3964 ": %1 :").
arg(wantHost));
3965 slaveUnreachable =
true;
3970 if (slaveUnreachable)
3971 strList <<
"SLAVE UNREACHABLE: " << host;
3973 if (strList.isEmpty() || (strList.at(0) ==
"0"))
3974 strList <<
"EMPTY LIST";
3984 QString storageGroup = slist[2];
3986 bool allowFallback =
true;
3987 bool useRegex =
false;
3988 QStringList fileList;
3990 if (!QHostAddress(
hostname).isNull())
3992 LOG(VB_GENERAL, LOG_ERR, QString(
"Mainserver: QUERY_FINDFILE called "
3993 "with IP (%1) instead of hostname. "
4000 if (storageGroup.isEmpty())
4001 storageGroup =
"Default";
4006 LOG(VB_GENERAL, LOG_ERR,
LOC +
4007 QString(
"ERROR QueryFindFile, filename '%1' "
4009 fileList <<
"ERROR: Bad/Missing Filename";
4014 if (slist.size() >= 5)
4015 useRegex = (slist[4].toInt() > 0);
4017 if (slist.size() >= 6)
4018 allowFallback = (slist[5].toInt() > 0);
4020 LOG(VB_FILE, LOG_INFO,
LOC +
4021 QString(
"Looking for file '%1' on host '%2' in group '%3' (useregex: %4, allowfallback: %5")
4035 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4037 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'").
arg(fi.path()).arg(fi.fileName()));
4039 for (
int x = 0; x < files.size(); x++)
4041 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").
arg(x).
arg(files[x]));
4044 QStringList filteredFiles = files.filter(QRegExp(fi.fileName()));
4045 for (
int x = 0; x < filteredFiles.size(); x++)
4049 fi.path() +
'/' + filteredFiles[x],
4065 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Checking remote host '%1' for file").
arg(
hostname));
4073 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4074 fileList += slaveFiles;
4080 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Slave '%1' was unreachable").
arg(
hostname));
4081 fileList << QString(
"ERROR: SLAVE UNREACHABLE: %1").arg(
hostname);
4089 if (
m_ismaster && fileList.isEmpty() && allowFallback)
4094 QString sql =
"SELECT DISTINCT hostname "
4095 "FROM storagegroup "
4096 "WHERE groupname = :GROUP "
4097 "AND hostname != :HOSTNAME";
4105 fileList <<
"ERROR: failed to get host list";
4121 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4123 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'").
arg(fi.path()).arg(fi.fileName()));
4125 for (
int x = 0; x < files.size(); x++)
4127 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").
arg(x).
arg(files[x]));
4130 QStringList filteredFiles = files.filter(QRegExp(fi.fileName()));
4132 for (
int x = 0; x < filteredFiles.size(); x++)
4136 fi.path() +
'/' + filteredFiles[x],
4143 if (!fname.isEmpty())
4158 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4159 fileList += slaveFiles;
4165 if (!fileList.isEmpty())
4170 if (fileList.isEmpty())
4172 fileList <<
"NOT FOUND";
4173 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File was not found"));
4177 for (
int x = 0; x < fileList.size(); x++)
4179 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File %1 was found at: '%2'").
arg(x).
arg(fileList[0]));
4192 QStringList strList;
4194 if (sList.size() < 4)
4196 LOG(VB_GENERAL, LOG_ERR,
LOC +
4197 QString(
"HandleSGFileQuery: Invalid Request. %1")
4198 .
arg(sList.join(
"[]:[]")));
4199 strList <<
"EMPTY LIST";
4205 const QString& wantHost = sList.at(1);
4206 QHostAddress wantHostaddr(wantHost);
4207 const QString& groupname = sList.at(2);
4208 const QString&
filename = sList.at(3);
4210 bool allowFallback =
true;
4211 if (sList.size() >= 5)
4212 allowFallback = (sList.at(4).toInt() > 0);
4213 LOG(VB_FILE, LOG_ERR, QString(
"HandleSGFileQuery - allowFallback: %1").
arg(allowFallback));
4215 bool slaveUnreachable =
false;
4217 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"HandleSGFileQuery: %1")
4222 if ((host.toLower() == wantHost.toLower()) ||
4223 (!addr.isEmpty() && addr == wantHostaddr.toString()))
4225 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGFileQuery: Getting local info");
4234 LOG(VB_FILE, LOG_INFO,
LOC +
4235 "HandleSGFileQuery: Getting remote info");
4238 slaveUnreachable =
false;
4242 LOG(VB_FILE, LOG_INFO,
LOC +
4243 QString(
"HandleSGFileQuery: Failed to grab slave socket : %1 :")
4245 slaveUnreachable =
true;
4250 if (slaveUnreachable)
4251 strList <<
"SLAVE UNREACHABLE: " << wantHost;
4253 if (strList.count() == 0 || (strList.at(0) ==
"0"))
4254 strList <<
"EMPTY LIST";
4262 QString pbshost =
pbs->getHostname();
4264 QStringList strlist;
4273 if ((cardid != -1) && (cardid != elink->GetInputID()))
4276 if (elink->IsLocal())
4279 enchost = elink->GetHostName();
4281 if ((enchost == pbshost) &&
4282 (elink->IsConnected()) &&
4283 (!elink->IsBusy()) &&
4284 (!elink->IsTunerLocked()))
4298 QString msg = QString(
"Cardid %1 LOCKed for external use on %2.")
4299 .arg(retval).arg(pbshost);
4300 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4306 "WHERE cardid = :CARDID ;");
4312 strlist << QString::number(retval)
4323 LOG(VB_GENERAL, LOG_ERR,
LOC +
4324 "MainServer::LockTuner(): Could not find "
4325 "card info in database");
4330 strlist <<
"-2" <<
"" <<
"" <<
"";
4336 strlist <<
"-1" <<
"" <<
"" <<
"";
4343 QStringList strlist;
4350 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleFreeTuner() " +
4351 QString(
"Unknown encoder: %1").
arg(cardid));
4352 strlist <<
"FAILED";
4359 QString msg = QString(
"Cardid %1 FREED from external use on %2.")
4360 .arg(cardid).arg(
pbs->getHostname());
4361 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4381 uint excluded_input)
4383 LOG(VB_CHANNEL, LOG_INFO,
4384 LOC + QString(
"Excluding input %1")
4385 .
arg(excluded_input));
4388 vector<InputInfo> busyinputs;
4389 vector<InputInfo> freeinputs;
4390 QMap<uint, QSet<uint> > groupids;
4400 if (!elink->IsConnected() || elink->IsTunerLocked())
4402 LOG(VB_CHANNEL, LOG_INFO,
4403 LOC + QString(
"Input %1 is locked or not connected")
4408 vector<uint> infogroups;
4410 for (
uint group : infogroups)
4414 if (info.
m_inputId != excluded_input && elink->IsBusy(&busyinfo))
4416 LOG(VB_CHANNEL, LOG_DEBUG,
4417 LOC + QString(
"Input %1 is busy on %2/%3")
4421 busyinputs.push_back(info);
4425 LOG(VB_CHANNEL, LOG_DEBUG,
4426 LOC + QString(
"Input %1 is free")
4428 freeinputs.push_back(info);
4435 for (
auto & busyinfo : busyinputs)
4437 auto freeiter = freeinputs.begin();
4438 while (freeiter != freeinputs.end())
4442 if ((groupids[busyinfo.m_inputId] & groupids[freeinfo.
m_inputId])
4449 if (busyinfo.m_sourceId == freeinfo.
m_sourceId)
4451 LOG(VB_CHANNEL, LOG_DEBUG,
4452 LOC + QString(
"Input %1 is limited to %2/%3 by input %4")
4454 .arg(busyinfo.m_mplexId).arg(busyinfo.m_inputId));
4455 freeinfo.
m_chanId = busyinfo.m_chanId;
4456 freeinfo.
m_mplexId = busyinfo.m_mplexId;
4461 LOG(VB_CHANNEL, LOG_DEBUG,
4462 LOC + QString(
"Input %1 is unavailable by input %2")
4464 freeiter = freeinputs.erase(freeiter);
4470 QStringList strlist;
4471 for (
auto & input : freeinputs)
4473 LOG(VB_CHANNEL, LOG_INFO,
4474 LOC + QString(
"Input %1 is available on %2/%3")
4475 .
arg(input.m_inputId).arg(input.m_chanId)
4476 .arg(input.m_mplexId));
4477 input.ToStringList(strlist);
4480 if (strlist.empty())
4505 if (commands.size() < 2 || slist.size() < 2)
4508 int recnum = commands[1].toInt();
4515 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleRecorderQuery() " +
4516 QString(
"Unknown encoder: %1").
arg(recnum));
4517 QStringList retlist(
"bad" );
4523 QString command = slist[1];
4525 QStringList retlist;
4530 LOG(VB_GENERAL, LOG_ERR,
LOC +
" MainServer::HandleRecorderQuery() " +
4531 QString(
"Command %1 for unconnected encoder %2")
4532 .
arg(command).
arg(recnum));
4538 if (command ==
"IS_RECORDING")
4542 else if (command ==
"GET_FRAMERATE")
4546 else if (command ==
"GET_FRAMES_WRITTEN")
4550 else if (command ==
"GET_FILE_POSITION")
4554 else if (command ==
"GET_MAX_BITRATE")
4558 else if (command ==
"GET_CURRENT_RECORDING")
4573 else if (command ==
"GET_KEYFRAME_POS")
4575 long long desired = slist[2].toLongLong();
4578 else if (command ==
"FILL_POSITION_MAP")
4580 int64_t start = slist[2].toLongLong();
4581 int64_t end = slist[3].toLongLong();
4590 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4592 retlist += QString::number(it.key());
4593 retlist += QString::number(*it);
4595 if (retlist.empty())
4599 else if (command ==
"FILL_DURATION_MAP")
4601 int64_t start = slist[2].toLongLong();
4602 int64_t end = slist[3].toLongLong();
4611 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4613 retlist += QString::number(it.key());
4614 retlist += QString::number(*it);
4616 if (retlist.empty())
4620 else if (command ==
"GET_RECORDING")
4635 else if (command ==
"FRONTEND_READY")
4640 else if (command ==
"CANCEL_NEXT_RECORDING")
4642 QString cancel = slist[2];
4643 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
4644 QString(
"Received: CANCEL_NEXT_RECORDING %1").
arg(cancel));
4648 else if (command ==
"SPAWN_LIVETV")
4650 QString chainid = slist[2];
4661 enc->
SpawnLiveTV(chain, slist[3].toInt() != 0, slist[4]);
4664 else if (command ==
"STOP_LIVETV")
4681 else if (command ==
"PAUSE")
4686 else if (command ==
"FINISH_RECORDING")
4691 else if (command ==
"SET_LIVE_RECORDING")
4693 int recording = slist[2].toInt();
4697 else if (command ==
"GET_INPUT")
4700 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4703 else if (command ==
"SET_INPUT")
4705 QString input = slist[2];
4706 QString ret = enc->
SetInput(input);
4707 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4710 else if (command ==
"TOGGLE_CHANNEL_FAVORITE")
4712 QString changroup = slist[2];
4716 else if (command ==
"CHANGE_CHANNEL")
4722 else if (command ==
"SET_CHANNEL")
4724 QString name = slist[2];
4728 else if (command ==
"SET_SIGNAL_MONITORING_RATE")
4730 auto rate = std::chrono::milliseconds(slist[2].toInt());
4731 int notifyFrontend = slist[3].toInt();
4733 retlist << QString::number(oldrate.count());
4735 else if (command ==
"GET_COLOUR")
4738 retlist << QString::number(ret);
4740 else if (command ==
"GET_CONTRAST")
4743 retlist << QString::number(ret);
4745 else if (command ==
"GET_BRIGHTNESS")
4748 retlist << QString::number(ret);
4750 else if (command ==
"GET_HUE")
4753 retlist << QString::number(ret);
4755 else if (command ==
"CHANGE_COLOUR")
4757 int type = slist[2].toInt();
4758 bool up = slist[3].toInt() != 0;
4761 retlist << QString::number(ret);
4763 else if (command ==
"CHANGE_CONTRAST")
4765 int type = slist[2].toInt();
4766 bool up = slist[3].toInt() != 0;
4769 retlist << QString::number(ret);
4771 else if (command ==
"CHANGE_BRIGHTNESS")
4773 int type= slist[2].toInt();
4774 bool up = slist[3].toInt() != 0;
4777 retlist << QString::number(ret);
4779 else if (command ==
"CHANGE_HUE")
4781 int type= slist[2].toInt();
4782 bool up = slist[3].toInt() != 0;
4785 retlist << QString::number(ret);
4787 else if (command ==
"CHECK_CHANNEL")
4789 QString name = slist[2];
4790 retlist << QString::number((
int)(enc->
CheckChannel(name)));
4792 else if (command ==
"SHOULD_SWITCH_CARD")
4794 QString chanid = slist[2];
4797 else if (command ==
"CHECK_CHANNEL_PREFIX")
4799 QString needed_spacer;
4800 QString
prefix = slist[2];
4801 uint complete_valid_channel_on_rec = 0;
4802 bool is_extra_char_useful =
false;
4805 prefix, complete_valid_channel_on_rec,
4806 is_extra_char_useful, needed_spacer);
4808 retlist << QString::number((
int)match);
4809 retlist << QString::number(complete_valid_channel_on_rec);
4810 retlist << QString::number((
int)is_extra_char_useful);
4811 retlist << ((needed_spacer.isEmpty()) ? QString(
"X") : needed_spacer);
4813 else if (command ==
"GET_NEXT_PROGRAM_INFO" && (slist.size() >= 6))
4815 QString channelname = slist[2];
4816 uint chanid = slist[3].toUInt();
4818 QString starttime = slist[5];
4821 QString subtitle =
"";
4824 QString endtime =
"";
4825 QString callsign =
"";
4826 QString iconpath =
"";
4827 QString seriesid =
"";
4828 QString programid =
"";
4832 endtime, callsign, iconpath, channelname, chanid,
4833 seriesid, programid);
4844 retlist << QString::number(chanid);
4848 else if (command ==
"GET_CHANNEL_INFO")
4850 uint chanid = slist[2].toUInt();
4852 QString callsign =
"";
4853 QString channum =
"";
4854 QString channame =
"";
4858 callsign, channum, channame, xmltv);
4860 retlist << QString::number(chanid);
4861 retlist << QString::number(sourceid);
4869 LOG(VB_GENERAL, LOG_ERR,
LOC +
4870 QString(
"Unknown command: %1").
arg(command));
4882 int recnum = commands[1].toInt();
4889 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleSetNextLiveTVDir() " +
4890 QString(
"Unknown encoder: %1").
arg(recnum));
4891 QStringList retlist(
"bad" );
4900 QStringList retlist(
"OK" );
4908 uint chanid = slist[1].toUInt();
4909 uint sourceid = slist[2].toUInt();
4910 QString oldcnum =
cleanup(slist[3]);
4911 QString callsign =
cleanup(slist[4]);
4912 QString channum =
cleanup(slist[5]);
4913 QString channame =
cleanup(slist[6]);
4914 QString xmltv =
cleanup(slist[7]);
4916 QStringList retlist;
4917 if (!chanid || !sourceid)
4929 ok &= encoder->SetChannelInfo(chanid, sourceid, oldcnum,
4930 callsign, channum, channame, xmltv);
4935 retlist << ((ok) ?
"1" :
"0");
4944 int recnum = commands[1].toInt();
4945 QStringList retlist;
4952 LOG(VB_GENERAL, LOG_ERR,
LOC +
4953 QString(
"HandleRemoteEncoder(cmd %1) ").
arg(slist[1]) +
4954 QString(
"Unknown encoder: %1").
arg(recnum));
4963 QString command = slist[1];
4965 if (command ==
"GET_STATE")
4967 retlist << QString::number((
int)enc->
GetState());
4969 else if (command ==
"GET_SLEEPSTATUS")
4973 else if (command ==
"GET_FLAGS")
4975 retlist << QString::number(enc->
GetFlags());
4977 else if (command ==
"IS_BUSY")
4979 auto arg2 = std::chrono::seconds(slist[2].toInt());
4980 std::chrono::seconds time_buffer = (slist.size() >= 3) ? arg2 : 5s;
4982 retlist << QString::number((
int)enc->
IsBusy(&busy_input, time_buffer));
4985 else if (command ==
"MATCHES_RECORDING" &&
4988 QStringList::const_iterator it = slist.cbegin() + 2;
4993 else if (command ==
"START_RECORDING" &&
4996 QStringList::const_iterator it = slist.cbegin() + 2;
5003 else if (command ==
"GET_RECORDING_STATUS")
5007 else if (command ==
"RECORD_PENDING" &&
5010 auto secsleft = std::chrono::seconds(slist[2].toInt());
5011 int haslater = slist[3].toInt();
5012 QStringList::const_iterator it = slist.cbegin() + 4;
5019 else if (command ==
"CANCEL_NEXT_RECORDING" &&
5020 (slist.size() >= 3))
5022 bool cancel = (
bool) slist[2].toInt();
5026 else if (command ==
"STOP_RECORDING")
5031 else if (command ==
"GET_MAX_BITRATE")
5035 else if (command ==
"GET_CURRENT_RECORDING")
5063 if (
pbs->isMediaServer())
5074 QStringList retlist;
5076 retlist.push_front(QString::number(retlist.size()));
5083 QStringList retlist;
5084 QString queryhostname = slist[1];
5089 if (slave !=
nullptr)
5105 QString fskey = fsInfo->getHostname() +
":" + fsInfo->getPath();
5115 size_t totalKBperMin = 0;
5120 if (!enc->IsConnected() || !enc->IsBusy())
5123 long long maxBitrate = enc->GetMaxBitrate();
5125 maxBitrate = 19500000LL;
5126 long long thisKBperMin = (((size_t)maxBitrate)*((size_t)15))>>11;
5127 totalKBperMin += thisKBperMin;
5128 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Cardid %1: max bitrate %2 KB/min")
5129 .
arg(enc->GetInputID()).arg(thisKBperMin));
5133 LOG(VB_FILE, LOG_INFO,
LOC +
5134 QString(
"Maximal bitrate of busy encoders is %1 KB/min")
5135 .
arg(totalKBperMin));
5137 return totalKBperMin;
5144 int64_t totalKB = -1;
5145 int64_t usedKB = -1;
5146 QMap <QString, bool>foundDirs;
5147 QString localStr =
"1";
5148 struct statfs statbuf {};
5150 groups.removeAll(
"LiveTV");
5151 QString specialGroups = groups.join(
"', '");
5152 QString sql = QString(
"SELECT MIN(id),dirname "
5153 "FROM storagegroup "
5154 "WHERE hostname = :HOSTNAME "
5155 "AND groupname NOT IN ( '%1' ) "
5156 "GROUP BY dirname;").arg(specialGroups);
5168 "FROM storagegroup "
5169 "WHERE groupname = :GROUP "
5170 "GROUP BY dirname;");
5185 currentDir = QString::fromUtf8(
query.
value(1)
5186 .toByteArray().constData());
5187 if (currentDir.endsWith(
"/"))
5188 currentDir.remove(currentDir.length() - 1, 1);
5190 checkDir.setPath(currentDir);
5191 if (!foundDirs.contains(currentDir))
5193 if (checkDir.exists())
5195 QByteArray cdir = currentDir.toLatin1();
5197 memset(&statbuf, 0,
sizeof(statbuf));
5201 if (
statfs(currentDir.toLocal8Bit().constData(), &statbuf) == 0)
5204 char *fstypename = statbuf.f_fstypename;
5205 if ((!strcmp(fstypename,
"nfs")) ||
5206 (!strcmp(fstypename,
"afpfs")) ||
5207 (!strcmp(fstypename,
"smbfs")))
5210 long fstype = statbuf.f_type;
5211 if ((fstype == 0x6969) ||
5212 (fstype == 0x517B) ||
5213 (fstype == (
long)0xFF534D42))
5220 strlist << currentDir;
5221 strlist << localStr;
5224 strlist << QString::number(bSize);
5225 strlist << QString::number(totalKB);
5226 strlist << QString::number(usedKB);
5228 foundDirs[currentDir] =
true;
5231 foundDirs[currentDir] =
false;
5238 QMap <QString, bool> backendsCounted;
5239 std::list<PlaybackSock *> localPlaybackList;
5245 if ((
pbs->IsDisconnected()) ||
5246 (!
pbs->isMediaServer()) ||
5248 (backendsCounted.contains(
pbs->getHostname())))
5251 backendsCounted[
pbs->getHostname()] =
true;
5253 localPlaybackList.push_back(
pbs);
5254 allHostList +=
"," +
pbs->getHostname();
5259 for (
auto &
pbs : localPlaybackList) {
5260 pbs->GetDiskSpace(strlist);
5268 QList<FileSystemInfo> fsInfos;
5269 QStringList::const_iterator it = strlist.cbegin();
5270 while (it != strlist.cend())
5276 fsInfo.
setLocal((*(it++)).toInt() > 0);
5283 fsInfos.push_back(fsInfo);
5289 maxWriteFiveSec = std::max((int64_t)2048, maxWriteFiveSec);
5291 for (
auto it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5293 if (it1->getFSysID() == -1)
5297 it1->getHostname().section(
".", 0, 0) +
":" + it1->getPath());
5300 for (
auto it2 = it1 + 1; it2 != fsInfos.end(); ++it2)
5304 int bSize = std::max(32, std::max(it1->getBlockSize(), it2->getBlockSize()) / 1024);
5305 int64_t diffSize = it1->getTotalSpace() - it2->getTotalSpace();
5306 int64_t diffUsed = it1->getUsedSpace() - it2->getUsedSpace();
5308 diffSize = 0 - diffSize;
5310 diffUsed = 0 - diffUsed;
5312 if (it2->getFSysID() == -1 && (diffSize <= bSize) &&
5313 (diffUsed <= maxWriteFiveSec))
5315 if (!it1->getHostname().contains(it2->getHostname()))
5316 it1->setHostname(it1->getHostname() +
"," + it2->getHostname());
5317 it1->setPath(it1->getPath() +
"," +
5318 it2->getHostname().section(
".", 0, 0) +
":" + it2->getPath());
5328 for (
const auto & fsInfo : qAsConst(fsInfos))
5330 strlist << fsInfo.getHostname();
5331 strlist << fsInfo.getPath();
5332 strlist << QString::number(static_cast<int>(fsInfo.isLocal()));
5333 strlist << QString::number(fsInfo.getFSysID());
5334 strlist << QString::number(fsInfo.getGroupID());
5335 strlist << QString::number(fsInfo.getBlockSize());
5336 strlist << QString::number(fsInfo.getTotalSpace());
5337 strlist << QString::number(fsInfo.getUsedSpace());
5339 totalKB += fsInfo.getTotalSpace();
5340 usedKB += fsInfo.getUsedSpace();
5345 strlist << allHostList;
5346 strlist <<
"TotalDiskSpace";
5351 strlist << QString::number(totalKB);
5352 strlist << QString::number(usedKB);
5367 QStringList strlist;
5374 QStringList::const_iterator it = strlist.cbegin();
5375 while (it != strlist.cend())
5379 fsInfo.
setLocal((*(it++)).toInt() > 0);
5387 fsInfos.push_back(fsInfo);
5390 LOG(VB_SCHEDULE | VB_FILE, LOG_DEBUG,
LOC +
5391 "Determining unique filesystems");
5394 maxWriteFiveSec = std::max((
size_t)2048, maxWriteFiveSec);
5398 QList<FileSystemInfo>::iterator it1;
5401 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5402 "--- GetFilesystemInfos directory list start ---");
5403 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5406 QString(
"Dir: %1:%2")
5407 .arg(it1->getHostname())
5408 .arg(it1->getPath());
5409 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC + msg) ;
5410 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5411 QString(
" Location: %1")
5412 .
arg(it1->isLocal() ?
"Local" :
"Remote"));
5413 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5414 QString(
" fsID : %1")
5415 .
arg(it1->getFSysID()));
5416 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5417 QString(
" dirID : %1")
5418 .
arg(it1->getGroupID()));
5419 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5420 QString(
" BlkSize : %1")
5421 .
arg(it1->getBlockSize()));
5422 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5423 QString(
" TotalKB : %1")
5424 .
arg(it1->getTotalSpace()));
5425 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5426 QString(
" UsedKB : %1")
5427 .
arg(it1->getUsedSpace()));
5428 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5429 QString(
" FreeKB : %1")
5430 .
arg(it1->getFreeSpace()));
5432 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5433 "--- GetFilesystemInfos directory list end ---");
5442 const QString &src,
const QString &dst)
5445 QStringList retlist;
5447 if (src.isEmpty() || dst.isEmpty()
5448 || src.contains(
"..") || dst.contains(
".."))
5450 LOG(VB_GENERAL, LOG_ERR,
LOC +
5451 QString(
"HandleMoveFile: ERROR moving file '%1' -> '%2', "
5452 "a path fails sanity checks").
arg(src, dst));
5453 retlist <<
"0" <<
"Invalid path";
5458 QString srcAbs = sgroup.
FindFile(src);
5459 if (srcAbs.isEmpty())
5461 LOG(VB_GENERAL, LOG_ERR,
LOC +
5462 QString(
"HandleMoveFile: Unable to find %1").
arg(src));
5463 retlist <<
"0" <<
"Source file not found";
5471 QString dstAbs = sgroup.
FindFile(dst);
5472 if (!dstAbs.isEmpty() && QFileInfo(dstAbs).isFile())
5474 LOG(VB_GENERAL, LOG_ERR,
LOC +
5475 QString(
"HandleMoveFile: Destination exists at %1").
arg(dstAbs));
5476 retlist <<
"0" <<
"Destination file exists";
5482 int sgPathSize = srcAbs.size() - src.size();
5483 dstAbs = srcAbs.mid(0, sgPathSize) + dst;
5497 LOG(VB_FILE, LOG_INFO, QString(
"MainServer::RenameThread: Renaming %1 -> %2")
5500 QStringList retlist;
5501 QFileInfo fi(
m_dst);
5503 if (QDir().mkpath(fi.path()) && QFile::rename(
m_src,
m_dst))
5509 retlist <<
"0" <<
"Rename failed";
5510 LOG(VB_FILE, LOG_ERR,
"MainServer::DoRenameThread: Rename failed");
5543 QStringList retlist;
5549 LOG(VB_GENERAL, LOG_ERR,
LOC +
5550 QString(
"ERROR deleting file, filename '%1' "
5562 if (fullfile.isEmpty()) {
5563 LOG(VB_GENERAL, LOG_ERR,
LOC +
5564 QString(
"Unable to find %1 in HandleDeleteFile()") .
arg(
filename));
5573 QFile checkFile(fullfile);
5580 const QFileInfo info(fullfile);
5584 if ((fd < 0) && checkFile.exists())
5586 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting file: %1.")
5606 auto *truncateThread =
new TruncateThread(
this, fullfile, fd, size);
5607 truncateThread->run();
5615 const QString &starttime,
5620 pbssock =
pbs->getSocket();
5623 frm_dir_map_t::const_iterator it;
5625 QStringList retlist;
5628 const ProgramInfo pginfo(chanid.toUInt(), recstartdt);
5637 for (it = markMap.cbegin(); it != markMap.cend(); ++it)
5640 QString intstr = QString(
"%1").arg(*it);
5642 retlist << QString::number(it.key());
5647 retlist.prepend(QString(
"%1").
arg(rowcnt));
5656 const QString &starttime,
5671 const QString &starttime,
5687 const QString &starttime,
5698 pbssock =
pbs->getSocket();
5702 chanid.toUInt(), recstartts);
5704 QStringList retlist;
5705 retlist << QString::number(bookmark);
5724 pbssock =
pbs->getSocket();
5726 QString chanid = tokens[1];
5727 QString starttime = tokens[2];
5728 long long bookmark = tokens[3].toLongLong();
5731 QStringList retlist;
5741 retlist <<
"FAILED";
5754 pbssock =
pbs->getSocket();
5757 QString setting = tokens[2];
5758 QStringList retlist;
5762 retlist << retvalue;
5770 bool synchronous = (command[0] ==
"DOWNLOAD_FILE_NOW");
5771 QString srcURL = command[1];
5772 QString storageGroup = command[2];
5777 QStringList retlist;
5781 pbssock =
pbs->getSocket();
5785 QFileInfo finfo(srcURL);
5789 if (outDir.isEmpty())
5791 LOG(VB_GENERAL, LOG_ERR,
LOC +
5792 QString(
"Unable to determine directory "
5793 "to write to in %1 write command").
arg(command[0]));
5794 retlist <<
"downloadfile_directory_not_found";
5803 LOG(VB_GENERAL, LOG_ERR,
LOC +
5804 QString(
"ERROR: %1 write filename '%2' does not pass "
5806 retlist <<
"downloadfile_filename_dangerous";
5847 pbssock =
pbs->getSocket();
5850 QString setting = tokens[2];
5851 QString svalue = tokens[3];
5852 QStringList retlist;
5867 QStringList retlist;
5887 QStringList strlist;
5893 QString sql =
"SELECT DISTINCT hostname "
5894 "FROM storagegroup "
5895 "WHERE groupname = 'Music'";
5907 LOG(VB_GENERAL, LOG_INFO,
LOC +
5908 QString(
"HandleScanMusic: running filescanner on master BE '%1'").
arg(
hostname));
5920 LOG(VB_GENERAL, LOG_INFO,
LOC +
5921 QString(
"HandleScanMusic: asking slave '%1' to run file scanner").
arg(
hostname));
5927 LOG(VB_GENERAL, LOG_INFO,
LOC +
5928 QString(
"HandleScanMusic: Failed to grab slave socket on '%1'").
arg(
hostname));
5937 LOG(VB_GENERAL, LOG_INFO,
LOC +
5938 QString(
"HandleScanMusic: running filescanner on slave BE '%1'")
5956 QStringList strlist;
5968 LOG(VB_GENERAL, LOG_INFO,
LOC +
5969 QString(
"HandleMusicTagUpdateVolatile: asking slave '%1' to update the metadata").
arg(
hostname));
5979 LOG(VB_GENERAL, LOG_INFO,
LOC +
5980 QString(
"HandleMusicTagUpdateVolatile: Failed to grab slave socket on '%1'").
arg(
hostname));
5982 strlist <<
"ERROR: slave not found";
5991 QStringList paramList;
5992 paramList.append(QString(
"--songid='%1'").
arg(slist[2]));
5993 paramList.append(QString(
"--rating='%1'").
arg(slist[3]));
5994 paramList.append(QString(
"--playcount='%1'").
arg(slist[4]));
5995 paramList.append(QString(
"--lastplayed='%1'").
arg(slist[5]));
5997 QString command =
GetAppBinDir() +
"mythutil --updatemeta " + paramList.join(
" ");
5999 LOG(VB_GENERAL, LOG_INFO,
LOC +
6000 QString(
"HandleMusicTagUpdateVolatile: running %1'").
arg(command));
6016 QStringList strlist;
6028 LOG(VB_GENERAL, LOG_INFO,
LOC +
6029 QString(
"HandleMusicCalcTrackLen: asking slave '%1' to update the track length").
arg(
hostname));
6039 LOG(VB_GENERAL, LOG_INFO,
LOC +
6040 QString(
"HandleMusicCalcTrackLen: Failed to grab slave socket on '%1'").
arg(
hostname));
6042 strlist <<
"ERROR: slave not found";