00001 #include <list>
00002 #include <cstdlib>
00003 #include <iostream>
00004 #include <algorithm>
00005 #include <cerrno>
00006 #include <memory>
00007 using namespace std;
00008
00009 #include <math.h>
00010 #include <unistd.h>
00011 #include <fcntl.h>
00012 #include "mythconfig.h"
00013
00014 #ifndef USING_MINGW
00015 #include <sys/ioctl.h>
00016 #endif
00017
00018 #include <sys/stat.h>
00019 #ifdef __linux__
00020 # include <sys/vfs.h>
00021 #else // if !__linux__
00022 # include <sys/param.h>
00023 # ifndef USING_MINGW
00024 # include <sys/mount.h>
00025 # endif // USING_MINGW
00026 #endif // !__linux__
00027
00028 #include <QCoreApplication>
00029 #include <QDateTime>
00030 #include <QFile>
00031 #include <QDir>
00032 #include <QWaitCondition>
00033 #include <QRegExp>
00034 #include <QEvent>
00035 #include <QUrl>
00036 #include <QTcpServer>
00037 #include <QTimer>
00038 #include <QNetworkInterface>
00039 #include <QNetworkProxy>
00040
00041 #include "previewgeneratorqueue.h"
00042 #include "exitcodes.h"
00043 #include "mythcontext.h"
00044 #include "mythversion.h"
00045 #include "mythdb.h"
00046 #include "mainserver.h"
00047 #include "server.h"
00048 #include "mthread.h"
00049 #include "scheduler.h"
00050 #include "backendutil.h"
00051 #include "programinfo.h"
00052 #include "recordinginfo.h"
00053 #include "recordingrule.h"
00054 #include "scheduledrecording.h"
00055 #include "jobqueue.h"
00056 #include "autoexpire.h"
00057 #include "storagegroup.h"
00058 #include "compat.h"
00059 #include "ringbuffer.h"
00060 #include "remotefile.h"
00061 #include "mythsystemevent.h"
00062 #include "tv.h"
00063 #include "mythcorecontext.h"
00064 #include "mythcoreutil.h"
00065 #include "mythdirs.h"
00066 #include "mythdownloadmanager.h"
00067 #include "metadatafactory.h"
00068 #include "videoutils.h"
00069 #include "mythlogging.h"
00070 #include "filesysteminfo.h"
00071
00075 #define PRT_TIMEOUT 10
00076
00077 #define PRT_STARTUP_THREAD_COUNT 5
00078
00079 #define LOC QString("MainServer: ")
00080 #define LOC_WARN QString("MainServer, Warning: ")
00081 #define LOC_ERR QString("MainServer, Error: ")
00082
00083 namespace {
00084
00085 int delete_file_immediately(const QString &filename,
00086 bool followLinks, bool checkexists)
00087 {
00088
00089 QFile checkFile(filename);
00090 int success1, success2;
00091
00092 LOG(VB_FILE, LOG_INFO, QString("About to delete file: %1").arg(filename));
00093 success1 = true;
00094 success2 = true;
00095 if (followLinks)
00096 {
00097 QFileInfo finfo(filename);
00098 if (finfo.isSymLink())
00099 {
00100 QString linktext = getSymlinkTarget(filename);
00101
00102 QFile target(linktext);
00103 if (!(success1 = target.remove()))
00104 {
00105 LOG(VB_GENERAL, LOG_ERR, QString("Error deleting '%1' -> '%2'")
00106 .arg(filename).arg(linktext) + ENO);
00107 }
00108 }
00109 }
00110 if ((!checkexists || checkFile.exists()) &&
00111 !(success2 = checkFile.remove()))
00112 {
00113 LOG(VB_GENERAL, LOG_ERR, QString("Error deleting '%1': %2")
00114 .arg(filename).arg(strerror(errno)));
00115 }
00116 return success1 && success2 ? 0 : -1;
00117 }
00118
00119 };
00120
00121 QMutex MainServer::truncate_and_close_lock;
00122 const uint MainServer::kMasterServerReconnectTimeout = 1000;
00123
00124 class ProcessRequestRunnable : public QRunnable
00125 {
00126 public:
00127 ProcessRequestRunnable(MainServer &parent, MythSocket *sock) :
00128 m_parent(parent), m_sock(sock)
00129 {
00130 m_sock->UpRef();
00131 }
00132
00133 virtual void run(void)
00134 {
00135 m_parent.ProcessRequest(m_sock);
00136 m_sock->DownRef();
00137 }
00138
00139 MainServer &m_parent;
00140 MythSocket *m_sock;
00141 };
00142
00143 class FreeSpaceUpdater : public QRunnable
00144 {
00145 public:
00146 FreeSpaceUpdater(MainServer &parent) :
00147 m_parent(parent), m_dorun(true), m_running(true)
00148 {
00149 m_lastRequest.start();
00150 }
00151 ~FreeSpaceUpdater()
00152 {
00153 QMutexLocker locker(&m_parent.masterFreeSpaceListLock);
00154 m_parent.masterFreeSpaceListUpdater = NULL;
00155 m_parent.masterFreeSpaceListWait.wakeAll();
00156 }
00157
00158 virtual void run(void)
00159 {
00160 while (true)
00161 {
00162 MythTimer t;
00163 t.start();
00164 QStringList list;
00165 m_parent.BackendQueryDiskSpace(list, true, true);
00166 {
00167 QMutexLocker locker(&m_parent.masterFreeSpaceListLock);
00168 m_parent.masterFreeSpaceList = list;
00169 }
00170 QMutexLocker locker(&m_lock);
00171 int left = kRequeryTimeout - t.elapsed();
00172 if (m_lastRequest.elapsed() + left > kExitTimeout)
00173 m_dorun = false;
00174 if (!m_dorun)
00175 {
00176 m_running = false;
00177 break;
00178 }
00179 if (left > 50)
00180 m_wait.wait(locker.mutex(), left);
00181 }
00182 }
00183
00184 bool KeepRunning(bool dorun)
00185 {
00186 QMutexLocker locker(&m_lock);
00187 if (dorun && m_running)
00188 {
00189 m_dorun = true;
00190 m_lastRequest.restart();
00191 }
00192 else
00193 {
00194 m_dorun = false;
00195 m_wait.wakeAll();
00196 }
00197 return m_running;
00198 }
00199
00200 MainServer &m_parent;
00201 QMutex m_lock;
00202 bool m_dorun;
00203 bool m_running;
00204 MythTimer m_lastRequest;
00205 QWaitCondition m_wait;
00206 const static int kRequeryTimeout;
00207 const static int kExitTimeout;
00208 };
00209 const int FreeSpaceUpdater::kRequeryTimeout = 15000;
00210 const int FreeSpaceUpdater::kExitTimeout = 61000;
00211
00212 MainServer::MainServer(bool master, int port,
00213 QMap<int, EncoderLink *> *tvList,
00214 Scheduler *sched, AutoExpire *expirer) :
00215 encoderList(tvList), mythserver(NULL),
00216 masterFreeSpaceListUpdater((master) ? new FreeSpaceUpdater(*this) : NULL),
00217 masterServerReconnect(NULL),
00218 masterServer(NULL), ismaster(master), threadPool("ProcessRequestPool"),
00219 masterBackendOverride(false),
00220 m_sched(sched), m_expirer(expirer), deferredDeleteTimer(NULL),
00221 autoexpireUpdateTimer(NULL), m_exitCode(GENERIC_EXIT_OK),
00222 m_stopped(false)
00223 {
00224 PreviewGeneratorQueue::CreatePreviewGeneratorQueue(
00225 PreviewGenerator::kLocalAndRemote, ~0, 0);
00226 PreviewGeneratorQueue::AddListener(this);
00227
00228 threadPool.setMaxThreadCount(PRT_STARTUP_THREAD_COUNT);
00229
00230 masterBackendOverride =
00231 gCoreContext->GetNumSetting("MasterBackendOverride", 0);
00232
00233 mythserver = new MythServer();
00234 mythserver->setProxy(QNetworkProxy::NoProxy);
00235 if (!mythserver->listen(port))
00236 {
00237 SetExitCode(GENERIC_EXIT_SOCKET_ERROR, false);
00238 return;
00239 }
00240 connect(mythserver, SIGNAL(newConnect(MythSocket *)),
00241 this, SLOT(newConnection(MythSocket *)));
00242
00243 gCoreContext->addListener(this);
00244
00245 if (!ismaster)
00246 {
00247 masterServerReconnect = new QTimer(this);
00248 masterServerReconnect->setSingleShot(true);
00249 connect(masterServerReconnect, SIGNAL(timeout()),
00250 this, SLOT(reconnectTimeout()));
00251 masterServerReconnect->start(kMasterServerReconnectTimeout);
00252 }
00253
00254 deferredDeleteTimer = new QTimer(this);
00255 connect(deferredDeleteTimer, SIGNAL(timeout()),
00256 this, SLOT(deferredDeleteSlot()));
00257 deferredDeleteTimer->start(30 * 1000);
00258
00259 if (sched)
00260 sched->SetMainServer(this);
00261 if (expirer)
00262 expirer->SetMainServer(this);
00263
00264 metadatafactory = new MetadataFactory(this);
00265
00266 autoexpireUpdateTimer = new QTimer(this);
00267 connect(autoexpireUpdateTimer, SIGNAL(timeout()),
00268 this, SLOT(autoexpireUpdate()));
00269 autoexpireUpdateTimer->setSingleShot(true);
00270
00271 AutoExpire::Update(true);
00272
00273 masterFreeSpaceList << gCoreContext->GetHostName();
00274 masterFreeSpaceList << "TotalDiskSpace";
00275 masterFreeSpaceList << "0";
00276 masterFreeSpaceList << "-2";
00277 masterFreeSpaceList << "-2";
00278 masterFreeSpaceList << "0";
00279 masterFreeSpaceList << "0";
00280 masterFreeSpaceList << "0";
00281 if (masterFreeSpaceListUpdater)
00282 {
00283 MThreadPool::globalInstance()->startReserved(
00284 masterFreeSpaceListUpdater, "FreeSpaceUpdater");
00285 }
00286 }
00287
00288 MainServer::~MainServer()
00289 {
00290 if (!m_stopped)
00291 Stop();
00292 }
00293
00294 void MainServer::Stop()
00295 {
00296 m_stopped = true;
00297
00298 gCoreContext->removeListener(this);
00299
00300 {
00301 QMutexLocker locker(&masterFreeSpaceListLock);
00302 if (masterFreeSpaceListUpdater)
00303 masterFreeSpaceListUpdater->KeepRunning(false);
00304 }
00305
00306 threadPool.Stop();
00307
00308
00309
00310
00311 if (m_sched)
00312 m_sched->Stop();
00313
00314 PreviewGeneratorQueue::RemoveListener(this);
00315 PreviewGeneratorQueue::TeardownPreviewGeneratorQueue();
00316
00317 if (mythserver)
00318 {
00319 mythserver->disconnect();
00320 mythserver->deleteLater();
00321 mythserver = NULL;
00322 }
00323
00324 if (m_sched)
00325 {
00326 m_sched->Wait();
00327 m_sched->SetMainServer(NULL);
00328 }
00329
00330 if (m_expirer)
00331 m_expirer->SetMainServer(NULL);
00332
00333 {
00334 QMutexLocker locker(&masterFreeSpaceListLock);
00335 while (masterFreeSpaceListUpdater)
00336 {
00337 masterFreeSpaceListUpdater->KeepRunning(false);
00338 masterFreeSpaceListWait.wait(locker.mutex());
00339 }
00340 }
00341 }
00342
00343 void MainServer::autoexpireUpdate(void)
00344 {
00345 AutoExpire::Update(false);
00346 }
00347
00348 void MainServer::newConnection(MythSocket *socket)
00349 {
00350 socket->setCallbacks(this);
00351 }
00352
00353 void MainServer::readyRead(MythSocket *sock)
00354 {
00355 sockListLock.lockForRead();
00356 PlaybackSock *testsock = GetPlaybackBySock(sock);
00357 bool expecting_reply = testsock && testsock->isExpectingReply();
00358 sockListLock.unlock();
00359 if (expecting_reply)
00360 return;
00361
00362 threadPool.startReserved(
00363 new ProcessRequestRunnable(*this, sock),
00364 "ProcessRequest", PRT_TIMEOUT);
00365 }
00366
00367 void MainServer::ProcessRequest(MythSocket *sock)
00368 {
00369 sock->Lock();
00370
00371 if (sock->bytesAvailable() > 0)
00372 {
00373 ProcessRequestWork(sock);
00374 }
00375
00376 sock->Unlock();
00377 }
00378
00379 void MainServer::ProcessRequestWork(MythSocket *sock)
00380 {
00381 QStringList listline;
00382 if (!sock->readStringList(listline))
00383 return;
00384
00385 QString line = listline[0];
00386
00387 line = line.simplified();
00388 QStringList tokens = line.split(' ', QString::SkipEmptyParts);
00389 QString command = tokens[0];
00390 #if 0
00391 LOG(VB_GENERAL, LOG_DEBUG, "command='" + command + "'");
00392 #endif
00393 if (command == "MYTH_PROTO_VERSION")
00394 {
00395 if (tokens.size() < 2)
00396 LOG(VB_GENERAL, LOG_CRIT, "Bad MYTH_PROTO_VERSION command");
00397 else
00398 HandleVersion(sock, tokens);
00399 return;
00400 }
00401 else if (command == "ANN")
00402 {
00403 HandleAnnounce(listline, tokens, sock);
00404 return;
00405 }
00406 else if (command == "DONE")
00407 {
00408 HandleDone(sock);
00409 return;
00410 }
00411
00412 sockListLock.lockForRead();
00413 PlaybackSock *pbs = GetPlaybackBySock(sock);
00414 if (!pbs)
00415 {
00416 sockListLock.unlock();
00417 LOG(VB_GENERAL, LOG_ERR, "ProcessRequest unknown socket");
00418 return;
00419 }
00420 pbs->UpRef();
00421 sockListLock.unlock();
00422
00423 if (command == "QUERY_RECORDINGS")
00424 {
00425 if (tokens.size() != 2)
00426 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_RECORDINGS query");
00427 else
00428 HandleQueryRecordings(tokens[1], pbs);
00429 }
00430 else if (command == "QUERY_RECORDING")
00431 {
00432 HandleQueryRecording(tokens, pbs);
00433 }
00434 else if (command == "GO_TO_SLEEP")
00435 {
00436 HandleGoToSleep(pbs);
00437 }
00438 else if (command == "QUERY_FREE_SPACE")
00439 {
00440 HandleQueryFreeSpace(pbs, false);
00441 }
00442 else if (command == "QUERY_FREE_SPACE_LIST")
00443 {
00444 HandleQueryFreeSpace(pbs, true);
00445 }
00446 else if (command == "QUERY_FREE_SPACE_SUMMARY")
00447 {
00448 HandleQueryFreeSpaceSummary(pbs);
00449 }
00450 else if (command == "QUERY_LOAD")
00451 {
00452 HandleQueryLoad(pbs);
00453 }
00454 else if (command == "QUERY_UPTIME")
00455 {
00456 HandleQueryUptime(pbs);
00457 }
00458 else if (command == "QUERY_HOSTNAME")
00459 {
00460 HandleQueryHostname(pbs);
00461 }
00462 else if (command == "QUERY_MEMSTATS")
00463 {
00464 HandleQueryMemStats(pbs);
00465 }
00466 else if (command == "QUERY_TIME_ZONE")
00467 {
00468 HandleQueryTimeZone(pbs);
00469 }
00470 else if (command == "QUERY_CHECKFILE")
00471 {
00472 HandleQueryCheckFile(listline, pbs);
00473 }
00474 else if (command == "QUERY_FILE_EXISTS")
00475 {
00476 if (listline.size() < 2)
00477 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_FILE_EXISTS command");
00478 else
00479 HandleQueryFileExists(listline, pbs);
00480 }
00481 else if (command == "QUERY_FILE_HASH")
00482 {
00483 if (listline.size() < 3)
00484 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_FILE_HASH command");
00485 else
00486 HandleQueryFileHash(listline, pbs);
00487 }
00488 else if (command == "QUERY_GUIDEDATATHROUGH")
00489 {
00490 HandleQueryGuideDataThrough(pbs);
00491 }
00492 else if (command == "DELETE_FILE")
00493 {
00494 if (listline.size() < 3)
00495 LOG(VB_GENERAL, LOG_ERR, "Bad DELETE_FILE command");
00496 else
00497 HandleDeleteFile(listline, pbs);
00498 }
00499 else if (command == "STOP_RECORDING")
00500 {
00501 HandleStopRecording(listline, pbs);
00502 }
00503 else if (command == "CHECK_RECORDING")
00504 {
00505 HandleCheckRecordingActive(listline, pbs);
00506 }
00507 else if (command == "DELETE_RECORDING")
00508 {
00509 if (3 <= tokens.size() && tokens.size() <= 5)
00510 {
00511 bool force = (tokens.size() >= 4) && (tokens[3] == "FORCE");
00512 bool forget = (tokens.size() >= 5) && (tokens[4] == "FORGET");
00513 HandleDeleteRecording(tokens[1], tokens[2], pbs, force, forget);
00514 }
00515 else
00516 HandleDeleteRecording(listline, pbs, false);
00517 }
00518 else if (command == "FORCE_DELETE_RECORDING")
00519 {
00520 HandleDeleteRecording(listline, pbs, true);
00521 }
00522 else if (command == "UNDELETE_RECORDING")
00523 {
00524 HandleUndeleteRecording(listline, pbs);
00525 }
00526 else if (command == "RESCHEDULE_RECORDINGS")
00527 {
00528 listline.pop_front();
00529 HandleRescheduleRecordings(listline, pbs);
00530 }
00531 else if (command == "FORGET_RECORDING")
00532 {
00533 HandleForgetRecording(listline, pbs);
00534 }
00535 else if (command == "QUERY_GETALLPENDING")
00536 {
00537 if (tokens.size() == 1)
00538 HandleGetPendingRecordings(pbs);
00539 else if (tokens.size() == 2)
00540 HandleGetPendingRecordings(pbs, tokens[1]);
00541 else
00542 HandleGetPendingRecordings(pbs, tokens[1], tokens[2].toInt());
00543 }
00544 else if (command == "QUERY_GETALLSCHEDULED")
00545 {
00546 HandleGetScheduledRecordings(pbs);
00547 }
00548 else if (command == "QUERY_GETCONFLICTING")
00549 {
00550 HandleGetConflictingRecordings(listline, pbs);
00551 }
00552 else if (command == "QUERY_GETEXPIRING")
00553 {
00554 HandleGetExpiringRecordings(pbs);
00555 }
00556 else if (command == "QUERY_SG_GETFILELIST")
00557 {
00558 HandleSGGetFileList(listline, pbs);
00559 }
00560 else if (command == "QUERY_SG_FILEQUERY")
00561 {
00562 HandleSGFileQuery(listline, pbs);
00563 }
00564 else if (command == "GET_FREE_RECORDER")
00565 {
00566 HandleGetFreeRecorder(pbs);
00567 }
00568 else if (command == "GET_FREE_RECORDER_COUNT")
00569 {
00570 HandleGetFreeRecorderCount(pbs);
00571 }
00572 else if (command == "GET_FREE_RECORDER_LIST")
00573 {
00574 HandleGetFreeRecorderList(pbs);
00575 }
00576 else if (command == "GET_NEXT_FREE_RECORDER")
00577 {
00578 HandleGetNextFreeRecorder(listline, pbs);
00579 }
00580 else if (command == "QUERY_RECORDER")
00581 {
00582 if (tokens.size() != 2)
00583 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_RECORDER");
00584 else
00585 HandleRecorderQuery(listline, tokens, pbs);
00586 }
00587 else if (command == "QUERY_RECORDING_DEVICE")
00588 {
00589
00590 }
00591 else if (command == "QUERY_RECORDING_DEVICES")
00592 {
00593
00594 }
00595 else if (command == "SET_NEXT_LIVETV_DIR")
00596 {
00597 if (tokens.size() != 3)
00598 LOG(VB_GENERAL, LOG_ERR, "Bad SET_NEXT_LIVETV_DIR");
00599 else
00600 HandleSetNextLiveTVDir(tokens, pbs);
00601 }
00602 else if (command == "SET_CHANNEL_INFO")
00603 {
00604 HandleSetChannelInfo(listline, pbs);
00605 }
00606 else if (command == "QUERY_REMOTEENCODER")
00607 {
00608 if (tokens.size() != 2)
00609 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_REMOTEENCODER");
00610 else
00611 HandleRemoteEncoder(listline, tokens, pbs);
00612 }
00613 else if (command == "GET_RECORDER_FROM_NUM")
00614 {
00615 HandleGetRecorderFromNum(listline, pbs);
00616 }
00617 else if (command == "GET_RECORDER_NUM")
00618 {
00619 HandleGetRecorderNum(listline, pbs);
00620 }
00621 else if (command == "QUERY_FILETRANSFER")
00622 {
00623 if (tokens.size() != 2)
00624 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_FILETRANSFER");
00625 else
00626 HandleFileTransferQuery(listline, tokens, pbs);
00627 }
00628 else if (command == "QUERY_GENPIXMAP2")
00629 {
00630 HandleGenPreviewPixmap(listline, pbs);
00631 }
00632 else if (command == "QUERY_PIXMAP_LASTMODIFIED")
00633 {
00634 HandlePixmapLastModified(listline, pbs);
00635 }
00636 else if (command == "QUERY_PIXMAP_GET_IF_MODIFIED")
00637 {
00638 HandlePixmapGetIfModified(listline, pbs);
00639 }
00640 else if (command == "QUERY_ISRECORDING")
00641 {
00642 HandleIsRecording(listline, pbs);
00643 }
00644 else if (command == "MESSAGE")
00645 {
00646 if ((listline.size() >= 2) && (listline[1].left(11) == "SET_VERBOSE"))
00647 HandleSetVerbose(listline, pbs);
00648 else if ((listline.size() >= 2) &&
00649 (listline[1].left(13) == "SET_LOG_LEVEL"))
00650 HandleSetLogLevel(listline, pbs);
00651 else
00652 HandleMessage(listline, pbs);
00653 }
00654 else if (command == "FILL_PROGRAM_INFO")
00655 {
00656 HandleFillProgramInfo(listline, pbs);
00657 }
00658 else if (command == "LOCK_TUNER")
00659 {
00660 if (tokens.size() == 1)
00661 HandleLockTuner(pbs);
00662 else if (tokens.size() == 2)
00663 HandleLockTuner(pbs, tokens[1].toInt());
00664 else
00665 LOG(VB_GENERAL, LOG_ERR, "Bad LOCK_TUNER query");
00666 }
00667 else if (command == "FREE_TUNER")
00668 {
00669 if (tokens.size() != 2)
00670 LOG(VB_GENERAL, LOG_ERR, "Bad FREE_TUNER query");
00671 else
00672 HandleFreeTuner(tokens[1].toInt(), pbs);
00673 }
00674 else if (command == "QUERY_ACTIVE_BACKENDS")
00675 {
00676 HandleActiveBackendsQuery(pbs);
00677 }
00678 else if (command == "QUERY_IS_ACTIVE_BACKEND")
00679 {
00680 if (tokens.size() != 1)
00681 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_IS_ACTIVE_BACKEND");
00682 else
00683 HandleIsActiveBackendQuery(listline, pbs);
00684 }
00685 else if (command == "QUERY_COMMBREAK")
00686 {
00687 if (tokens.size() != 3)
00688 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_COMMBREAK");
00689 else
00690 HandleCommBreakQuery(tokens[1], tokens[2], pbs);
00691 }
00692 else if (command == "QUERY_CUTLIST")
00693 {
00694 if (tokens.size() != 3)
00695 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_CUTLIST");
00696 else
00697 HandleCutlistQuery(tokens[1], tokens[2], pbs);
00698 }
00699 else if (command == "QUERY_BOOKMARK")
00700 {
00701 if (tokens.size() != 3)
00702 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_BOOKMARK");
00703 else
00704 HandleBookmarkQuery(tokens[1], tokens[2], pbs);
00705 }
00706 else if (command == "SET_BOOKMARK")
00707 {
00708 if (tokens.size() != 5)
00709 LOG(VB_GENERAL, LOG_ERR, "Bad SET_BOOKMARK");
00710 else
00711 HandleSetBookmark(tokens, pbs);
00712 }
00713 else if (command == "QUERY_SETTING")
00714 {
00715 if (tokens.size() != 3)
00716 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_SETTING");
00717 else
00718 HandleSettingQuery(tokens, pbs);
00719 }
00720 else if (command == "SET_SETTING")
00721 {
00722 if (tokens.size() != 4)
00723 LOG(VB_GENERAL, LOG_ERR, "Bad SET_SETTING");
00724 else
00725 HandleSetSetting(tokens, pbs);
00726 }
00727 else if (command == "SCAN_VIDEOS")
00728 {
00729 HandleScanVideos(pbs);
00730 }
00731 else if (command == "ALLOW_SHUTDOWN")
00732 {
00733 if (tokens.size() != 1)
00734 LOG(VB_GENERAL, LOG_ERR, "Bad ALLOW_SHUTDOWN");
00735 else
00736 HandleBlockShutdown(false, pbs);
00737 }
00738 else if (command == "BLOCK_SHUTDOWN")
00739 {
00740 if (tokens.size() != 1)
00741 LOG(VB_GENERAL, LOG_ERR, "Bad BLOCK_SHUTDOWN");
00742 else
00743 HandleBlockShutdown(true, pbs);
00744 }
00745 else if (command == "SHUTDOWN_NOW")
00746 {
00747 if (tokens.size() != 1)
00748 LOG(VB_GENERAL, LOG_ERR, "Bad SHUTDOWN_NOW query");
00749 else if (!ismaster)
00750 {
00751 QString halt_cmd;
00752 if (listline.size() >= 2)
00753 halt_cmd = listline[1];
00754
00755 if (!halt_cmd.isEmpty())
00756 {
00757 LOG(VB_GENERAL, LOG_NOTICE,
00758 "Going down now as of Mainserver request!");
00759 myth_system(halt_cmd);
00760 }
00761 else
00762 LOG(VB_GENERAL, LOG_WARNING,
00763 "Received an empty SHUTDOWN_NOW query!");
00764 }
00765 }
00766 else if (command == "BACKEND_MESSAGE")
00767 {
00768 QString message = listline[1];
00769 QStringList extra( listline[2] );
00770 for (int i = 3; i < listline.size(); i++)
00771 extra << listline[i];
00772 MythEvent me(message, extra);
00773 gCoreContext->dispatch(me);
00774 }
00775 else if ((command == "DOWNLOAD_FILE") ||
00776 (command == "DOWNLOAD_FILE_NOW"))
00777 {
00778 if (listline.size() != 4)
00779 LOG(VB_GENERAL, LOG_ERR, QString("Bad %1 command").arg(command));
00780 else
00781 HandleDownloadFile(listline, pbs);
00782 }
00783 else if (command == "REFRESH_BACKEND")
00784 {
00785 LOG(VB_GENERAL, LOG_INFO ,"Reloading backend settings");
00786 HandleBackendRefresh(sock);
00787 }
00788 else if (command == "OK")
00789 {
00790 LOG(VB_GENERAL, LOG_ERR, "Got 'OK' out of sequence.");
00791 }
00792 else if (command == "UNKNOWN_COMMAND")
00793 {
00794 LOG(VB_GENERAL, LOG_ERR, "Got 'UNKNOWN_COMMAND' out of sequence.");
00795 }
00796 else
00797 {
00798 LOG(VB_GENERAL, LOG_ERR, "Unknown command: " + command);
00799
00800 MythSocket *pbssock = pbs->getSocket();
00801
00802 QStringList strlist;
00803 strlist << "UNKNOWN_COMMAND";
00804
00805 SendResponse(pbssock, strlist);
00806 }
00807
00808
00809 pbs->DownRef();
00810 }
00811
00812 void MainServer::customEvent(QEvent *e)
00813 {
00814 QStringList broadcast;
00815 QSet<QString> receivers;
00816
00817 if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
00818 {
00819 MythEvent *me = (MythEvent *)e;
00820
00821 QString message = me->Message();
00822 QString error;
00823 if ((message == "PREVIEW_SUCCESS" || message == "PREVIEW_QUEUED") &&
00824 me->ExtraDataCount() >= 5)
00825 {
00826 bool ok = true;
00827 QString pginfokey = me->ExtraData(0);
00828 QString filename = me->ExtraData(1);
00829 QString msg = me->ExtraData(2);
00830 QString datetime = me->ExtraData(3);
00831
00832 if (message == "PREVIEW_QUEUED")
00833 {
00834 LOG(VB_PLAYBACK, LOG_INFO, QString("Preview Queued: '%1' '%2'")
00835 .arg(pginfokey).arg(filename));
00836 return;
00837 }
00838
00839 QFile file(filename);
00840 ok = ok && file.open(QIODevice::ReadOnly);
00841
00842 if (ok)
00843 {
00844 QByteArray data = file.readAll();
00845 QStringList extra("OK");
00846 extra.push_back(pginfokey);
00847 extra.push_back(msg);
00848 extra.push_back(datetime);
00849 extra.push_back(QString::number(data.size()));
00850 extra.push_back(
00851 QString::number(qChecksum(data.constData(), data.size())));
00852 extra.push_back(QString(data.toBase64()));
00853
00854 for (uint i = 4 ; i < (uint) me->ExtraDataCount(); i++)
00855 {
00856 QString token = me->ExtraData(i);
00857 extra.push_back(token);
00858 RequestedBy::iterator it = m_previewRequestedBy.find(token);
00859 if (it != m_previewRequestedBy.end())
00860 {
00861 receivers.insert(*it);
00862 m_previewRequestedBy.erase(it);
00863 }
00864 }
00865
00866 if (receivers.empty())
00867 {
00868 LOG(VB_GENERAL, LOG_ERR, LOC +
00869 "PREVIEW_SUCCESS but no receivers.");
00870 return;
00871 }
00872
00873 broadcast.push_back("BACKEND_MESSAGE");
00874 broadcast.push_back("GENERATED_PIXMAP");
00875 broadcast += extra;
00876 }
00877 else
00878 {
00879 message = "PREVIEW_FAILED";
00880 error = QString("Failed to read '%1'").arg(filename);
00881 LOG(VB_GENERAL, LOG_ERR, LOC + error);
00882 }
00883 }
00884
00885 if (message == "PREVIEW_FAILED" && me->ExtraDataCount() >= 5)
00886 {
00887 QString pginfokey = me->ExtraData(0);
00888 QString msg = me->ExtraData(2);
00889
00890 QStringList extra("ERROR");
00891 extra.push_back(pginfokey);
00892 extra.push_back(msg);
00893 for (uint i = 4 ; i < (uint) me->ExtraDataCount(); i++)
00894 {
00895 QString token = me->ExtraData(i);
00896 extra.push_back(token);
00897 RequestedBy::iterator it = m_previewRequestedBy.find(token);
00898 if (it != m_previewRequestedBy.end())
00899 {
00900 receivers.insert(*it);
00901 m_previewRequestedBy.erase(it);
00902 }
00903 }
00904
00905 if (receivers.empty())
00906 {
00907 LOG(VB_GENERAL, LOG_ERR, LOC +
00908 "PREVIEW_FAILED but no receivers.");
00909 return;
00910 }
00911
00912 broadcast.push_back("BACKEND_MESSAGE");
00913 broadcast.push_back("GENERATED_PIXMAP");
00914 broadcast += extra;
00915 }
00916
00917 if (me->Message().left(11) == "AUTO_EXPIRE")
00918 {
00919 QStringList tokens = me->Message()
00920 .split(" ", QString::SkipEmptyParts);
00921
00922 if (tokens.size() != 3)
00923 {
00924 LOG(VB_GENERAL, LOG_ERR, "Bad AUTO_EXPIRE message");
00925 return;
00926 }
00927
00928 QDateTime startts = QDateTime::fromString(tokens[2], Qt::ISODate);
00929 RecordingInfo recInfo(tokens[1].toUInt(), startts);
00930
00931 if (recInfo.GetChanID())
00932 {
00933 SendMythSystemPlayEvent("REC_EXPIRED", &recInfo);
00934
00935
00936
00937 if (recInfo.GetRecordingGroup() != "LiveTV" &&
00938 recInfo.GetRecordingGroup() != "Deleted" &&
00939 (gCoreContext->GetNumSetting("RerecordWatched", 0) ||
00940 !recInfo.IsWatched()))
00941 {
00942 recInfo.ForgetHistory();
00943 }
00944 DoHandleDeleteRecording(recInfo, NULL, false, true, false);
00945 }
00946 else
00947 {
00948 QString msg = QString("Cannot find program info for '%1', "
00949 "while attempting to Auto-Expire.")
00950 .arg(me->Message());
00951 LOG(VB_GENERAL, LOG_ERR, LOC + msg);
00952 }
00953
00954 return;
00955 }
00956
00957 if (me->Message().left(21) == "QUERY_NEXT_LIVETV_DIR" && m_sched)
00958 {
00959 QStringList tokens = me->Message()
00960 .split(" ", QString::SkipEmptyParts);
00961
00962 if (tokens.size() != 2)
00963 {
00964 LOG(VB_GENERAL, LOG_ERR,
00965 QString("Bad %1 message").arg(tokens[0]));
00966 return;
00967 }
00968
00969 m_sched->GetNextLiveTVDir(tokens[1].toInt());
00970 return;
00971 }
00972
00973 if ((me->Message().left(16) == "DELETE_RECORDING") ||
00974 (me->Message().left(22) == "FORCE_DELETE_RECORDING"))
00975 {
00976 QStringList tokens = me->Message()
00977 .split(" ", QString::SkipEmptyParts);
00978
00979 if (tokens.size() != 3)
00980 {
00981 LOG(VB_GENERAL, LOG_ERR,
00982 QString("Bad %1 message").arg(tokens[0]));
00983 return;
00984 }
00985
00986 QDateTime startts = QDateTime::fromString(tokens[2], Qt::ISODate);
00987 RecordingInfo recInfo(tokens[1].toUInt(), startts);
00988
00989 if (recInfo.GetChanID())
00990 {
00991 if (tokens[0] == "FORCE_DELETE_RECORDING")
00992 DoHandleDeleteRecording(recInfo, NULL, true, false, false);
00993 else
00994 DoHandleDeleteRecording(recInfo, NULL, false, false, false);
00995 }
00996 else
00997 {
00998 LOG(VB_GENERAL, LOG_ERR,
00999 QString("Cannot find program info for '%1' while "
01000 "attempting to delete.").arg(me->Message()));
01001 }
01002
01003 return;
01004 }
01005
01006 if (me->Message().left(21) == "RESCHEDULE_RECORDINGS" && m_sched)
01007 {
01008 QStringList request = me->ExtraDataList();
01009 m_sched->Reschedule(request);
01010 return;
01011 }
01012
01013 if (me->Message().left(23) == "SCHEDULER_ADD_RECORDING" && m_sched)
01014 {
01015 ProgramInfo pi(me->ExtraDataList());
01016 if (!pi.GetChanID())
01017 {
01018 LOG(VB_GENERAL, LOG_ERR, "Bad SCHEDULER_ADD_RECORDING message");
01019 return;
01020 }
01021
01022 m_sched->AddRecording(pi);
01023 return;
01024 }
01025
01026 if (me->Message().left(23) == "UPDATE_RECORDING_STATUS" && m_sched)
01027 {
01028 QStringList tokens = me->Message()
01029 .split(" ", QString::SkipEmptyParts);
01030
01031 if (tokens.size() != 6)
01032 {
01033 LOG(VB_GENERAL, LOG_ERR, "Bad UPDATE_RECORDING_STATUS message");
01034 return;
01035 }
01036
01037 uint cardid = tokens[1].toUInt();
01038 uint chanid = tokens[2].toUInt();
01039 QDateTime startts = QDateTime::fromString(tokens[3], Qt::ISODate);
01040 RecStatusType recstatus = RecStatusType(tokens[4].toInt());
01041 QDateTime recendts = QDateTime::fromString(tokens[5], Qt::ISODate);
01042 m_sched->UpdateRecStatus(cardid, chanid, startts,
01043 recstatus, recendts);
01044 return;
01045 }
01046
01047 if (me->Message().left(13) == "LIVETV_EXITED")
01048 {
01049 QString chainid = me->ExtraData();
01050 LiveTVChain *chain = GetExistingChain(chainid);
01051 if (chain)
01052 DeleteChain(chain);
01053
01054 return;
01055 }
01056
01057 if (me->Message() == "CLEAR_SETTINGS_CACHE")
01058 gCoreContext->ClearSettingsCache();
01059
01060 if (me->Message().left(14) == "RESET_IDLETIME" && m_sched)
01061 m_sched->ResetIdleTime();
01062
01063 if (me->Message() == "LOCAL_RECONNECT_TO_MASTER")
01064 masterServerReconnect->start(kMasterServerReconnectTimeout);
01065
01066 if (me->Message() == "LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE")
01067 HandleSlaveDisconnectedEvent(*me);
01068
01069 if (me->Message().left(6) == "LOCAL_")
01070 return;
01071
01072 MythEvent mod_me("");
01073 if (me->Message().left(23) == "MASTER_UPDATE_PROG_INFO")
01074 {
01075 QStringList tokens = me->Message().simplified().split(" ");
01076 uint chanid = 0;
01077 QDateTime recstartts;
01078 if (tokens.size() >= 3)
01079 {
01080 chanid = tokens[1].toUInt();
01081 recstartts = QDateTime::fromString(tokens[2], Qt::ISODate);
01082 }
01083
01084 ProgramInfo evinfo(chanid, recstartts);
01085 if (evinfo.GetChanID())
01086 {
01087 QDateTime rectime = QDateTime::currentDateTime().addSecs(
01088 -gCoreContext->GetNumSetting("RecordOverTime"));
01089
01090 if (m_sched && evinfo.GetRecordingEndTime() > rectime)
01091 evinfo.SetRecordingStatus(m_sched->GetRecStatus(evinfo));
01092
01093 QStringList list;
01094 evinfo.ToStringList(list);
01095 mod_me = MythEvent("RECORDING_LIST_CHANGE UPDATE", list);
01096 me = &mod_me;
01097 }
01098 else
01099 {
01100 return;
01101 }
01102 }
01103
01104 if (me->Message().left(13) == "DOWNLOAD_FILE")
01105 {
01106 QStringList extraDataList = me->ExtraDataList();
01107 QString localFile = extraDataList[1];
01108 QFile file(localFile);
01109 QStringList tokens = me->Message().simplified().split(" ");
01110 QMutexLocker locker(&m_downloadURLsLock);
01111
01112 if (!m_downloadURLs.contains(localFile))
01113 return;
01114
01115 extraDataList[1] = m_downloadURLs[localFile];
01116
01117 if ((tokens.size() >= 2) && (tokens[1] == "FINISHED"))
01118 m_downloadURLs.remove(localFile);
01119
01120 mod_me = MythEvent(me->Message(), extraDataList);
01121 me = &mod_me;
01122 }
01123
01124 if (broadcast.empty())
01125 {
01126 broadcast.push_back("BACKEND_MESSAGE");
01127 broadcast.push_back(me->Message());
01128 broadcast += me->ExtraDataList();
01129 }
01130 }
01131
01132 if (!broadcast.empty())
01133 {
01134
01135 vector<PlaybackSock *> localPBSList;
01136 sockListLock.lockForRead();
01137 vector<PlaybackSock *>::iterator it = playbackList.begin();
01138 for (; it != playbackList.end(); ++it)
01139 {
01140 (*it)->UpRef();
01141 localPBSList.push_back(*it);
01142 }
01143 sockListLock.unlock();
01144
01145 bool sendGlobal = false;
01146 if (ismaster && broadcast[1].left(7) == "GLOBAL_")
01147 {
01148 broadcast[1].replace("GLOBAL_", "LOCAL_");
01149 MythEvent me(broadcast[1], broadcast[2]);
01150 gCoreContext->dispatch(me);
01151
01152 sendGlobal = true;
01153 }
01154
01155 QSet<PlaybackSock*> sentSet;
01156
01157 bool isSystemEvent = broadcast[1].startsWith("SYSTEM_EVENT ");
01158 QStringList sentSetSystemEvent(gCoreContext->GetHostName());
01159
01160 vector<PlaybackSock*>::const_iterator iter;
01161 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
01162 {
01163 PlaybackSock *pbs = *iter;
01164
01165 if (sentSet.contains(pbs) || pbs->IsDisconnected())
01166 continue;
01167
01168 if (!receivers.empty() && !receivers.contains(pbs->getHostname()))
01169 continue;
01170
01171 sentSet.insert(pbs);
01172
01173 bool reallysendit = false;
01174
01175 if (broadcast[1] == "CLEAR_SETTINGS_CACHE")
01176 {
01177 if ((ismaster) &&
01178 (pbs->isSlaveBackend() || pbs->wantsEvents()))
01179 reallysendit = true;
01180 }
01181 else if (sendGlobal)
01182 {
01183 if (pbs->isSlaveBackend())
01184 reallysendit = true;
01185 }
01186 else if (pbs->wantsEvents())
01187 {
01188 reallysendit = true;
01189 }
01190
01191 if (reallysendit)
01192 {
01193 if (isSystemEvent)
01194 {
01195 if (!pbs->wantsSystemEvents())
01196 {
01197 continue;
01198 }
01199 else if (!pbs->wantsOnlySystemEvents())
01200 {
01201 if (sentSetSystemEvent.contains(pbs->getHostname()))
01202 continue;
01203
01204 sentSetSystemEvent << pbs->getHostname();
01205 }
01206 }
01207 else if (pbs->wantsOnlySystemEvents())
01208 continue;
01209 }
01210
01211 MythSocket *sock = pbs->getSocket();
01212 if (reallysendit && sock->socket() >= 0)
01213 {
01214 sock->Lock();
01215
01216 if (sock->socket() >= 0)
01217 sock->writeStringList(broadcast);
01218
01219 sock->Unlock();
01220 }
01221 }
01222
01223
01224 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
01225 {
01226 PlaybackSock *pbs = *iter;
01227 pbs->DownRef();
01228 }
01229 }
01230 }
01231
01240 void MainServer::HandleVersion(MythSocket *socket, const QStringList &slist)
01241 {
01242 QStringList retlist;
01243 QString version = slist[1];
01244 if (version != MYTH_PROTO_VERSION)
01245 {
01246 LOG(VB_GENERAL, LOG_CRIT,
01247 "MainServer::HandleVersion - Client speaks protocol version " +
01248 version + " but we speak " + MYTH_PROTO_VERSION + '!');
01249 retlist << "REJECT" << MYTH_PROTO_VERSION;
01250 socket->writeStringList(retlist);
01251 HandleDone(socket);
01252 return;
01253 }
01254
01255 if (slist.size() < 3)
01256 {
01257 LOG(VB_GENERAL, LOG_CRIT,
01258 "MainServer::HandleVersion - Client did not pass protocol "
01259 "token. Refusing connection!");
01260 retlist << "REJECT" << MYTH_PROTO_VERSION;
01261 socket->writeStringList(retlist);
01262 HandleDone(socket);
01263 return;
01264 }
01265
01266 QString token = slist[2];
01267 if (token != MYTH_PROTO_TOKEN)
01268 {
01269 LOG(VB_GENERAL, LOG_CRIT,
01270 "MainServer::HandleVersion - Client sent incorrect protocol"
01271 " token for protocol version. Refusing connection!");
01272 retlist << "REJECT" << MYTH_PROTO_VERSION;
01273 socket->writeStringList(retlist);
01274 HandleDone(socket);
01275 return;
01276 }
01277
01278 retlist << "ACCEPT" << MYTH_PROTO_VERSION;
01279 socket->writeStringList(retlist);
01280 }
01281
01294 void MainServer::HandleAnnounce(QStringList &slist, QStringList commands,
01295 MythSocket *socket)
01296 {
01297 QStringList retlist( "OK" );
01298 QStringList errlist( "ERROR" );
01299
01300 if (commands.size() < 3 || commands.size() > 6)
01301 {
01302 QString info = "";
01303 if (commands.size() == 2)
01304 info = QString(" %1").arg(commands[1]);
01305
01306 LOG(VB_GENERAL, LOG_ERR, QString("Received malformed ANN%1 query")
01307 .arg(info));
01308
01309 errlist << "malformed_ann_query";
01310 socket->writeStringList(errlist);
01311 return;
01312 }
01313
01314 sockListLock.lockForRead();
01315 vector<PlaybackSock *>::iterator iter = playbackList.begin();
01316 for (; iter != playbackList.end(); ++iter)
01317 {
01318 PlaybackSock *pbs = *iter;
01319 if (pbs->getSocket() == socket)
01320 {
01321 LOG(VB_GENERAL, LOG_WARNING,
01322 QString("Client %1 is trying to announce a socket "
01323 "multiple times.")
01324 .arg(commands[2]));
01325 socket->writeStringList(retlist);
01326 sockListLock.unlock();
01327 return;
01328 }
01329 }
01330 sockListLock.unlock();
01331
01332 if (commands[1] == "Playback" || commands[1] == "Monitor")
01333 {
01334 if (commands.size() < 4)
01335 {
01336 LOG(VB_GENERAL, LOG_ERR, QString("Received malformed ANN %1 query")
01337 .arg(commands[1]));
01338
01339 errlist << "malformed_ann_query";
01340 socket->writeStringList(errlist);
01341 return;
01342 }
01343
01344
01345
01346 PlaybackSockEventsMode eventsMode =
01347 (PlaybackSockEventsMode)commands[3].toInt();
01348 LOG(VB_GENERAL, LOG_INFO, QString("MainServer::ANN %1")
01349 .arg(commands[1]));
01350 LOG(VB_GENERAL, LOG_INFO, QString("adding: %1 as a client (events: %2)")
01351 .arg(commands[2]).arg(eventsMode));
01352 PlaybackSock *pbs = new PlaybackSock(this, socket, commands[2],
01353 eventsMode);
01354 pbs->setBlockShutdown(commands[1] == "Playback");
01355
01356 sockListLock.lockForWrite();
01357 playbackList.push_back(pbs);
01358 sockListLock.unlock();
01359
01360 if (eventsMode != kPBSEvents_None && commands[2] != "tzcheck")
01361 gCoreContext->SendSystemEvent(
01362 QString("CLIENT_CONNECTED HOSTNAME %1").arg(commands[2]));
01363 }
01364 else if (commands[1] == "MediaServer")
01365 {
01366 if (commands.size() < 3)
01367 {
01368 LOG(VB_GENERAL, LOG_ERR,
01369 "Received malformed ANN MediaServer query");
01370 errlist << "malformed_ann_query";
01371 socket->writeStringList(errlist);
01372 return;
01373 }
01374
01375 PlaybackSock *pbs = new PlaybackSock(this, socket, commands[2],
01376 kPBSEvents_Normal);
01377 pbs->setAsMediaServer();
01378 pbs->setBlockShutdown(false);
01379 sockListLock.lockForWrite();
01380 playbackList.push_back(pbs);
01381 sockListLock.unlock();
01382
01383 gCoreContext->SendSystemEvent(
01384 QString("CLIENT_CONNECTED HOSTNAME %1").arg(commands[2]));
01385 }
01386 else if (commands[1] == "SlaveBackend")
01387 {
01388 if (commands.size() < 4)
01389 {
01390 LOG(VB_GENERAL, LOG_ERR, QString("Received malformed ANN %1 query")
01391 .arg(commands[1]));
01392 errlist << "malformed_ann_query";
01393 socket->writeStringList(errlist);
01394 return;
01395 }
01396
01397 LOG(VB_GENERAL, LOG_INFO,
01398 QString("adding: %1 as a slave backend server")
01399 .arg(commands[2]));
01400 PlaybackSock *pbs = new PlaybackSock(this, socket, commands[2],
01401 kPBSEvents_None);
01402 pbs->setAsSlaveBackend();
01403 pbs->setIP(commands[3]);
01404
01405 if (m_sched)
01406 {
01407 RecordingList slavelist;
01408 QStringList::const_iterator sit = slist.begin()+1;
01409 while (sit != slist.end())
01410 {
01411 RecordingInfo *recinfo = new RecordingInfo(sit, slist.end());
01412 if (!recinfo->GetChanID())
01413 {
01414 delete recinfo;
01415 break;
01416 }
01417 slavelist.push_back(recinfo);
01418 }
01419 m_sched->SlaveConnected(slavelist);
01420 }
01421
01422 bool wasAsleep = true;
01423 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
01424 for (; iter != encoderList->end(); ++iter)
01425 {
01426 EncoderLink *elink = *iter;
01427 if (elink->GetHostName() == commands[2])
01428 {
01429 if (! (elink->IsWaking() || elink->IsAsleep()))
01430 wasAsleep = false;
01431 elink->SetSocket(pbs);
01432 }
01433 }
01434
01435 if (!wasAsleep && m_sched)
01436 m_sched->ReschedulePlace("SlaveConnected");
01437
01438 QString message = QString("LOCAL_SLAVE_BACKEND_ONLINE %2")
01439 .arg(commands[2]);
01440 MythEvent me(message);
01441 gCoreContext->dispatch(me);
01442
01443 pbs->setBlockShutdown(false);
01444
01445 sockListLock.lockForWrite();
01446 playbackList.push_back(pbs);
01447 sockListLock.unlock();
01448
01449 autoexpireUpdateTimer->start(1000);
01450
01451 gCoreContext->SendSystemEvent(
01452 QString("SLAVE_CONNECTED HOSTNAME %1").arg(commands[2]));
01453 }
01454 else if (commands[1] == "FileTransfer")
01455 {
01456 if (slist.size() < 3)
01457 {
01458 LOG(VB_GENERAL, LOG_ERR, "Received malformed FileTransfer command");
01459 errlist << "malformed_filetransfer_command";
01460 socket->writeStringList(errlist);
01461 return;
01462 }
01463
01464 LOG(VB_GENERAL, LOG_INFO, "MainServer::HandleAnnounce FileTransfer");
01465 LOG(VB_GENERAL, LOG_INFO,
01466 QString("adding: %1 as a remote file transfer") .arg(commands[2]));
01467 QStringList::const_iterator it = slist.begin();
01468 QUrl qurl = *(++it);
01469 QString wantgroup = *(++it);
01470 QString filename;
01471 QStringList checkfiles;
01472 for (++it; it != slist.end(); ++it)
01473 checkfiles += *it;
01474
01475 FileTransfer *ft = NULL;
01476 bool writemode = false;
01477 bool usereadahead = true;
01478 int timeout_ms = 2000;
01479 if (commands.size() > 3)
01480 writemode = commands[3].toInt();
01481
01482 if (commands.size() > 4)
01483 usereadahead = commands[4].toInt();
01484
01485 if (commands.size() > 5)
01486 timeout_ms = commands[5].toInt();
01487
01488 if (writemode)
01489 {
01490 if (wantgroup.isEmpty())
01491 wantgroup = "Default";
01492
01493 StorageGroup sgroup(wantgroup, gCoreContext->GetHostName(), false);
01494 QString dir = sgroup.FindNextDirMostFree();
01495 if (dir.isEmpty())
01496 {
01497 LOG(VB_GENERAL, LOG_ERR, "Unable to determine directory "
01498 "to write to in FileTransfer write command");
01499 errlist << "filetransfer_directory_not_found";
01500 socket->writeStringList(errlist);
01501 return;
01502 }
01503
01504 QString basename = qurl.path();
01505 if (qurl.hasFragment())
01506 basename += "#" + qurl.fragment();
01507
01508 if (basename.isEmpty())
01509 {
01510 LOG(VB_GENERAL, LOG_ERR,
01511 QString("FileTransfer write filename is empty in url '%1'.")
01512 .arg(qurl.toString()));
01513 errlist << "filetransfer_filename_empty";
01514 socket->writeStringList(errlist);
01515 return;
01516 }
01517
01518 if ((basename.contains("/../")) ||
01519 (basename.startsWith("../")))
01520 {
01521 LOG(VB_GENERAL, LOG_ERR,
01522 QString("FileTransfer write filename '%1' does not pass "
01523 "sanity checks.") .arg(basename));
01524 errlist << "filetransfer_filename_dangerous";
01525 socket->writeStringList(errlist);
01526 return;
01527 }
01528
01529 filename = dir + "/" + basename;
01530 }
01531 else
01532 filename = LocalFilePath(qurl, wantgroup);
01533
01534 if (filename.isEmpty())
01535 {
01536 LOG(VB_GENERAL, LOG_ERR, "Empty filename, cowardly aborting!");
01537 errlist << "filetransfer_filename_empty";
01538 socket->writeStringList(errlist);
01539 return;
01540 }
01541
01542
01543 QFileInfo finfo(filename);
01544 if (finfo.isDir())
01545 {
01546 LOG(VB_GENERAL, LOG_ERR,
01547 QString("FileTransfer filename '%1' is actually a directory, "
01548 "cannot transfer.") .arg(filename));
01549 errlist << "filetransfer_filename_is_a_directory";
01550 socket->writeStringList(errlist);
01551 return;
01552 }
01553
01554 if (writemode)
01555 {
01556 QString dirPath = finfo.absolutePath();
01557 QDir qdir(dirPath);
01558 if (!qdir.exists())
01559 {
01560 if (!qdir.mkpath(dirPath))
01561 {
01562 LOG(VB_GENERAL, LOG_ERR,
01563 QString("FileTransfer filename '%1' is in a "
01564 "subdirectory which does not exist, and can "
01565 "not be created.") .arg(filename));
01566 errlist << "filetransfer_unable_to_create_subdirectory";
01567 socket->writeStringList(errlist);
01568 return;
01569 }
01570 }
01571 ft = new FileTransfer(filename, socket, writemode);
01572 }
01573 else
01574 ft = new FileTransfer(filename, socket, usereadahead, timeout_ms);
01575
01576 sockListLock.lockForWrite();
01577 fileTransferList.push_back(ft);
01578 sockListLock.unlock();
01579
01580 retlist << QString::number(socket->socket());
01581 ft->UpRef();
01582 retlist << QString::number(ft->GetFileSize());
01583 ft->DownRef();
01584
01585 if (checkfiles.size())
01586 {
01587 QFileInfo fi(filename);
01588 QDir dir = fi.absoluteDir();
01589 for (it = checkfiles.begin(); it != checkfiles.end(); ++it)
01590 {
01591 if (dir.exists(*it) &&
01592 QFileInfo(dir, *it).size() >= kReadTestSize)
01593 {
01594 retlist<<*it;
01595 }
01596 }
01597 }
01598 }
01599
01600 socket->writeStringList(retlist);
01601 }
01602
01608 void MainServer::HandleDone(MythSocket *socket)
01609 {
01610 socket->close();
01611 }
01612
01613 void MainServer::SendResponse(MythSocket *socket, QStringList &commands)
01614 {
01615
01616
01617
01618
01619
01620
01621
01622 bool do_write = false;
01623 if (socket)
01624 {
01625 sockListLock.lockForRead();
01626 do_write = (GetPlaybackBySock(socket) ||
01627 GetFileTransferBySock(socket));
01628 sockListLock.unlock();
01629 }
01630
01631 if (do_write)
01632 {
01633 socket->writeStringList(commands);
01634 }
01635 else
01636 {
01637 LOG(VB_GENERAL, LOG_ERR,
01638 "SendResponse: Unable to write to client socket, as it's no "
01639 "longer there");
01640 }
01641 }
01642
01651 void MainServer::HandleQueryRecordings(QString type, PlaybackSock *pbs)
01652 {
01653 MythSocket *pbssock = pbs->getSocket();
01654 QString playbackhost = pbs->getHostname();
01655
01656 QMap<QString,ProgramInfo*> recMap;
01657 if (m_sched)
01658 recMap = m_sched->GetRecording();
01659
01660 QMap<QString,uint32_t> inUseMap = ProgramInfo::QueryInUseMap();
01661 QMap<QString,bool> isJobRunning =
01662 ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
01663
01664 int sort = 0;
01665
01666
01667 if ((type == "Ascending") || (type == "Play"))
01668 sort = 1;
01669 else if ((type == "Descending") || (type == "Delete"))
01670 sort = -1;
01671
01672 ProgramList destination;
01673 LoadFromRecorded(
01674 destination, (type == "Recording"),
01675 inUseMap, isJobRunning, recMap, sort);
01676
01677 QMap<QString,ProgramInfo*>::iterator mit = recMap.begin();
01678 for (; mit != recMap.end(); mit = recMap.erase(mit))
01679 delete *mit;
01680
01681 QStringList outputlist(QString::number(destination.size()));
01682 QMap<QString, QString> backendIpMap;
01683 QMap<QString, QString> backendPortMap;
01684 QString ip = gCoreContext->GetBackendServerIP();
01685 QString port = gCoreContext->GetSetting("BackendServerPort");
01686
01687 ProgramList::iterator it = destination.begin();
01688 for (it = destination.begin(); it != destination.end(); ++it)
01689 {
01690 ProgramInfo *proginfo = *it;
01691 PlaybackSock *slave = NULL;
01692
01693 if (proginfo->GetHostname() != gCoreContext->GetHostName())
01694 slave = GetSlaveByHostname(proginfo->GetHostname());
01695
01696 if ((proginfo->GetHostname() == gCoreContext->GetHostName()) ||
01697 (!slave && masterBackendOverride))
01698 {
01699 proginfo->SetPathname(gCoreContext->GenMythURL(ip,port,proginfo->GetBasename()));
01700 if (!proginfo->GetFilesize())
01701 {
01702 QString tmpURL = GetPlaybackURL(proginfo);
01703 if (tmpURL.startsWith('/'))
01704 {
01705 QFile checkFile(tmpURL);
01706 if (!tmpURL.isEmpty() && checkFile.exists())
01707 {
01708 proginfo->SetFilesize(checkFile.size());
01709 if (proginfo->GetRecordingEndTime() <
01710 QDateTime::currentDateTime())
01711 {
01712 proginfo->SaveFilesize(proginfo->GetFilesize());
01713 }
01714 }
01715 }
01716 }
01717 }
01718 else if (!slave)
01719 {
01720 proginfo->SetPathname(GetPlaybackURL(proginfo));
01721 if (proginfo->GetPathname().isEmpty())
01722 {
01723 LOG(VB_GENERAL, LOG_ERR, LOC +
01724 QString("HandleQueryRecordings() "
01725 "Couldn't find backend for:\n\t\t\t%1")
01726 .arg(proginfo->toString(ProgramInfo::kTitleSubtitle)));
01727
01728 proginfo->SetFilesize(0);
01729 proginfo->SetPathname("file not found");
01730 }
01731 }
01732 else
01733 {
01734 if (!proginfo->GetFilesize())
01735 {
01736 if (!slave->FillProgramInfo(*proginfo, playbackhost))
01737 {
01738 LOG(VB_GENERAL, LOG_ERR,
01739 "MainServer::HandleQueryRecordings()"
01740 "\n\t\t\tCould not fill program info "
01741 "from backend");
01742 }
01743 else
01744 {
01745 if (proginfo->GetRecordingEndTime() <
01746 QDateTime::currentDateTime())
01747 {
01748 proginfo->SaveFilesize(proginfo->GetFilesize());
01749 }
01750 }
01751 }
01752 else
01753 {
01754 ProgramInfo *p = proginfo;
01755 if (!backendIpMap.contains(p->GetHostname()))
01756 backendIpMap[p->GetHostname()] =
01757 gCoreContext->GetSettingOnHost("BackendServerIp",
01758 p->GetHostname());
01759 if (!backendPortMap.contains(p->GetHostname()))
01760 backendPortMap[p->GetHostname()] =
01761 gCoreContext->GetSettingOnHost("BackendServerPort",
01762 p->GetHostname());
01763 p->SetPathname(gCoreContext->GenMythURL(backendIpMap[p->GetHostname()],
01764 backendPortMap[p->GetHostname()],
01765 p->GetBasename()));
01766 }
01767 }
01768
01769 if (slave)
01770 slave->DownRef();
01771
01772 proginfo->ToStringList(outputlist);
01773 }
01774
01775 SendResponse(pbssock, outputlist);
01776 }
01777
01783 void MainServer::HandleQueryRecording(QStringList &slist, PlaybackSock *pbs)
01784 {
01785 if (slist.size() < 3)
01786 {
01787 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_RECORDING query");
01788 return;
01789 }
01790
01791 MythSocket *pbssock = pbs->getSocket();
01792 QString command = slist[1].toUpper();
01793 ProgramInfo *pginfo = NULL;
01794
01795 if (command == "BASENAME")
01796 {
01797 pginfo = new ProgramInfo(slist[2]);
01798 }
01799 else if (command == "TIMESLOT")
01800 {
01801 if (slist.size() < 4)
01802 {
01803 LOG(VB_GENERAL, LOG_ERR, "Bad QUERY_RECORDING query");
01804 return;
01805 }
01806
01807 QDateTime recstartts = myth_dt_from_string(slist[3]);
01808 pginfo = new ProgramInfo(slist[2].toUInt(), recstartts);
01809 }
01810
01811 QStringList strlist;
01812
01813 if (pginfo && pginfo->GetChanID())
01814 {
01815 strlist << "OK";
01816 pginfo->ToStringList(strlist);
01817 }
01818 else
01819 {
01820 strlist << "ERROR";
01821 }
01822
01823 delete pginfo;
01824
01825 SendResponse(pbssock, strlist);
01826 }
01827
01828 void MainServer::HandleFillProgramInfo(QStringList &slist, PlaybackSock *pbs)
01829 {
01830 MythSocket *pbssock = pbs->getSocket();
01831
01832 QString playbackhost = slist[1];
01833
01834 QStringList::const_iterator it = slist.begin() + 2;
01835 ProgramInfo pginfo(it, slist.end());
01836
01837 if (pginfo.HasPathname())
01838 {
01839 QString lpath = GetPlaybackURL(&pginfo);
01840 QString ip = gCoreContext->GetBackendServerIP();
01841 QString port = gCoreContext->GetSetting("BackendServerPort");
01842
01843 if (playbackhost == gCoreContext->GetHostName())
01844 pginfo.SetPathname(lpath);
01845 else
01846 pginfo.SetPathname(gCoreContext->GenMythURL(ip,port,pginfo.GetBasename()));
01847
01848 const QFileInfo info(lpath);
01849 pginfo.SetFilesize(info.size());
01850 }
01851
01852 QStringList strlist;
01853
01854 pginfo.ToStringList(strlist);
01855
01856 SendResponse(pbssock, strlist);
01857 }
01858
01859
01860 void DeleteThread::run(void)
01861 {
01862 if (m_ms)
01863 m_ms->DoDeleteThread(this);
01864 }
01865
01866 void MainServer::DoDeleteThread(DeleteStruct *ds)
01867 {
01868
01869
01870 sleep(3);
01871 usleep(random()%2000);
01872
01873 deletelock.lock();
01874
01875 QString logInfo = QString("chanid %1 at %2")
01876 .arg(ds->m_chanid)
01877 .arg(ds->m_recstartts.toString());
01878
01879 QString name = QString("deleteThread%1%2").arg(getpid()).arg(random());
01880 QFile checkFile(ds->m_filename);
01881
01882 if (!MSqlQuery::testDBConnection())
01883 {
01884 QString msg = QString("ERROR opening database connection for Delete "
01885 "Thread for chanid %1 recorded at %2. Program "
01886 "will NOT be deleted.")
01887 .arg(ds->m_chanid)
01888 .arg(ds->m_recstartts.toString());
01889 LOG(VB_GENERAL, LOG_ERR, msg);
01890
01891 deletelock.unlock();
01892 return;
01893 }
01894
01895 ProgramInfo pginfo(ds->m_chanid, ds->m_recstartts);
01896
01897 if (!pginfo.GetChanID())
01898 {
01899 QString msg = QString("ERROR retrieving program info when trying to "
01900 "delete program for chanid %1 recorded at %2. "
01901 "Recording will NOT be deleted.")
01902 .arg(ds->m_chanid)
01903 .arg(ds->m_recstartts.toString());
01904 LOG(VB_GENERAL, LOG_ERR, msg);
01905
01906 deletelock.unlock();
01907 return;
01908 }
01909
01910
01911
01912
01913
01914 if ((!checkFile.exists()) && pginfo.GetFilesize() &&
01915 (!ds->m_forceMetadataDelete))
01916 {
01917 LOG(VB_GENERAL, LOG_ERR,
01918 QString("ERROR when trying to delete file: %1. File "
01919 "doesn't exist. Database metadata will not be removed.")
01920 .arg(ds->m_filename));
01921
01922 pginfo.SaveDeletePendingFlag(false);
01923 deletelock.unlock();
01924 return;
01925 }
01926
01927 JobQueue::DeleteAllJobs(ds->m_chanid, ds->m_recstartts);
01928
01929 LiveTVChain *tvchain = GetChainWithRecording(pginfo);
01930 if (tvchain)
01931 tvchain->DeleteProgram(&pginfo);
01932
01933 bool followLinks = gCoreContext->GetNumSetting("DeletesFollowLinks", 0);
01934 bool slowDeletes = gCoreContext->GetNumSetting("TruncateDeletesSlowly", 0);
01935 int fd = -1;
01936 off_t size = 0;
01937 bool errmsg = false;
01938
01939
01940 if (slowDeletes)
01941 {
01942
01943
01944 const QFileInfo info(ds->m_filename);
01945 size = info.size();
01946 fd = DeleteFile(ds->m_filename, followLinks, ds->m_forceMetadataDelete);
01947
01948 if ((fd < 0) && checkFile.exists())
01949 errmsg = true;
01950 }
01951 else
01952 {
01953 delete_file_immediately(ds->m_filename, followLinks, false);
01954 sleep(2);
01955 if (checkFile.exists())
01956 errmsg = true;
01957 }
01958
01959 if (errmsg)
01960 {
01961 LOG(VB_GENERAL, LOG_ERR,
01962 QString("Error deleting file: %1. Keeping metadata in database.")
01963 .arg(ds->m_filename));
01964
01965 pginfo.SaveDeletePendingFlag(false);
01966 deletelock.unlock();
01967 return;
01968 }
01969
01970
01971
01972 QFileInfo fInfo( ds->m_filename );
01973 QString nameFilter = fInfo.fileName() + "*.png";
01974
01975
01976
01977 nameFilter.replace(QRegExp("( |;)"), "?");
01978 QDir dir ( fInfo.path(), nameFilter );
01979
01980 for (uint nIdx = 0; nIdx < dir.count(); nIdx++)
01981 {
01982 QString sFileName = QString( "%1/%2" )
01983 .arg( fInfo.path() )
01984 .arg( dir[ nIdx ] );
01985
01986 delete_file_immediately( sFileName, followLinks, true);
01987 }
01988
01989 DeleteRecordedFiles(ds);
01990
01991 DoDeleteInDB(ds);
01992
01993 deletelock.unlock();
01994
01995 if (slowDeletes && fd >= 0)
01996 TruncateAndClose(&pginfo, fd, ds->m_filename, size);
01997 }
01998
01999 void MainServer::DeleteRecordedFiles(DeleteStruct *ds)
02000 {
02001 QString logInfo = QString("chanid %1 at %2")
02002 .arg(ds->m_chanid).arg(ds->m_recstartts.toString());
02003
02004 MSqlQuery update(MSqlQuery::InitCon());
02005 MSqlQuery query(MSqlQuery::InitCon());
02006 query.prepare("SELECT basename, hostname, storagegroup FROM recordedfile "
02007 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
02008 query.bindValue(":CHANID", ds->m_chanid);
02009 query.bindValue(":STARTTIME", ds->m_recstartts);
02010
02011 if (!query.exec() || !query.isActive())
02012 {
02013 MythDB::DBError("RecordedFiles deletion", query);
02014 LOG(VB_GENERAL, LOG_ERR,
02015 QString("Error querying recordedfiles for %1.") .arg(logInfo));
02016 }
02017
02018 QString basename;
02019 QString hostname;
02020 QString storagegroup;
02021 bool deleteInDB;
02022 while (query.next())
02023 {
02024 basename = query.value(0).toString();
02025 hostname = query.value(1).toString();
02026 storagegroup = query.value(2).toString();
02027 deleteInDB = false;
02028
02029 if (basename == ds->m_filename)
02030 deleteInDB = true;
02031 else
02032 {
02033 LOG(VB_FILE, LOG_INFO,
02034 QString("DeleteRecordedFiles(%1), deleting '%2'")
02035 .arg(logInfo).arg(query.value(0).toString()));
02036
02037 StorageGroup sgroup(storagegroup);
02038 QString localFile = sgroup.FindFile(basename);
02039
02040 QString url = gCoreContext->GenMythURL(
02041 gCoreContext->GetBackendServerIP(hostname),
02042 gCoreContext->GetSettingOnHost("BackendServerPort", hostname),
02043 basename,
02044 storagegroup);
02045
02046 if ((((hostname == gCoreContext->GetHostName()) ||
02047 (!localFile.isEmpty())) &&
02048 (HandleDeleteFile(basename, storagegroup))) ||
02049 (((hostname != gCoreContext->GetHostName()) ||
02050 (localFile.isEmpty())) &&
02051 (RemoteFile::DeleteFile(url))))
02052 {
02053 deleteInDB = true;
02054 }
02055 }
02056
02057 if (deleteInDB)
02058 {
02059 update.prepare("DELETE FROM recordedfile "
02060 "WHERE chanid = :CHANID "
02061 "AND starttime = :STARTTIME "
02062 "AND basename = :BASENAME ;");
02063 update.bindValue(":CHANID", ds->m_chanid);
02064 update.bindValue(":STARTTIME", ds->m_recstartts);
02065 update.bindValue(":BASENAME", basename);
02066 if (!update.exec())
02067 {
02068 MythDB::DBError("RecordedFiles deletion", update);
02069 LOG(VB_GENERAL, LOG_ERR,
02070 QString("Error querying recordedfile (%1) for %2.")
02071 .arg(query.value(1).toString())
02072 .arg(logInfo));
02073 }
02074 }
02075 }
02076 }
02077
02078 void MainServer::DoDeleteInDB(DeleteStruct *ds)
02079 {
02080 QString logInfo = QString("chanid %1 at %2")
02081 .arg(ds->m_chanid).arg(ds->m_recstartts.toString());
02082
02083 MSqlQuery query(MSqlQuery::InitCon());
02084 query.prepare("DELETE FROM recorded WHERE chanid = :CHANID AND "
02085 "title = :TITLE AND starttime = :STARTTIME;");
02086 query.bindValue(":CHANID", ds->m_chanid);
02087 query.bindValue(":TITLE", ds->m_title);
02088 query.bindValue(":STARTTIME", ds->m_recstartts);
02089
02090 if (!query.exec() || !query.isActive())
02091 {
02092 MythDB::DBError("Recorded program deletion", query);
02093 LOG(VB_GENERAL, LOG_ERR,
02094 QString("Error deleting recorded entry for %1.") .arg(logInfo));
02095 }
02096
02097 sleep(1);
02098
02099
02100 QString msg = QString("RECORDING_LIST_CHANGE DELETE %1 %2")
02101 .arg(ds->m_chanid).arg(ds->m_recstartts.toString(Qt::ISODate));
02102 gCoreContext->SendEvent(MythEvent(msg));
02103
02104
02105 sleep(3);
02106
02107 query.prepare("DELETE FROM recordedmarkup "
02108 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
02109 query.bindValue(":CHANID", ds->m_chanid);
02110 query.bindValue(":STARTTIME", ds->m_recstartts);
02111
02112 if (!query.exec())
02113 {
02114 MythDB::DBError("Recorded program delete recordedmarkup", query);
02115 LOG(VB_GENERAL, LOG_ERR,
02116 QString("Error deleting recordedmarkup for %1.") .arg(logInfo));
02117 }
02118
02119 query.prepare("DELETE FROM recordedseek "
02120 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
02121 query.bindValue(":CHANID", ds->m_chanid);
02122 query.bindValue(":STARTTIME", ds->m_recstartts);
02123
02124 if (!query.exec())
02125 {
02126 MythDB::DBError("Recorded program delete recordedseek", query);
02127 LOG(VB_GENERAL, LOG_ERR, QString("Error deleting recordedseek for %1.")
02128 .arg(logInfo));
02129 }
02130 }
02131
02141 int MainServer::DeleteFile(const QString &filename, bool followLinks,
02142 bool deleteBrokenSymlinks)
02143 {
02144 QFileInfo finfo(filename);
02145 int fd = -1, err = 0;
02146 QString linktext = "";
02147 QByteArray fname = filename.toLocal8Bit();
02148
02149 LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file: '%1'")
02150 .arg(fname.constData()));
02151
02152 QString errmsg = QString("Delete Error '%1'").arg(fname.constData());
02153 if (finfo.isSymLink())
02154 {
02155 linktext = getSymlinkTarget(filename);
02156 QByteArray alink = linktext.toLocal8Bit();
02157 errmsg += QString(" -> '%2'").arg(alink.constData());
02158 }
02159
02160 if (followLinks && finfo.isSymLink())
02161 {
02162 if (!finfo.exists() && deleteBrokenSymlinks)
02163 err = unlink(fname.constData());
02164 else
02165 {
02166 fd = OpenAndUnlink(linktext);
02167 if (fd >= 0)
02168 err = unlink(fname.constData());
02169 }
02170 }
02171 else if (!finfo.isSymLink())
02172 {
02173 fd = OpenAndUnlink(filename);
02174 }
02175 else
02176 {
02177 err = unlink(fname.constData());
02178 if (err == 0)
02179 return -2;
02180 }
02181
02182 if (fd < 0)
02183 LOG(VB_GENERAL, LOG_ERR, errmsg + ENO);
02184
02185 return fd;
02186 }
02187
02197 int MainServer::OpenAndUnlink(const QString &filename)
02198 {
02199 QByteArray fname = filename.toLocal8Bit();
02200 QString msg = QString("Error deleting '%1'").arg(fname.constData());
02201 int fd = open(fname.constData(), O_WRONLY);
02202
02203 if (fd == -1)
02204 {
02205 LOG(VB_GENERAL, LOG_ERR, msg + " could not open " + ENO);
02206 return -1;
02207 }
02208
02209 if (unlink(fname.constData()))
02210 {
02211 LOG(VB_GENERAL, LOG_ERR, msg + " could not unlink " + ENO);
02212 close(fd);
02213 return -1;
02214 }
02215
02216 return fd;
02217 }
02218
02227 bool MainServer::TruncateAndClose(ProgramInfo *pginfo, int fd,
02228 const QString &filename, off_t fsize)
02229 {
02230 QMutexLocker locker(&truncate_and_close_lock);
02231
02232 if (pginfo)
02233 {
02234 pginfo->SetPathname(filename);
02235 pginfo->MarkAsInUse(true, kTruncatingDeleteInUseID);
02236 }
02237
02238 int cards = 5;
02239
02240 MSqlQuery query(MSqlQuery::InitCon());
02241 query.prepare("SELECT COUNT(cardid) FROM capturecard;");
02242 if (query.exec() && query.next())
02243 cards = query.value(0).toInt();
02244
02245
02246 const size_t sleep_time = 500;
02247 const size_t min_tps = 8 * 1024 * 1024;
02248 const size_t calc_tps = (size_t) (cards * 1.2 * (22200000LL / 8));
02249 const size_t tps = max(min_tps, calc_tps);
02250 const size_t increment = (size_t) (tps * (sleep_time * 0.001f));
02251
02252 LOG(VB_FILE, LOG_INFO,
02253 QString("Truncating '%1' by %2 MB every %3 milliseconds")
02254 .arg(filename)
02255 .arg(increment / (1024.0 * 1024.0), 0, 'f', 2)
02256 .arg(sleep_time));
02257
02258 int count = 0;
02259 while (fsize > 0)
02260 {
02261 #if 0
02262 LOG(VB_FILE, LOG_DEBUG, QString("Truncating '%1' to %2 MB")
02263 .arg(filename).arg(fsize / (1024.0 * 1024.0), 0, 'f', 2));
02264 #endif
02265
02266 int err = ftruncate(fd, fsize);
02267 if (err)
02268 {
02269 LOG(VB_GENERAL, LOG_ERR, QString("Error truncating '%1'")
02270 .arg(filename) + ENO);
02271 if (pginfo)
02272 pginfo->MarkAsInUse(false, kTruncatingDeleteInUseID);
02273 return 0 == close(fd);
02274 }
02275
02276 fsize -= increment;
02277
02278 if (pginfo && ((count % 100) == 0))
02279 pginfo->UpdateInUseMark(true);
02280
02281 count++;
02282
02283 usleep(sleep_time * 1000);
02284 }
02285
02286 bool ok = (0 == close(fd));
02287
02288 if (pginfo)
02289 pginfo->MarkAsInUse(false, kTruncatingDeleteInUseID);
02290
02291 LOG(VB_FILE, LOG_INFO, QString("Finished truncating '%1'").arg(filename));
02292
02293 return ok;
02294 }
02295
02296 void MainServer::HandleCheckRecordingActive(QStringList &slist,
02297 PlaybackSock *pbs)
02298 {
02299 MythSocket *pbssock = NULL;
02300 if (pbs)
02301 pbssock = pbs->getSocket();
02302
02303 QStringList::const_iterator it = slist.begin() + 1;
02304 ProgramInfo pginfo(it, slist.end());
02305
02306 int result = 0;
02307
02308 if (ismaster && pginfo.GetHostname() != gCoreContext->GetHostName())
02309 {
02310 PlaybackSock *slave = GetSlaveByHostname(pginfo.GetHostname());
02311 if (slave)
02312 {
02313 result = slave->CheckRecordingActive(&pginfo);
02314 slave->DownRef();
02315 }
02316 }
02317 else
02318 {
02319 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02320 for (; iter != encoderList->end(); ++iter)
02321 {
02322 EncoderLink *elink = *iter;
02323
02324 if (elink->IsLocal() && elink->MatchesRecording(&pginfo))
02325 result = iter.key();
02326 }
02327 }
02328
02329 QStringList outputlist( QString::number(result) );
02330 if (pbssock)
02331 SendResponse(pbssock, outputlist);
02332
02333 return;
02334 }
02335
02336 void MainServer::HandleStopRecording(QStringList &slist, PlaybackSock *pbs)
02337 {
02338 QStringList::const_iterator it = slist.begin() + 1;
02339 RecordingInfo recinfo(it, slist.end());
02340 if (recinfo.GetChanID())
02341 DoHandleStopRecording(recinfo, pbs);
02342 }
02343
02344 void MainServer::DoHandleStopRecording(
02345 RecordingInfo &recinfo, PlaybackSock *pbs)
02346 {
02347 MythSocket *pbssock = NULL;
02348 if (pbs)
02349 pbssock = pbs->getSocket();
02350
02351
02352
02353
02354
02355
02356
02357 recinfo.SetRecordingStatus(rsUnknown);
02358
02359 if (ismaster && recinfo.GetHostname() != gCoreContext->GetHostName())
02360 {
02361 PlaybackSock *slave = GetSlaveByHostname(recinfo.GetHostname());
02362
02363 if (slave)
02364 {
02365 int num = slave->StopRecording(&recinfo);
02366
02367 if (num > 0)
02368 {
02369 (*encoderList)[num]->StopRecording();
02370 if (m_sched)
02371 m_sched->UpdateRecStatus(&recinfo);
02372 }
02373 if (pbssock)
02374 {
02375 QStringList outputlist( "0" );
02376 SendResponse(pbssock, outputlist);
02377 }
02378
02379 slave->DownRef();
02380 return;
02381 }
02382 else
02383 {
02384
02385
02386
02387
02388 if (m_sched)
02389 m_sched->UpdateRecStatus(&recinfo);
02390 }
02391
02392 }
02393
02394 int recnum = -1;
02395
02396 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02397 for (; iter != encoderList->end(); ++iter)
02398 {
02399 EncoderLink *elink = *iter;
02400
02401 if (elink->IsLocal() && elink->MatchesRecording(&recinfo))
02402 {
02403 recnum = iter.key();
02404
02405 elink->StopRecording();
02406
02407 while (elink->IsBusyRecording() ||
02408 elink->GetState() == kState_ChangingState)
02409 {
02410 usleep(100);
02411 }
02412
02413 if (ismaster)
02414 {
02415 if (m_sched)
02416 m_sched->UpdateRecStatus(&recinfo);
02417 }
02418 }
02419 }
02420
02421 if (pbssock)
02422 {
02423 QStringList outputlist( QString::number(recnum) );
02424 SendResponse(pbssock, outputlist);
02425 }
02426 }
02427
02428 void MainServer::HandleDeleteRecording(QString &chanid, QString &starttime,
02429 PlaybackSock *pbs,
02430 bool forceMetadataDelete,
02431 bool forgetHistory)
02432 {
02433 QDateTime recstartts = myth_dt_from_string(starttime);
02434 RecordingInfo recinfo(chanid.toUInt(), recstartts);
02435
02436 if (!recinfo.GetChanID())
02437 {
02438 MythSocket *pbssock = NULL;
02439 if (pbs)
02440 pbssock = pbs->getSocket();
02441
02442 QStringList outputlist( QString::number(0) );
02443
02444 SendResponse(pbssock, outputlist);
02445 return;
02446 }
02447
02448 DoHandleDeleteRecording(recinfo, pbs, forceMetadataDelete, false, forgetHistory);
02449 }
02450
02451 void MainServer::HandleDeleteRecording(QStringList &slist, PlaybackSock *pbs,
02452 bool forceMetadataDelete)
02453 {
02454 QStringList::const_iterator it = slist.begin() + 1;
02455 RecordingInfo recinfo(it, slist.end());
02456 if (recinfo.GetChanID())
02457 DoHandleDeleteRecording(recinfo, pbs, forceMetadataDelete, false, false);
02458 }
02459
02460 void MainServer::DoHandleDeleteRecording(
02461 RecordingInfo &recinfo, PlaybackSock *pbs,
02462 bool forceMetadataDelete, bool expirer, bool forgetHistory)
02463 {
02464 int resultCode = -1;
02465 MythSocket *pbssock = NULL;
02466 if (pbs)
02467 pbssock = pbs->getSocket();
02468
02469 bool justexpire = expirer ? false :
02470 (
02471 (recinfo.GetRecordingGroup() != "Deleted") &&
02472 (recinfo.GetRecordingGroup() != "LiveTV"));
02473
02474 QString filename = GetPlaybackURL(&recinfo, false);
02475 if (filename.isEmpty())
02476 {
02477 LOG(VB_GENERAL, LOG_ERR,
02478 QString("ERROR when trying to delete file for %1. Unable "
02479 "to determine filename of recording.")
02480 .arg(recinfo.toString(ProgramInfo::kRecordingKey)));
02481
02482 if (pbssock)
02483 {
02484 resultCode = -2;
02485 QStringList outputlist(QString::number(resultCode));
02486 SendResponse(pbssock, outputlist);
02487 }
02488
02489 return;
02490 }
02491
02492
02493 DoHandleStopRecording(recinfo, NULL);
02494
02495 if (justexpire && !forceMetadataDelete &&
02496 recinfo.GetFilesize() > (1024 * 1024) )
02497 {
02498 recinfo.ApplyRecordRecGroupChange("Deleted");
02499 recinfo.SaveAutoExpire(kDeletedAutoExpire, true);
02500 if (forgetHistory)
02501 recinfo.ForgetHistory();
02502 else if (m_sched)
02503 m_sched->RescheduleCheck(recinfo, "DoHandleDelete1");
02504 QStringList outputlist( QString::number(0) );
02505 SendResponse(pbssock, outputlist);
02506 return;
02507 }
02508
02509
02510
02511 if (ismaster && recinfo.GetHostname() != gCoreContext->GetHostName())
02512 {
02513 PlaybackSock *slave = GetSlaveByHostname(recinfo.GetHostname());
02514
02515 if (slave)
02516 {
02517 int num = slave->DeleteRecording(&recinfo, forceMetadataDelete);
02518
02519 if (forgetHistory)
02520 recinfo.ForgetHistory();
02521 else if (m_sched &&
02522 recinfo.GetRecordingGroup() != "Deleted" &&
02523 recinfo.GetRecordingGroup() != "LiveTV")
02524 m_sched->RescheduleCheck(recinfo, "DoHandleDelete2");
02525
02526 if (pbssock)
02527 {
02528 QStringList outputlist( QString::number(num) );
02529 SendResponse(pbssock, outputlist);
02530 }
02531
02532 slave->DownRef();
02533 return;
02534 }
02535 }
02536
02537 QFile checkFile(filename);
02538 bool fileExists = checkFile.exists();
02539 if (!fileExists)
02540 {
02541 QFile checkFileUTF8(QString::fromUtf8(filename.toAscii().constData()));
02542 fileExists = checkFileUTF8.exists();
02543 if (fileExists)
02544 filename = QString::fromUtf8(filename.toAscii().constData());
02545 }
02546
02547
02548
02549
02550
02551 if (fileExists || !recinfo.GetFilesize() || forceMetadataDelete)
02552 {
02553 recinfo.SaveDeletePendingFlag(true);
02554
02555 DeleteThread *deleteThread = new DeleteThread(this, filename,
02556 recinfo.GetTitle(), recinfo.GetChanID(),
02557 recinfo.GetRecordingStartTime(), recinfo.GetRecordingEndTime(),
02558 forceMetadataDelete);
02559 deleteThread->start();
02560 }
02561 else
02562 {
02563 QString logInfo = QString("chanid %1")
02564 .arg(recinfo.toString(ProgramInfo::kRecordingKey));
02565
02566 LOG(VB_GENERAL, LOG_ERR,
02567 QString("ERROR when trying to delete file: %1. File doesn't "
02568 "exist. Database metadata will not be removed.")
02569 .arg(filename));
02570 resultCode = -2;
02571 }
02572
02573 if (pbssock)
02574 {
02575 QStringList outputlist( QString::number(resultCode) );
02576 SendResponse(pbssock, outputlist);
02577 }
02578
02579 if (forgetHistory)
02580 recinfo.ForgetHistory();
02581 else if (m_sched &&
02582 recinfo.GetRecordingGroup() != "Deleted" &&
02583 recinfo.GetRecordingGroup() != "LiveTV")
02584 m_sched->RescheduleCheck(recinfo, "DoHandleDelete3");
02585
02586
02587 if (fileExists || !recinfo.GetFilesize() || forceMetadataDelete)
02588 {
02589 gCoreContext->SendSystemEvent(
02590 QString("REC_DELETED CHANID %1 STARTTIME %2")
02591 .arg(recinfo.GetChanID())
02592 .arg(recinfo.GetRecordingStartTime(ISODate)));
02593
02594 recinfo.SendDeletedEvent();
02595 }
02596 }
02597
02598 void MainServer::HandleUndeleteRecording(QStringList &slist, PlaybackSock *pbs)
02599 {
02600 if (slist.size() == 3)
02601 {
02602 RecordingInfo recinfo(
02603 slist[1].toUInt(), QDateTime::fromString(slist[2], Qt::ISODate));
02604 if (recinfo.GetChanID())
02605 DoHandleUndeleteRecording(recinfo, pbs);
02606 }
02607 else if (slist.size() >= (1 + NUMPROGRAMLINES))
02608 {
02609 QStringList::const_iterator it = slist.begin()+1;
02610 RecordingInfo recinfo(it, slist.end());
02611 if (recinfo.GetChanID())
02612 DoHandleUndeleteRecording(recinfo, pbs);
02613 }
02614 }
02615
02616 void MainServer::DoHandleUndeleteRecording(
02617 RecordingInfo &recinfo, PlaybackSock *pbs)
02618 {
02619 int ret = -1;
02620
02621 MythSocket *pbssock = NULL;
02622 if (pbs)
02623 pbssock = pbs->getSocket();
02624
02625 #if 0
02626 if (gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0))
02627 {
02628 #endif
02629 recinfo.ApplyRecordRecGroupChange("Default");
02630 recinfo.UpdateLastDelete(false);
02631 recinfo.SaveAutoExpire(kDisableAutoExpire);
02632 ret = 0;
02633 #if 0
02634 }
02635 #endif
02636
02637 QStringList outputlist( QString::number(ret) );
02638 SendResponse(pbssock, outputlist);
02639 }
02640
02641 void MainServer::HandleRescheduleRecordings(const QStringList &request,
02642 PlaybackSock *pbs)
02643 {
02644 QStringList result;
02645 if (m_sched)
02646 {
02647 m_sched->Reschedule(request);
02648 result = QStringList( QString::number(1) );
02649 }
02650 else
02651 result = QStringList( QString::number(0) );
02652
02653 if (pbs)
02654 {
02655 MythSocket *pbssock = pbs->getSocket();
02656 if (pbssock)
02657 SendResponse(pbssock, result);
02658 }
02659 }
02660
02661 void MainServer::HandleForgetRecording(QStringList &slist, PlaybackSock *pbs)
02662 {
02663 QStringList::const_iterator it = slist.begin() + 1;
02664 RecordingInfo recinfo(it, slist.end());
02665 if (recinfo.GetChanID())
02666 recinfo.ForgetHistory();
02667
02668 MythSocket *pbssock = NULL;
02669 if (pbs)
02670 pbssock = pbs->getSocket();
02671 if (pbssock)
02672 {
02673 QStringList outputlist( QString::number(0) );
02674 SendResponse(pbssock, outputlist);
02675 }
02676 }
02677
02678
02679
02680
02681
02682
02683 void MainServer::HandleGoToSleep(PlaybackSock *pbs)
02684 {
02685 QStringList strlist;
02686
02687 QString sleepCmd = gCoreContext->GetSetting("SleepCommand");
02688 if (!sleepCmd.isEmpty())
02689 {
02690 strlist << "OK";
02691 SendResponse(pbs->getSocket(), strlist);
02692 LOG(VB_GENERAL, LOG_NOTICE,
02693 "Received GO_TO_SLEEP command from master, running SleepCommand.");
02694 myth_system(sleepCmd);
02695 }
02696 else
02697 {
02698 strlist << "ERROR: SleepCommand is empty";
02699 LOG(VB_GENERAL, LOG_ERR,
02700 "ERROR: in HandleGoToSleep(), but no SleepCommand found!");
02701 SendResponse(pbs->getSocket(), strlist);
02702 }
02703 }
02704
02714 void MainServer::HandleQueryFreeSpace(PlaybackSock *pbs, bool allHosts)
02715 {
02716 QStringList strlist;
02717
02718 if (allHosts)
02719 {
02720 QMutexLocker locker(&masterFreeSpaceListLock);
02721 strlist = masterFreeSpaceList;
02722 if (!masterFreeSpaceListUpdater ||
02723 !masterFreeSpaceListUpdater->KeepRunning(true))
02724 {
02725 while (masterFreeSpaceListUpdater)
02726 {
02727 masterFreeSpaceListUpdater->KeepRunning(false);
02728 masterFreeSpaceListWait.wait(locker.mutex());
02729 }
02730 masterFreeSpaceListUpdater = new FreeSpaceUpdater(*this);
02731 MThreadPool::globalInstance()->startReserved(
02732 masterFreeSpaceListUpdater, "FreeSpaceUpdater");
02733 }
02734 }
02735 else
02736 BackendQueryDiskSpace(strlist, allHosts, allHosts);
02737
02738 SendResponse(pbs->getSocket(), strlist);
02739 }
02740
02746 void MainServer::HandleQueryFreeSpaceSummary(PlaybackSock *pbs)
02747 {
02748 QStringList strlist;
02749 {
02750 QMutexLocker locker(&masterFreeSpaceListLock);
02751 strlist = masterFreeSpaceList;
02752 if (!masterFreeSpaceListUpdater ||
02753 !masterFreeSpaceListUpdater->KeepRunning(true))
02754 {
02755 while (masterFreeSpaceListUpdater)
02756 {
02757 masterFreeSpaceListUpdater->KeepRunning(false);
02758 masterFreeSpaceListWait.wait(locker.mutex());
02759 }
02760 masterFreeSpaceListUpdater = new FreeSpaceUpdater(*this);
02761 MThreadPool::globalInstance()->startReserved(
02762 masterFreeSpaceListUpdater, "FreeSpaceUpdater");
02763 }
02764 }
02765
02766
02767 QStringList shortlist;
02768 if (strlist.size() < 4)
02769 {
02770 shortlist << QString("0");
02771 shortlist << QString("0");
02772 }
02773 else
02774 {
02775 unsigned int index = (uint)(strlist.size()) - 2;
02776 shortlist << strlist[index++];
02777 shortlist << strlist[index++];
02778 }
02779
02780 SendResponse(pbs->getSocket(), shortlist);
02781 }
02782
02789 void MainServer::HandleQueryLoad(PlaybackSock *pbs)
02790 {
02791 MythSocket *pbssock = pbs->getSocket();
02792
02793 QStringList strlist;
02794
02795 double loads[3];
02796 if (getloadavg(loads,3) == -1)
02797 {
02798 strlist << "ERROR";
02799 strlist << "getloadavg() failed";
02800 }
02801 else
02802 strlist << QString::number(loads[0])
02803 << QString::number(loads[1])
02804 << QString::number(loads[2]);
02805
02806 SendResponse(pbssock, strlist);
02807 }
02808
02814 void MainServer::HandleQueryUptime(PlaybackSock *pbs)
02815 {
02816 MythSocket *pbssock = pbs->getSocket();
02817 QStringList strlist;
02818 time_t uptime;
02819
02820 if (getUptime(uptime))
02821 strlist << QString::number(uptime);
02822 else
02823 {
02824 strlist << "ERROR";
02825 strlist << "Could not determine uptime.";
02826 }
02827
02828 SendResponse(pbssock, strlist);
02829 }
02830
02836 void MainServer::HandleQueryHostname(PlaybackSock *pbs)
02837 {
02838 MythSocket *pbssock = pbs->getSocket();
02839 QStringList strlist;
02840
02841 strlist << gCoreContext->GetHostName();
02842
02843 SendResponse(pbssock, strlist);
02844 }
02845
02851 void MainServer::HandleQueryMemStats(PlaybackSock *pbs)
02852 {
02853 MythSocket *pbssock = pbs->getSocket();
02854 QStringList strlist;
02855 int totalMB, freeMB, totalVM, freeVM;
02856
02857 if (getMemStats(totalMB, freeMB, totalVM, freeVM))
02858 strlist << QString::number(totalMB) << QString::number(freeMB)
02859 << QString::number(totalVM) << QString::number(freeVM);
02860 else
02861 {
02862 strlist << "ERROR";
02863 strlist << "Could not determine memory stats.";
02864 }
02865
02866 SendResponse(pbssock, strlist);
02867 }
02868
02874 void MainServer::HandleQueryTimeZone(PlaybackSock *pbs)
02875 {
02876 MythSocket *pbssock = pbs->getSocket();
02877 QStringList strlist;
02878 strlist << getTimeZoneID() << QString::number(calc_utc_offset())
02879 << mythCurrentDateTime().toString(Qt::ISODate);
02880
02881 SendResponse(pbssock, strlist);
02882 }
02883
02888 void MainServer::HandleQueryCheckFile(QStringList &slist, PlaybackSock *pbs)
02889 {
02890 MythSocket *pbssock = pbs->getSocket();
02891 bool checkSlaves = slist[1].toInt();
02892
02893 QStringList::const_iterator it = slist.begin() + 2;
02894 RecordingInfo recinfo(it, slist.end());
02895
02896 int exists = 0;
02897
02898 if (recinfo.HasPathname() && (ismaster) &&
02899 (recinfo.GetHostname() != gCoreContext->GetHostName()) &&
02900 (checkSlaves))
02901 {
02902 PlaybackSock *slave = GetSlaveByHostname(recinfo.GetHostname());
02903
02904 if (slave)
02905 {
02906 exists = slave->CheckFile(&recinfo);
02907 slave->DownRef();
02908
02909 QStringList outputlist( QString::number(exists) );
02910 if (exists)
02911 outputlist << recinfo.GetPathname();
02912 else
02913 outputlist << "";
02914
02915 SendResponse(pbssock, outputlist);
02916 return;
02917 }
02918 }
02919
02920 QString pburl;
02921 if (recinfo.HasPathname())
02922 {
02923 pburl = GetPlaybackURL(&recinfo);
02924 exists = QFileInfo(pburl).exists();
02925 if (!exists)
02926 pburl.clear();
02927 }
02928
02929 QStringList strlist( QString::number(exists) );
02930 strlist << pburl;
02931 SendResponse(pbssock, strlist);
02932 }
02933
02934
02939 void MainServer::HandleQueryFileHash(QStringList &slist, PlaybackSock *pbs)
02940 {
02941 QString storageGroup = "Default";
02942 QString hostname = gCoreContext->GetHostName();
02943 QString filename = "";
02944 QStringList res;
02945
02946 switch (slist.size()) {
02947 case 4:
02948 if (!slist[3].isEmpty())
02949 hostname = slist[3];
02950 case 3:
02951 if (slist[2].isEmpty())
02952 storageGroup = slist[2];
02953 case 2:
02954 filename = slist[1];
02955 if (filename.isEmpty() ||
02956 filename.contains("/../") ||
02957 filename.startsWith("../"))
02958 {
02959 LOG(VB_GENERAL, LOG_ERR,
02960 QString("ERROR checking for file, filename '%1' "
02961 "fails sanity checks").arg(filename));
02962 res << "";
02963 SendResponse(pbs->getSocket(), res);
02964 return;
02965 }
02966 break;
02967 default:
02968 LOG(VB_GENERAL, LOG_ERR, "ERROR, invalid input count for QUERY_FILE_HASH");
02969 res << "";
02970 SendResponse(pbs->getSocket(), res);
02971 return;
02972 }
02973
02974 QString hash = "";
02975
02976 if (hostname == gCoreContext->GetHostName())
02977 {
02978 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
02979 QString fullname = sgroup.FindFile(filename);
02980 hash = FileHash(fullname);
02981 }
02982 else
02983 {
02984 PlaybackSock *slave = GetMediaServerByHostname(hostname);
02985 if (slave)
02986 {
02987 hash = slave->GetFileHash(filename, storageGroup);
02988 slave->DownRef();
02989 }
02990 else
02991 {
02992 MSqlQuery query(MSqlQuery::InitCon());
02993 query.prepare("SELECT hostname FROM settings "
02994 "WHERE value='BackendServerIP' "
02995 "OR value='BackendServerIP6' "
02996 "AND data=:HOSTNAME;");
02997 query.bindValue(":HOSTNAME", hostname);
02998
02999 if (query.exec() && query.next())
03000 {
03001 hostname = query.value(0).toString();
03002 slave = GetMediaServerByHostname(hostname);
03003 if (slave)
03004 {
03005 hash = slave->GetFileHash(filename, storageGroup);
03006 slave->DownRef();
03007 }
03008 }
03009 }
03010 }
03011
03012 res << hash;
03013 SendResponse(pbs->getSocket(), res);
03014 }
03015
03020 void MainServer::HandleQueryFileExists(QStringList &slist, PlaybackSock *pbs)
03021 {
03022 QString filename = slist[1];
03023 QString storageGroup = "Default";
03024 QStringList retlist;
03025
03026 if (slist.size() > 2)
03027 storageGroup = slist[2];
03028
03029 if ((filename.isEmpty()) ||
03030 (filename.contains("/../")) ||
03031 (filename.startsWith("../")))
03032 {
03033 LOG(VB_GENERAL, LOG_ERR,
03034 QString("ERROR checking for file, filename '%1' "
03035 "fails sanity checks").arg(filename));
03036 retlist << "0";
03037 SendResponse(pbs->getSocket(), retlist);
03038 return;
03039 }
03040
03041 if (storageGroup.isEmpty())
03042 storageGroup = "Default";
03043
03044 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
03045
03046 QString fullname = sgroup.FindFile(filename);
03047
03048 if (!fullname.isEmpty())
03049 {
03050 retlist << "1";
03051 retlist << fullname;
03052
03053 struct stat fileinfo;
03054 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
03055 {
03056 retlist << QString::number(fileinfo.st_dev);
03057 retlist << QString::number(fileinfo.st_ino);
03058 retlist << QString::number(fileinfo.st_mode);
03059 retlist << QString::number(fileinfo.st_nlink);
03060 retlist << QString::number(fileinfo.st_uid);
03061 retlist << QString::number(fileinfo.st_gid);
03062 retlist << QString::number(fileinfo.st_rdev);
03063 retlist << QString::number(fileinfo.st_size);
03064 #ifdef USING_MINGW
03065 retlist << "0";
03066 retlist << "0";
03067 #else
03068 retlist << QString::number(fileinfo.st_blksize);
03069 retlist << QString::number(fileinfo.st_blocks);
03070 #endif
03071 retlist << QString::number(fileinfo.st_atime);
03072 retlist << QString::number(fileinfo.st_mtime);
03073 retlist << QString::number(fileinfo.st_ctime);
03074 }
03075 }
03076 else
03077 retlist << "0";
03078
03079 SendResponse(pbs->getSocket(), retlist);
03080 }
03081
03082 void MainServer::getGuideDataThrough(QDateTime &GuideDataThrough)
03083 {
03084 MSqlQuery query(MSqlQuery::InitCon());
03085 query.prepare("SELECT MAX(endtime) FROM program WHERE manualid = 0;");
03086
03087 if (query.exec() && query.next())
03088 {
03089 GuideDataThrough = QDateTime::fromString(
03090 query.value(0).toString(), Qt::ISODate);
03091 }
03092 }
03093
03094 void MainServer::HandleQueryGuideDataThrough(PlaybackSock *pbs)
03095 {
03096 QDateTime GuideDataThrough;
03097 MythSocket *pbssock = pbs->getSocket();
03098 QStringList strlist;
03099
03100 getGuideDataThrough(GuideDataThrough);
03101
03102 if (GuideDataThrough.isNull())
03103 strlist << QString("0000-00-00 00:00");
03104 else
03105 strlist << QDateTime(GuideDataThrough).toString("yyyy-MM-dd hh:mm");
03106
03107 SendResponse(pbssock, strlist);
03108 }
03109
03110 void MainServer::HandleGetPendingRecordings(PlaybackSock *pbs,
03111 QString tmptable, int recordid)
03112 {
03113 MythSocket *pbssock = pbs->getSocket();
03114
03115 QStringList strList;
03116
03117 if (m_sched)
03118 {
03119 if (tmptable.isEmpty())
03120 m_sched->GetAllPending(strList);
03121 else
03122 {
03123 Scheduler *sched = new Scheduler(false, encoderList,
03124 tmptable, m_sched);
03125 sched->FillRecordListFromDB(recordid);
03126 sched->GetAllPending(strList);
03127 delete sched;
03128
03129 if (recordid > 0)
03130 {
03131 MSqlQuery query(MSqlQuery::InitCon());
03132 query.prepare("SELECT NULL FROM record "
03133 "WHERE recordid = :RECID;");
03134 query.bindValue(":RECID", recordid);
03135
03136 if (query.exec() && query.size())
03137 {
03138 RecordingRule *record = new RecordingRule();
03139 record->m_recordID = recordid;
03140 if (record->Load() &&
03141 record->m_searchType == kManualSearch)
03142 m_sched->RescheduleMatch(recordid, 0, 0, QDateTime(),
03143 "Speculation");
03144 delete record;
03145 }
03146 query.prepare("DELETE FROM program WHERE manualid = :RECID;");
03147 query.bindValue(":RECID", recordid);
03148 if (!query.exec())
03149 MythDB::DBError("MainServer::HandleGetPendingRecordings "
03150 "- delete", query);
03151 }
03152 }
03153 }
03154 else
03155 {
03156 strList << QString::number(0);
03157 strList << QString::number(0);
03158 }
03159
03160 SendResponse(pbssock, strList);
03161 }
03162
03163 void MainServer::HandleGetScheduledRecordings(PlaybackSock *pbs)
03164 {
03165 MythSocket *pbssock = pbs->getSocket();
03166
03167 QStringList strList;
03168
03169 if (m_sched)
03170 Scheduler::GetAllScheduled(strList);
03171 else
03172 strList << QString::number(0);
03173
03174 SendResponse(pbssock, strList);
03175 }
03176
03177 void MainServer::HandleGetConflictingRecordings(QStringList &slist,
03178 PlaybackSock *pbs)
03179 {
03180 MythSocket *pbssock = pbs->getSocket();
03181
03182 QStringList::const_iterator it = slist.begin() + 1;
03183 RecordingInfo recinfo(it, slist.end());
03184
03185 QStringList strlist;
03186
03187 if (m_sched && recinfo.GetChanID())
03188 m_sched->getConflicting(&recinfo, strlist);
03189 else
03190 strlist << QString::number(0);
03191
03192 SendResponse(pbssock, strlist);
03193 }
03194
03195 void MainServer::HandleGetExpiringRecordings(PlaybackSock *pbs)
03196 {
03197 MythSocket *pbssock = pbs->getSocket();
03198
03199 QStringList strList;
03200
03201 if (m_expirer)
03202 m_expirer->GetAllExpiring(strList);
03203 else
03204 strList << QString::number(0);
03205
03206 SendResponse(pbssock, strList);
03207 }
03208
03209 void MainServer::HandleSGGetFileList(QStringList &sList,
03210 PlaybackSock *pbs)
03211 {
03212 MythSocket *pbssock = pbs->getSocket();
03213 QStringList strList;
03214
03215 if ((sList.size() < 4) || (sList.size() > 5))
03216 {
03217 LOG(VB_GENERAL, LOG_ERR,
03218 QString("HandleSGGetFileList: Invalid Request. %1")
03219 .arg(sList.join("[]:[]")));
03220 strList << "EMPTY LIST";
03221 SendResponse(pbssock, strList);
03222 return;
03223 }
03224
03225 QString host = gCoreContext->GetHostName();
03226 QString wantHost = sList.at(1);
03227 QString groupname = sList.at(2);
03228 QString path = sList.at(3);
03229 bool fileNamesOnly = false;
03230
03231 if (sList.size() >= 5)
03232 fileNamesOnly = sList.at(4).toInt();
03233
03234 bool slaveUnreachable = false;
03235
03236 LOG(VB_FILE, LOG_INFO, QString("HandleSGGetFileList: group = %1 host = %2 "
03237 " path = %3 wanthost = %4")
03238 .arg(groupname).arg(host).arg(path).arg(wantHost));
03239
03240 if ((host.toLower() == wantHost.toLower()) ||
03241 (gCoreContext->GetSetting("BackendServerIP") == wantHost) ||
03242 (gCoreContext->GetSetting("BackendServerIP6") == wantHost))
03243 {
03244 StorageGroup sg(groupname, host);
03245 LOG(VB_FILE, LOG_INFO, "HandleSGGetFileList: Getting local info");
03246 if (fileNamesOnly)
03247 strList = sg.GetFileList(path);
03248 else
03249 strList = sg.GetFileInfoList(path);
03250 }
03251 else
03252 {
03253 PlaybackSock *slave = GetMediaServerByHostname(wantHost);
03254 if (slave)
03255 {
03256 LOG(VB_FILE, LOG_INFO, "HandleSGGetFileList: Getting remote info");
03257 strList = slave->GetSGFileList(wantHost, groupname, path,
03258 fileNamesOnly);
03259 slave->DownRef();
03260 slaveUnreachable = false;
03261 }
03262 else
03263 {
03264 LOG(VB_FILE, LOG_INFO,
03265 QString("HandleSGGetFileList: Failed to grab slave socket "
03266 ": %1 :").arg(wantHost));
03267 slaveUnreachable = true;
03268 }
03269
03270 }
03271
03272 if (slaveUnreachable)
03273 strList << "SLAVE UNREACHABLE: " << host;
03274
03275 if (strList.count() == 0 || (strList.at(0) == "0"))
03276 strList << "EMPTY LIST";
03277
03278 SendResponse(pbssock, strList);
03279 }
03280
03281 void MainServer::HandleSGFileQuery(QStringList &sList,
03282 PlaybackSock *pbs)
03283 {
03284 MythSocket *pbssock = pbs->getSocket();
03285 QStringList strList;
03286
03287 if (sList.size() != 4)
03288 {
03289 LOG(VB_GENERAL, LOG_ERR,
03290 QString("HandleSGFileQuery: Invalid Request. %1")
03291 .arg(sList.join("[]:[]")));
03292 strList << "EMPTY LIST";
03293 SendResponse(pbssock, strList);
03294 return;
03295 }
03296
03297 QString wantHost = sList.at(1);
03298 QString groupname = sList.at(2);
03299 QString filename = sList.at(3);
03300
03301 bool slaveUnreachable = false;
03302
03303 LOG(VB_FILE, LOG_INFO, QString("HandleSGFileQuery: %1")
03304 .arg(gCoreContext->GenMythURL(wantHost, 0, filename, groupname)));
03305
03306 if ((wantHost.toLower() == gCoreContext->GetHostName().toLower()) ||
03307 (wantHost == gCoreContext->GetSetting("BackendServerIP")) ||
03308 (wantHost == gCoreContext->GetSetting("BackendServerIP6")))
03309 {
03310 LOG(VB_FILE, LOG_INFO, "HandleSGFileQuery: Getting local info");
03311 StorageGroup sg(groupname, gCoreContext->GetHostName());
03312 strList = sg.GetFileInfo(filename);
03313 }
03314 else
03315 {
03316 PlaybackSock *slave = GetMediaServerByHostname(wantHost);
03317 if (slave)
03318 {
03319 LOG(VB_FILE, LOG_INFO, "HandleSGFileQuery: Getting remote info");
03320 strList = slave->GetSGFileQuery(wantHost, groupname, filename);
03321 slave->DownRef();
03322 slaveUnreachable = false;
03323 }
03324 else
03325 {
03326 LOG(VB_FILE, LOG_INFO,
03327 QString("HandleSGFileQuery: Failed to grab slave socket : %1 :")
03328 .arg(wantHost));
03329 slaveUnreachable = true;
03330 }
03331
03332 }
03333
03334 if (slaveUnreachable)
03335 strList << "SLAVE UNREACHABLE: " << wantHost;
03336
03337 if (strList.count() == 0 || (strList.at(0) == "0"))
03338 strList << "EMPTY LIST";
03339
03340 SendResponse(pbssock, strList);
03341 }
03342
03343 void MainServer::HandleLockTuner(PlaybackSock *pbs, int cardid)
03344 {
03345 MythSocket *pbssock = pbs->getSocket();
03346 QString pbshost = pbs->getHostname();
03347
03348 QStringList strlist;
03349 int retval;
03350
03351 EncoderLink *encoder = NULL;
03352 QString enchost;
03353
03354 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
03355 for (; iter != encoderList->end(); ++iter)
03356 {
03357 EncoderLink *elink = *iter;
03358
03359
03360 if ((cardid != -1) && (cardid != elink->GetCardID()))
03361 continue;
03362
03363 if (elink->IsLocal())
03364 enchost = gCoreContext->GetHostName();
03365 else
03366 enchost = elink->GetHostName();
03367
03368 if ((enchost == pbshost) &&
03369 (elink->IsConnected()) &&
03370 (!elink->IsBusy()) &&
03371 (!elink->IsTunerLocked()))
03372 {
03373 encoder = elink;
03374 break;
03375 }
03376 }
03377
03378 if (encoder)
03379 {
03380 retval = encoder->LockTuner();
03381
03382 if (retval != -1)
03383 {
03384 QString msg = QString("Cardid %1 LOCKed for external use on %2.")
03385 .arg(retval).arg(pbshost);
03386 LOG(VB_GENERAL, LOG_INFO, msg);
03387
03388 MSqlQuery query(MSqlQuery::InitCon());
03389 query.prepare("SELECT videodevice, audiodevice, "
03390 "vbidevice "
03391 "FROM capturecard "
03392 "WHERE cardid = :CARDID ;");
03393 query.bindValue(":CARDID", retval);
03394
03395 if (query.exec() && query.next())
03396 {
03397
03398 strlist << QString::number(retval)
03399 << query.value(0).toString()
03400 << query.value(1).toString()
03401 << query.value(2).toString();
03402
03403 if (m_sched)
03404 m_sched->ReschedulePlace("LockTuner");
03405
03406 SendResponse(pbssock, strlist);
03407 return;
03408 }
03409 else
03410 LOG(VB_GENERAL, LOG_ERR,
03411 "MainServer::LockTuner(): Could not find "
03412 "card info in database");
03413 }
03414 else
03415 {
03416
03417 strlist << "-2" << "" << "" << "";
03418 SendResponse(pbssock, strlist);
03419 return;
03420 }
03421 }
03422
03423 strlist << "-1" << "" << "" << "";
03424 SendResponse(pbssock, strlist);
03425 }
03426
03427 void MainServer::HandleFreeTuner(int cardid, PlaybackSock *pbs)
03428 {
03429 MythSocket *pbssock = pbs->getSocket();
03430 QStringList strlist;
03431 EncoderLink *encoder = NULL;
03432
03433 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(cardid);
03434 if (iter == encoderList->end())
03435 {
03436 LOG(VB_GENERAL, LOG_ERR, "MainServer::HandleFreeTuner() " +
03437 QString("Unknown encoder: %1").arg(cardid));
03438 strlist << "FAILED";
03439 }
03440 else
03441 {
03442 encoder = *iter;
03443 encoder->FreeTuner();
03444
03445 QString msg = QString("Cardid %1 FREED from external use on %2.")
03446 .arg(cardid).arg(pbs->getHostname());
03447 LOG(VB_GENERAL, LOG_INFO, msg);
03448
03449 if (m_sched)
03450 m_sched->ReschedulePlace("FreeTuner");
03451
03452 strlist << "OK";
03453 }
03454
03455 SendResponse(pbssock, strlist);
03456 }
03457
03458 void MainServer::HandleGetFreeRecorder(PlaybackSock *pbs)
03459 {
03460 MythSocket *pbssock = pbs->getSocket();
03461 QString pbshost = pbs->getHostname();
03462
03463 vector<uint> excluded_cardids;
03464 QStringList strlist;
03465 int retval = -1;
03466
03467 EncoderLink *encoder = NULL;
03468 QString enchost;
03469 uint bestorder = 0;
03470
03471 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
03472 for (; iter != encoderList->end(); ++iter)
03473 {
03474 EncoderLink *elink = *iter;
03475
03476 if (elink->IsLocal())
03477 enchost = gCoreContext->GetHostName();
03478 else
03479 enchost = elink->GetHostName();
03480
03481 LOG(VB_RECORD, LOG_INFO,
03482 QString("Checking card %1. Best card so far %2")
03483 .arg(iter.key()).arg(retval));
03484
03485 if (!elink->IsConnected() || elink->IsTunerLocked())
03486 continue;
03487
03488 vector<InputInfo> inputs = elink->GetFreeInputs(excluded_cardids);
03489
03490 for (uint i = 0; i < inputs.size(); ++i)
03491 {
03492 if (!encoder || inputs[i].livetvorder < bestorder)
03493 {
03494 retval = iter.key();
03495 encoder = elink;
03496 bestorder = inputs[i].livetvorder;
03497 }
03498 }
03499 }
03500
03501 LOG(VB_RECORD, LOG_INFO,
03502 QString("Best card is %1").arg(retval));
03503
03504 strlist << QString::number(retval);
03505
03506 if (encoder)
03507 {
03508 if (encoder->IsLocal())
03509 {
03510 strlist << gCoreContext->GetBackendServerIP();
03511 strlist << gCoreContext->GetSetting("BackendServerPort");
03512 }
03513 else
03514 {
03515 strlist << gCoreContext->GetBackendServerIP(encoder->GetHostName());
03516 strlist << gCoreContext->GetSettingOnHost("BackendServerPort",
03517 encoder->GetHostName(), "-1");
03518 }
03519 }
03520 else
03521 {
03522 strlist << "nohost";
03523 strlist << "-1";
03524 }
03525
03526 SendResponse(pbssock, strlist);
03527 }
03528
03529 void MainServer::HandleGetFreeRecorderCount(PlaybackSock *pbs)
03530 {
03531 MythSocket *pbssock = pbs->getSocket();
03532
03533 vector<uint> excluded_cardids;
03534 QStringList strlist;
03535 int count = 0;
03536
03537 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
03538 for (; iter != encoderList->end(); ++iter)
03539 {
03540 EncoderLink *elink = *iter;
03541
03542 if (elink->IsConnected() && !elink->IsTunerLocked() &&
03543 !elink->GetFreeInputs(excluded_cardids).empty())
03544 {
03545 count++;
03546 }
03547 }
03548
03549 strlist << QString::number(count);
03550
03551 SendResponse(pbssock, strlist);
03552 }
03553
03554 static bool comp_livetvorder(const InputInfo &a, const InputInfo &b)
03555 {
03556 return a.livetvorder < b.livetvorder;
03557 }
03558
03559 void MainServer::HandleGetFreeRecorderList(PlaybackSock *pbs)
03560 {
03561 MythSocket *pbssock = pbs->getSocket();
03562
03563 vector<uint> excluded_cardids;
03564 vector<InputInfo> allinputs;
03565
03566 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
03567 for (; iter != encoderList->end(); ++iter)
03568 {
03569 EncoderLink *elink = *iter;
03570
03571 if (!elink->IsConnected() || elink->IsTunerLocked())
03572 continue;
03573
03574 vector<InputInfo> inputs = elink->GetFreeInputs(excluded_cardids);
03575 allinputs.insert(allinputs.end(), inputs.begin(), inputs.end());
03576 }
03577
03578 stable_sort(allinputs.begin(), allinputs.end(), comp_livetvorder);
03579
03580 QStringList strlist;
03581 QMap<int, bool> cardidused;
03582 for (uint i = 0; i < allinputs.size(); ++i)
03583 {
03584 uint cardid = allinputs[i].cardid;
03585 if (!cardidused[cardid])
03586 {
03587 strlist << QString::number(cardid);
03588 cardidused[cardid] = true;
03589 }
03590 }
03591
03592 if (strlist.size() == 0)
03593 strlist << "0";
03594
03595 SendResponse(pbssock, strlist);
03596 }
03597
03598 void MainServer::HandleGetNextFreeRecorder(QStringList &slist,
03599 PlaybackSock *pbs)
03600 {
03601 MythSocket *pbssock = pbs->getSocket();
03602 QString pbshost = pbs->getHostname();
03603
03604 QStringList strlist;
03605 int retval = -1;
03606 int currrec = slist[1].toInt();
03607
03608 EncoderLink *encoder = NULL;
03609 QString enchost;
03610
03611 LOG(VB_RECORD, LOG_INFO, QString("Getting next free recorder after : %1")
03612 .arg(currrec));
03613
03614
03615 QMap<int, EncoderLink *>::Iterator iter, curr = encoderList->find(currrec);
03616
03617 if (currrec > 0 && curr != encoderList->end())
03618 {
03619 vector<uint> excluded_cardids;
03620 excluded_cardids.push_back(currrec);
03621
03622
03623 for (iter = curr;;)
03624 {
03625 EncoderLink *elink;
03626
03627
03628 if (++iter == encoderList->end())
03629 {
03630 iter = encoderList->begin();
03631 }
03632
03633 elink = *iter;
03634
03635 if (retval == -1 && elink->IsConnected() &&
03636 !elink->IsTunerLocked() &&
03637 !elink->GetFreeInputs(excluded_cardids).empty())
03638 {
03639 encoder = elink;
03640 retval = iter.key();
03641 }
03642
03643
03644 if (iter == curr)
03645 break;
03646 }
03647 }
03648 else
03649 {
03650 HandleGetFreeRecorder(pbs);
03651 return;
03652 }
03653
03654
03655 strlist << QString::number(retval);
03656
03657 if (encoder)
03658 {
03659 if (encoder->IsLocal())
03660 {
03661 strlist << gCoreContext->GetBackendServerIP();
03662 strlist << gCoreContext->GetSetting("BackendServerPort");
03663 }
03664 else
03665 {
03666 strlist << gCoreContext->GetBackendServerIP(encoder->GetHostName());
03667 strlist << gCoreContext->GetSettingOnHost("BackendServerPort",
03668 encoder->GetHostName(), "-1");
03669 }
03670 }
03671 else
03672 {
03673 strlist << "nohost";
03674 strlist << "-1";
03675 }
03676
03677 SendResponse(pbssock, strlist);
03678 }
03679
03680 static QString cleanup(const QString &str)
03681 {
03682 if (str == " ")
03683 return "";
03684 return str;
03685 }
03686
03687 static QString make_safe(const QString &str)
03688 {
03689 if (str.isEmpty())
03690 return " ";
03691 return str;
03692 }
03693
03694 void MainServer::HandleRecorderQuery(QStringList &slist, QStringList &commands,
03695 PlaybackSock *pbs)
03696 {
03697 MythSocket *pbssock = pbs->getSocket();
03698
03699 if (commands.size() < 2 || slist.size() < 2)
03700 return;
03701
03702 int recnum = commands[1].toInt();
03703
03704 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recnum);
03705 if (iter == encoderList->end())
03706 {
03707 LOG(VB_GENERAL, LOG_ERR, "MainServer::HandleRecorderQuery() " +
03708 QString("Unknown encoder: %1").arg(recnum));
03709 QStringList retlist( "bad" );
03710 SendResponse(pbssock, retlist);
03711 return;
03712 }
03713
03714 QString command = slist[1];
03715
03716 QStringList retlist;
03717
03718 EncoderLink *enc = *iter;
03719 if (!enc->IsConnected())
03720 {
03721 LOG(VB_GENERAL, LOG_ERR, " MainServer::HandleRecorderQuery() " +
03722 QString("Command %1 for unconnected encoder %2")
03723 .arg(command).arg(recnum));
03724 retlist << "bad";
03725 SendResponse(pbssock, retlist);
03726 return;
03727 }
03728
03729 if (command == "IS_RECORDING")
03730 {
03731 retlist << QString::number((int)enc->IsReallyRecording());
03732 }
03733 else if (command == "GET_FRAMERATE")
03734 {
03735 retlist << QString::number(enc->GetFramerate());
03736 }
03737 else if (command == "GET_FRAMES_WRITTEN")
03738 {
03739 retlist << QString::number(enc->GetFramesWritten());
03740 }
03741 else if (command == "GET_FILE_POSITION")
03742 {
03743 retlist << QString::number(enc->GetFilePosition());
03744 }
03745 else if (command == "GET_MAX_BITRATE")
03746 {
03747 retlist << QString::number(enc->GetMaxBitrate());
03748 }
03749 else if (command == "GET_CURRENT_RECORDING")
03750 {
03751 ProgramInfo *info = enc->GetRecording();
03752 if (info)
03753 {
03754 info->ToStringList(retlist);
03755 delete info;
03756 }
03757 else
03758 {
03759 ProgramInfo dummy;
03760 dummy.ToStringList(retlist);
03761 }
03762 }
03763 else if (command == "GET_KEYFRAME_POS")
03764 {
03765 long long desired = slist[2].toLongLong();
03766 retlist << QString::number(enc->GetKeyframePosition(desired));
03767 }
03768 else if (command == "FILL_POSITION_MAP")
03769 {
03770 long long start = slist[2].toLongLong();
03771 long long end = slist[3].toLongLong();
03772 frm_pos_map_t map;
03773
03774 if (!enc->GetKeyframePositions(start, end, map))
03775 {
03776 retlist << "error";
03777 }
03778 else
03779 {
03780 frm_pos_map_t::const_iterator it = map.begin();
03781 for (; it != map.end(); ++it)
03782 {
03783 retlist += QString::number(it.key());
03784 retlist += QString::number(*it);
03785 }
03786 if (retlist.empty())
03787 retlist << "ok";
03788 }
03789 }
03790 else if (command == "GET_RECORDING")
03791 {
03792 ProgramInfo *pginfo = enc->GetRecording();
03793 if (pginfo)
03794 {
03795 pginfo->ToStringList(retlist);
03796 delete pginfo;
03797 }
03798 else
03799 {
03800 ProgramInfo dummy;
03801 dummy.ToStringList(retlist);
03802 }
03803 }
03804 else if (command == "FRONTEND_READY")
03805 {
03806 enc->FrontendReady();
03807 retlist << "ok";
03808 }
03809 else if (command == "CANCEL_NEXT_RECORDING")
03810 {
03811 QString cancel = slist[2];
03812 LOG(VB_GENERAL, LOG_NOTICE,
03813 QString("Received: CANCEL_NEXT_RECORDING %1").arg(cancel));
03814 enc->CancelNextRecording(cancel == "1");
03815 retlist << "ok";
03816 }
03817 else if (command == "SPAWN_LIVETV")
03818 {
03819 QString chainid = slist[2];
03820 LiveTVChain *chain = GetExistingChain(chainid);
03821 if (!chain)
03822 {
03823 chain = new LiveTVChain();
03824 chain->LoadFromExistingChain(chainid);
03825 AddToChains(chain);
03826 }
03827
03828 chain->SetHostSocket(pbssock);
03829
03830 enc->SpawnLiveTV(chain, slist[3].toInt(), slist[4]);
03831 retlist << "ok";
03832 }
03833 else if (command == "STOP_LIVETV")
03834 {
03835 QString chainid = enc->GetChainID();
03836 enc->StopLiveTV();
03837
03838 LiveTVChain *chain = GetExistingChain(chainid);
03839 if (chain)
03840 {
03841 chain->DelHostSocket(pbssock);
03842 if (chain->HostSocketCount() == 0)
03843 {
03844 DeleteChain(chain);
03845 }
03846 }
03847
03848 retlist << "ok";
03849 }
03850 else if (command == "PAUSE")
03851 {
03852 enc->PauseRecorder();
03853 retlist << "ok";
03854 }
03855 else if (command == "FINISH_RECORDING")
03856 {
03857 enc->FinishRecording();
03858 retlist << "ok";
03859 }
03860 else if (command == "SET_LIVE_RECORDING")
03861 {
03862 int recording = slist[2].toInt();
03863 enc->SetLiveRecording(recording);
03864 retlist << "ok";
03865 }
03866 else if (command == "GET_FREE_INPUTS")
03867 {
03868 vector<uint> excluded_cardids;
03869 for (int i = 2; i < slist.size(); i++)
03870 excluded_cardids.push_back(slist[i].toUInt());
03871
03872 vector<InputInfo> inputs = enc->GetFreeInputs(excluded_cardids);
03873
03874 if (inputs.empty())
03875 retlist << "EMPTY_LIST";
03876 else
03877 {
03878 for (uint i = 0; i < inputs.size(); i++)
03879 inputs[i].ToStringList(retlist);
03880 }
03881 }
03882 else if (command == "GET_INPUT")
03883 {
03884 QString ret = enc->GetInput();
03885 ret = (ret.isEmpty()) ? "UNKNOWN" : ret;
03886 retlist << ret;
03887 }
03888 else if (command == "SET_INPUT")
03889 {
03890 QString input = slist[2];
03891 QString ret = enc->SetInput(input);
03892 ret = (ret.isEmpty()) ? "UNKNOWN" : ret;
03893 retlist << ret;
03894 }
03895 else if (command == "TOGGLE_CHANNEL_FAVORITE")
03896 {
03897 QString changroup = slist[2];
03898 enc->ToggleChannelFavorite(changroup);
03899 retlist << "ok";
03900 }
03901 else if (command == "CHANGE_CHANNEL")
03902 {
03903 ChannelChangeDirection direction =
03904 (ChannelChangeDirection) slist[2].toInt();
03905 enc->ChangeChannel(direction);
03906 retlist << "ok";
03907 }
03908 else if (command == "SET_CHANNEL")
03909 {
03910 QString name = slist[2];
03911 enc->SetChannel(name);
03912 retlist << "ok";
03913 }
03914 else if (command == "SET_SIGNAL_MONITORING_RATE")
03915 {
03916 int rate = slist[2].toInt();
03917 int notifyFrontend = slist[3].toInt();
03918 int oldrate = enc->SetSignalMonitoringRate(rate, notifyFrontend);
03919 retlist << QString::number(oldrate);
03920 }
03921 else if (command == "GET_COLOUR")
03922 {
03923 int ret = enc->GetPictureAttribute(kPictureAttribute_Colour);
03924 retlist << QString::number(ret);
03925 }
03926 else if (command == "GET_CONTRAST")
03927 {
03928 int ret = enc->GetPictureAttribute(kPictureAttribute_Contrast);
03929 retlist << QString::number(ret);
03930 }
03931 else if (command == "GET_BRIGHTNESS")
03932 {
03933 int ret = enc->GetPictureAttribute(kPictureAttribute_Brightness);
03934 retlist << QString::number(ret);
03935 }
03936 else if (command == "GET_HUE")
03937 {
03938 int ret = enc->GetPictureAttribute(kPictureAttribute_Hue);
03939 retlist << QString::number(ret);
03940 }
03941 else if (command == "CHANGE_COLOUR")
03942 {
03943 int type = slist[2].toInt();
03944 bool up = slist[3].toInt();
03945 int ret = enc->ChangePictureAttribute(
03946 (PictureAdjustType) type, kPictureAttribute_Colour, up);
03947 retlist << QString::number(ret);
03948 }
03949 else if (command == "CHANGE_CONTRAST")
03950 {
03951 int type = slist[2].toInt();
03952 bool up = slist[3].toInt();
03953 int ret = enc->ChangePictureAttribute(
03954 (PictureAdjustType) type, kPictureAttribute_Contrast, up);
03955 retlist << QString::number(ret);
03956 }
03957 else if (command == "CHANGE_BRIGHTNESS")
03958 {
03959 int type= slist[2].toInt();
03960 bool up = slist[3].toInt();
03961 int ret = enc->ChangePictureAttribute(
03962 (PictureAdjustType) type, kPictureAttribute_Brightness, up);
03963 retlist << QString::number(ret);
03964 }
03965 else if (command == "CHANGE_HUE")
03966 {
03967 int type= slist[2].toInt();
03968 bool up = slist[3].toInt();
03969 int ret = enc->ChangePictureAttribute(
03970 (PictureAdjustType) type, kPictureAttribute_Hue, up);
03971 retlist << QString::number(ret);
03972 }
03973 else if (command == "CHECK_CHANNEL")
03974 {
03975 QString name = slist[2];
03976 retlist << QString::number((int)(enc->CheckChannel(name)));
03977 }
03978 else if (command == "SHOULD_SWITCH_CARD")
03979 {
03980 QString chanid = slist[2];
03981 retlist << QString::number((int)(enc->ShouldSwitchToAnotherCard(chanid)));
03982 }
03983 else if (command == "CHECK_CHANNEL_PREFIX")
03984 {
03985 QString needed_spacer;
03986 QString prefix = slist[2];
03987 uint is_complete_valid_channel_on_rec = 0;
03988 bool is_extra_char_useful = false;
03989
03990 bool match = enc->CheckChannelPrefix(
03991 prefix, is_complete_valid_channel_on_rec,
03992 is_extra_char_useful, needed_spacer);
03993
03994 retlist << QString::number((int)match);
03995 retlist << QString::number(is_complete_valid_channel_on_rec);
03996 retlist << QString::number((int)is_extra_char_useful);
03997 retlist << ((needed_spacer.isEmpty()) ? QString("X") : needed_spacer);
03998 }
03999 else if (command == "GET_NEXT_PROGRAM_INFO" && (slist.size() >= 6))
04000 {
04001 QString channelname = slist[2];
04002 uint chanid = slist[3].toUInt();
04003 BrowseDirection direction = (BrowseDirection)slist[4].toInt();
04004 QString starttime = slist[5];
04005
04006 QString title = "", subtitle = "", desc = "", category = "";
04007 QString endtime = "", callsign = "", iconpath = "";
04008 QString seriesid = "", programid = "";
04009
04010 enc->GetNextProgram(direction,
04011 title, subtitle, desc, category, starttime,
04012 endtime, callsign, iconpath, channelname, chanid,
04013 seriesid, programid);
04014
04015 retlist << make_safe(title);
04016 retlist << make_safe(subtitle);
04017 retlist << make_safe(desc);
04018 retlist << make_safe(category);
04019 retlist << make_safe(starttime);
04020 retlist << make_safe(endtime);
04021 retlist << make_safe(callsign);
04022 retlist << make_safe(iconpath);
04023 retlist << make_safe(channelname);
04024 retlist << QString::number(chanid);
04025 retlist << make_safe(seriesid);
04026 retlist << make_safe(programid);
04027 }
04028 else if (command == "GET_CHANNEL_INFO")
04029 {
04030 uint chanid = slist[2].toUInt();
04031 uint sourceid = 0;
04032 QString callsign = "", channum = "", channame = "", xmltv = "";
04033
04034 enc->GetChannelInfo(chanid, sourceid,
04035 callsign, channum, channame, xmltv);
04036
04037 retlist << QString::number(chanid);
04038 retlist << QString::number(sourceid);
04039 retlist << make_safe(callsign);
04040 retlist << make_safe(channum);
04041 retlist << make_safe(channame);
04042 retlist << make_safe(xmltv);
04043 }
04044 else
04045 {
04046 LOG(VB_GENERAL, LOG_ERR, QString("Unknown command: %1").arg(command));
04047 retlist << "ok";
04048 }
04049
04050 SendResponse(pbssock, retlist);
04051 }
04052
04053 void MainServer::HandleSetNextLiveTVDir(QStringList &commands,
04054 PlaybackSock *pbs)
04055 {
04056 MythSocket *pbssock = pbs->getSocket();
04057
04058 int recnum = commands[1].toInt();
04059
04060 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recnum);
04061 if (iter == encoderList->end())
04062 {
04063 LOG(VB_GENERAL, LOG_ERR, "MainServer::HandleSetNextLiveTVDir() " +
04064 QString("Unknown encoder: %1").arg(recnum));
04065 QStringList retlist( "bad" );
04066 SendResponse(pbssock, retlist);
04067 return;
04068 }
04069
04070 EncoderLink *enc = *iter;
04071 enc->SetNextLiveTVDir(commands[2]);
04072
04073 QStringList retlist( "OK" );
04074 SendResponse(pbssock, retlist);
04075 }
04076
04077 void MainServer::HandleSetChannelInfo(QStringList &slist, PlaybackSock *pbs)
04078 {
04079 bool ok = true;
04080 MythSocket *pbssock = pbs->getSocket();
04081 uint chanid = slist[1].toUInt();
04082 uint sourceid = slist[2].toUInt();
04083 QString oldcnum = cleanup(slist[3]);
04084 QString callsign = cleanup(slist[4]);
04085 QString channum = cleanup(slist[5]);
04086 QString channame = cleanup(slist[6]);
04087 QString xmltv = cleanup(slist[7]);
04088
04089 QStringList retlist;
04090 if (!chanid || !sourceid)
04091 {
04092 retlist << "0";
04093 SendResponse(pbssock, retlist);
04094 return;
04095 }
04096
04097 QMap<int, EncoderLink *>::iterator it = encoderList->begin();
04098 for (; it != encoderList->end(); ++it)
04099 {
04100 if (*it)
04101 {
04102 ok &= (*it)->SetChannelInfo(chanid, sourceid, oldcnum,
04103 callsign, channum, channame, xmltv);
04104 }
04105 }
04106
04107 retlist << ((ok) ? "1" : "0");
04108 SendResponse(pbssock, retlist);
04109 }
04110
04111 void MainServer::HandleRemoteEncoder(QStringList &slist, QStringList &commands,
04112 PlaybackSock *pbs)
04113 {
04114 MythSocket *pbssock = pbs->getSocket();
04115
04116 int recnum = commands[1].toInt();
04117 QStringList retlist;
04118
04119 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recnum);
04120 if (iter == encoderList->end())
04121 {
04122 LOG(VB_GENERAL, LOG_ERR, "MainServer: " +
04123 QString("HandleRemoteEncoder(cmd %1) ").arg(slist[1]) +
04124 QString("Unknown encoder: %1").arg(recnum));
04125 retlist << QString::number((int) kState_Error);
04126 SendResponse(pbssock, retlist);
04127 return;
04128 }
04129
04130 EncoderLink *enc = *iter;
04131
04132 QString command = slist[1];
04133
04134 if (command == "GET_STATE")
04135 {
04136 retlist << QString::number((int)enc->GetState());
04137 }
04138 else if (command == "GET_SLEEPSTATUS")
04139 {
04140 retlist << QString::number(enc->GetSleepStatus());
04141 }
04142 else if (command == "GET_FLAGS")
04143 {
04144 retlist << QString::number(enc->GetFlags());
04145 }
04146 else if (command == "IS_BUSY")
04147 {
04148 int time_buffer = (slist.size() >= 3) ? slist[2].toInt() : 5;
04149 TunedInputInfo busy_input;
04150 retlist << QString::number((int)enc->IsBusy(&busy_input, time_buffer));
04151 busy_input.ToStringList(retlist);
04152 }
04153 else if (command == "MATCHES_RECORDING" &&
04154 slist.size() >= (2 + NUMPROGRAMLINES))
04155 {
04156 QStringList::const_iterator it = slist.begin() + 2;
04157 ProgramInfo pginfo(it, slist.end());
04158
04159 retlist << QString::number((int)enc->MatchesRecording(&pginfo));
04160 }
04161 else if (command == "START_RECORDING" &&
04162 slist.size() >= (2 + NUMPROGRAMLINES))
04163 {
04164 QStringList::const_iterator it = slist.begin() + 2;
04165 ProgramInfo pginfo(it, slist.end());
04166
04167 retlist << QString::number(enc->StartRecording(&pginfo));
04168 }
04169 else if (command == "GET_RECORDING_STATUS")
04170 {
04171 retlist << QString::number((int)enc->GetRecordingStatus());
04172 }
04173 else if (command == "RECORD_PENDING" &&
04174 (slist.size() >= 4 + NUMPROGRAMLINES))
04175 {
04176 int secsleft = slist[2].toInt();
04177 int haslater = slist[3].toInt();
04178 QStringList::const_iterator it = slist.begin() + 4;
04179 ProgramInfo pginfo(it, slist.end());
04180
04181 enc->RecordPending(&pginfo, secsleft, haslater);
04182
04183 retlist << "OK";
04184 }
04185 else if (command == "CANCEL_NEXT_RECORDING" &&
04186 (slist.size() >= 3))
04187 {
04188 bool cancel = (bool) slist[2].toInt();
04189 enc->CancelNextRecording(cancel);
04190 retlist << "OK";
04191 }
04192 else if (command == "STOP_RECORDING")
04193 {
04194 enc->StopRecording();
04195 retlist << "OK";
04196 }
04197 else if (command == "GET_MAX_BITRATE")
04198 {
04199 retlist << QString::number(enc->GetMaxBitrate());
04200 }
04201 else if (command == "GET_CURRENT_RECORDING")
04202 {
04203 ProgramInfo *info = enc->GetRecording();
04204 if (info)
04205 {
04206 info->ToStringList(retlist);
04207 delete info;
04208 }
04209 else
04210 {
04211 ProgramInfo dummy;
04212 dummy.ToStringList(retlist);
04213 }
04214 }
04215 else if (command == "GET_FREE_INPUTS")
04216 {
04217 vector<uint> excluded_cardids;
04218 for (int i = 2; i < slist.size(); i++)
04219 excluded_cardids.push_back(slist[i].toUInt());
04220
04221 vector<InputInfo> inputs = enc->GetFreeInputs(excluded_cardids);
04222
04223 if (inputs.empty())
04224 retlist << "EMPTY_LIST";
04225 else
04226 {
04227 for (uint i = 0; i < inputs.size(); i++)
04228 inputs[i].ToStringList(retlist);
04229 }
04230 }
04231
04232 SendResponse(pbssock, retlist);
04233 }
04234
04235 void MainServer::GetActiveBackends(QStringList &hosts)
04236 {
04237 hosts.clear();
04238 hosts << gCoreContext->GetHostName();
04239
04240 QString hostname;
04241 QReadLocker rlock(&sockListLock);
04242 vector<PlaybackSock*>::iterator it;
04243 for (it = playbackList.begin(); it != playbackList.end(); ++it)
04244 {
04245 if ((*it)->isMediaServer())
04246 {
04247 hostname = (*it)->getHostname();
04248 if (!hosts.contains(hostname))
04249 hosts << hostname;
04250 }
04251 }
04252 }
04253
04254 void MainServer::HandleActiveBackendsQuery(PlaybackSock *pbs)
04255 {
04256 QStringList retlist;
04257 GetActiveBackends(retlist);
04258 retlist.push_front(QString::number(retlist.size()));
04259 SendResponse(pbs->getSocket(), retlist);
04260 }
04261
04262 void MainServer::HandleIsActiveBackendQuery(QStringList &slist,
04263 PlaybackSock *pbs)
04264 {
04265 QStringList retlist;
04266 QString queryhostname = slist[1];
04267
04268 if (gCoreContext->GetHostName() != queryhostname)
04269 {
04270 PlaybackSock *slave = GetSlaveByHostname(queryhostname);
04271 if (slave != NULL)
04272 {
04273 retlist << "TRUE";
04274 slave->DownRef();
04275 }
04276 else
04277 retlist << "FALSE";
04278 }
04279 else
04280 retlist << "TRUE";
04281
04282 SendResponse(pbs->getSocket(), retlist);
04283 }
04284
04285 int MainServer::GetfsID(QList<FileSystemInfo>::iterator fsInfo)
04286 {
04287 QString fskey = fsInfo->getHostname() + ":" + fsInfo->getPath();
04288 QMutexLocker lock(&fsIDcacheLock);
04289 if (!fsIDcache.contains(fskey))
04290 fsIDcache[fskey] = fsIDcache.count();
04291
04292 return fsIDcache[fskey];
04293 }
04294
04295 size_t MainServer::GetCurrentMaxBitrate(void)
04296 {
04297 size_t totalKBperMin = 0;
04298
04299 QMap<int, EncoderLink*>::iterator it = encoderList->begin();
04300 for (; it != encoderList->end(); ++it)
04301 {
04302 EncoderLink *enc = *it;
04303
04304 if (!enc->IsConnected() || !enc->IsBusy())
04305 continue;
04306
04307 long long maxBitrate = enc->GetMaxBitrate();
04308 if (maxBitrate<=0)
04309 maxBitrate = 19500000LL;
04310 long long thisKBperMin = (((size_t)maxBitrate)*((size_t)15))>>11;
04311 totalKBperMin += thisKBperMin;
04312 LOG(VB_FILE, LOG_INFO, QString("Cardid %1: max bitrate %2 KB/min")
04313 .arg(enc->GetCardID()).arg(thisKBperMin));
04314 }
04315
04316 LOG(VB_FILE, LOG_INFO,
04317 QString("Maximal bitrate of busy encoders is %1 KB/min")
04318 .arg(totalKBperMin));
04319
04320 return totalKBperMin;
04321 }
04322
04323 void MainServer::BackendQueryDiskSpace(QStringList &strlist, bool consolidated,
04324 bool allHosts)
04325 {
04326 QString allHostList = gCoreContext->GetHostName();
04327 int64_t totalKB = -1, usedKB = -1;
04328 QMap <QString, bool>foundDirs;
04329 QString driveKey;
04330 QString localStr = "1";
04331 struct statfs statbuf;
04332 QStringList groups(StorageGroup::kSpecialGroups);
04333 groups.removeAll("LiveTV");
04334 QString specialGroups = groups.join("', '");
04335 QString sql = QString("SELECT MIN(id),dirname "
04336 "FROM storagegroup "
04337 "WHERE hostname = :HOSTNAME "
04338 "AND groupname NOT IN ( '%1' ) "
04339 "GROUP BY dirname;").arg(specialGroups);
04340 MSqlQuery query(MSqlQuery::InitCon());
04341 query.prepare(sql);
04342 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
04343
04344 if (query.exec())
04345 {
04346
04347
04348 if (!query.size())
04349 {
04350 query.prepare("SELECT MIN(id),dirname "
04351 "FROM storagegroup "
04352 "WHERE groupname = :GROUP "
04353 "GROUP BY dirname;");
04354 query.bindValue(":GROUP", "Default");
04355 if (!query.exec())
04356 MythDB::DBError("BackendQueryDiskSpace", query);
04357 }
04358
04359 QDir checkDir("");
04360 QString dirID;
04361 QString currentDir;
04362 int bSize;
04363 while (query.next())
04364 {
04365 dirID = query.value(0).toString();
04366
04367
04368
04369 currentDir = QString::fromUtf8(query.value(1)
04370 .toByteArray().constData());
04371 if (currentDir.right(1) == "/")
04372 currentDir.remove(currentDir.length() - 1, 1);
04373
04374 checkDir.setPath(currentDir);
04375 if (!foundDirs.contains(currentDir))
04376 {
04377 if (checkDir.exists())
04378 {
04379 QByteArray cdir = currentDir.toAscii();
04380 getDiskSpace(cdir.constData(), totalKB, usedKB);
04381 memset(&statbuf, 0, sizeof(statbuf));
04382 localStr = "1";
04383 bSize = 0;
04384
04385 if (!statfs(currentDir.toLocal8Bit().constData(), &statbuf))
04386 {
04387 #if CONFIG_DARWIN
04388 char *fstypename = statbuf.f_fstypename;
04389 if ((!strcmp(fstypename, "nfs")) ||
04390 (!strcmp(fstypename, "afpfs")) ||
04391 (!strcmp(fstypename, "smbfs")))
04392 localStr = "0";
04393 #elif __linux__
04394 long fstype = statbuf.f_type;
04395 if ((fstype == 0x6969) ||
04396 (fstype == 0x517B) ||
04397 (fstype == (long)0xFF534D42))
04398 localStr = "0";
04399 #endif
04400 bSize = statbuf.f_bsize;
04401 }
04402
04403 strlist << gCoreContext->GetHostName();
04404 strlist << currentDir;
04405 strlist << localStr;
04406 strlist << "-1";
04407 strlist << dirID;
04408 strlist << QString::number(bSize);
04409 strlist << QString::number(totalKB);
04410 strlist << QString::number(usedKB);
04411
04412 foundDirs[currentDir] = true;
04413 }
04414 else
04415 foundDirs[currentDir] = false;
04416 }
04417 }
04418 }
04419
04420 if (allHosts)
04421 {
04422 QMap <QString, bool> backendsCounted;
04423 QString pbsHost;
04424
04425 list<PlaybackSock *> localPlaybackList;
04426
04427 sockListLock.lockForRead();
04428
04429 vector<PlaybackSock *>::iterator pbsit = playbackList.begin();
04430 for (; pbsit != playbackList.end(); ++pbsit)
04431 {
04432 PlaybackSock *pbs = *pbsit;
04433
04434 if ((pbs->IsDisconnected()) ||
04435 (!pbs->isMediaServer()) ||
04436 (pbs->isLocal()) ||
04437 (backendsCounted.contains(pbs->getHostname())))
04438 continue;
04439
04440 backendsCounted[pbs->getHostname()] = true;
04441 pbs->UpRef();
04442 localPlaybackList.push_back(pbs);
04443 allHostList += "," + pbs->getHostname();
04444 }
04445
04446 sockListLock.unlock();
04447
04448 for (list<PlaybackSock *>::iterator p = localPlaybackList.begin() ;
04449 p != localPlaybackList.end() ; ++p) {
04450 (*p)->GetDiskSpace(strlist);
04451 (*p)->DownRef();
04452 }
04453 }
04454
04455 if (!consolidated)
04456 return;
04457
04458 FileSystemInfo fsInfo;
04459 QList<FileSystemInfo> fsInfos;
04460
04461 QStringList::const_iterator it = strlist.begin();
04462 while (it != strlist.end())
04463 {
04464 fsInfo.setHostname(*(it++));
04465 fsInfo.setPath(*(it++));
04466 fsInfo.setLocal((*(it++)).toInt() > 0);
04467 fsInfo.setFSysID(-1);
04468 ++it;
04469 fsInfo.setGroupID((*(it++)).toInt());
04470 fsInfo.setBlockSize((*(it++)).toInt());
04471 fsInfo.setTotalSpace((*(it++)).toLongLong());
04472 fsInfo.setUsedSpace((*(it++)).toLongLong());
04473 fsInfos.push_back(fsInfo);
04474 }
04475 strlist.clear();
04476
04477
04478 int64_t maxWriteFiveSec = GetCurrentMaxBitrate()/12 ;
04479 maxWriteFiveSec = max((int64_t)2048, maxWriteFiveSec);
04480 QList<FileSystemInfo>::iterator it1, it2;
04481 int bSize = 32;
04482 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
04483 {
04484 if (it1->getFSysID() == -1)
04485 {
04486 it1->setFSysID(GetfsID(it1));
04487 it1->setPath(
04488 it1->getHostname().section(".", 0, 0) + ":" + it1->getPath());
04489 }
04490
04491 for (it2 = it1 + 1; it2 != fsInfos.end(); ++it2)
04492 {
04493
04494
04495 bSize = max(32, max(it1->getBlockSize(), it2->getBlockSize()) / 1024);
04496 int64_t diffSize = it1->getTotalSpace() - it2->getTotalSpace();
04497 int64_t diffUsed = it1->getUsedSpace() - it2->getUsedSpace();
04498 if (diffSize < 0)
04499 diffSize = 0 - diffSize;
04500 if (diffUsed < 0)
04501 diffUsed = 0 - diffUsed;
04502
04503 if (it2->getFSysID() == -1 && (diffSize <= bSize) &&
04504 (diffUsed <= maxWriteFiveSec))
04505 {
04506 if (!it1->getHostname().contains(it2->getHostname()))
04507 it1->setHostname(it1->getHostname() + "," + it2->getHostname());
04508 it1->setPath(it1->getPath() + "," +
04509 it2->getHostname().section(".", 0, 0) + ":" + it2->getPath());
04510 fsInfos.erase(it2);
04511 it2 = it1;
04512 }
04513 }
04514 }
04515
04516
04517 totalKB = 0;
04518 usedKB = 0;
04519 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
04520 {
04521 strlist << it1->getHostname();
04522 strlist << it1->getPath();
04523 strlist << QString::number(it1->isLocal());
04524 strlist << QString::number(it1->getFSysID());
04525 strlist << QString::number(it1->getGroupID());
04526 strlist << QString::number(it1->getBlockSize());
04527 strlist << QString::number(it1->getTotalSpace());
04528 strlist << QString::number(it1->getUsedSpace());
04529
04530 totalKB += it1->getTotalSpace();
04531 usedKB += it1->getUsedSpace();
04532 }
04533
04534 if (allHosts)
04535 {
04536 strlist << allHostList;
04537 strlist << "TotalDiskSpace";
04538 strlist << "0";
04539 strlist << "-2";
04540 strlist << "-2";
04541 strlist << "0";
04542 strlist << QString::number(totalKB);
04543 strlist << QString::number(usedKB);
04544 }
04545 }
04546
04547 void MainServer::GetFilesystemInfos(QList<FileSystemInfo> &fsInfos)
04548 {
04549 QStringList strlist;
04550 FileSystemInfo fsInfo;
04551
04552 fsInfos.clear();
04553
04554 BackendQueryDiskSpace(strlist, false, true);
04555
04556 QStringList::const_iterator it = strlist.begin();
04557 while (it != strlist.end())
04558 {
04559 fsInfo.setHostname(*(it++));
04560 fsInfo.setPath(*(it++));
04561 fsInfo.setLocal((*(it++)).toInt() > 0);
04562 fsInfo.setFSysID(-1);
04563 ++it;
04564 fsInfo.setGroupID((*(it++)).toInt());
04565 fsInfo.setBlockSize((*(it++)).toInt());
04566 fsInfo.setTotalSpace((*(it++)).toLongLong());
04567 fsInfo.setUsedSpace((*(it++)).toLongLong());
04568 fsInfo.setWeight(0);
04569 fsInfos.push_back(fsInfo);
04570 }
04571
04572 LOG(VB_SCHEDULE | VB_FILE, LOG_DEBUG, "Determining unique filesystems");
04573 size_t maxWriteFiveSec = GetCurrentMaxBitrate()/12 ;
04574
04575 maxWriteFiveSec = max((size_t)2048, maxWriteFiveSec);
04576
04577 FileSystemInfo::Consolidate(fsInfos, false, maxWriteFiveSec);
04578
04579 QList<FileSystemInfo>::iterator it1;
04580 if (VERBOSE_LEVEL_CHECK(VB_FILE | VB_SCHEDULE, LOG_INFO))
04581 {
04582 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
04583 "--- GetFilesystemInfos directory list start ---");
04584 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
04585 {
04586 QString msg = QString("Dir: %1:%2")
04587 .arg(it1->getHostname()).arg(it1->getPath());
04588 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, msg) ;
04589 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" Location: %1")
04590 .arg(it1->isLocal() ? "Local" : "Remote"));
04591 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" fsID : %1")
04592 .arg(it1->getFSysID()));
04593 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" dirID : %1")
04594 .arg(it1->getGroupID()));
04595 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" BlkSize : %1")
04596 .arg(it1->getBlockSize()));
04597 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" TotalKB : %1")
04598 .arg(it1->getTotalSpace()));
04599 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" UsedKB : %1")
04600 .arg(it1->getUsedSpace()));
04601 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, QString(" FreeKB : %1")
04602 .arg(it1->getFreeSpace()));
04603 }
04604 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
04605 "--- GetFilesystemInfos directory list end ---");
04606 }
04607 }
04608
04609 void TruncateThread::run(void)
04610 {
04611 if (m_ms)
04612 m_ms->DoTruncateThread(this);
04613 }
04614
04615 void MainServer::DoTruncateThread(DeleteStruct *ds)
04616 {
04617 if (gCoreContext->GetNumSetting("TruncateDeletesSlowly", 0))
04618 {
04619 TruncateAndClose(NULL, ds->m_fd, ds->m_filename, ds->m_size);
04620 }
04621 else
04622 {
04623 QMutexLocker dl(&deletelock);
04624 close(ds->m_fd);
04625 }
04626 }
04627
04628 bool MainServer::HandleDeleteFile(QStringList &slist, PlaybackSock *pbs)
04629 {
04630 return HandleDeleteFile(slist[1], slist[2], pbs);
04631 }
04632
04633 bool MainServer::HandleDeleteFile(QString filename, QString storagegroup,
04634 PlaybackSock *pbs)
04635 {
04636 StorageGroup sgroup(storagegroup, "", false);
04637 QStringList retlist;
04638
04639 if ((filename.isEmpty()) ||
04640 (filename.contains("/../")) ||
04641 (filename.startsWith("../")))
04642 {
04643 LOG(VB_GENERAL, LOG_ERR, QString("ERROR deleting file, filename '%1' "
04644 "fails sanity checks").arg(filename));
04645 if (pbs)
04646 {
04647 retlist << "0";
04648 SendResponse(pbs->getSocket(), retlist);
04649 }
04650 return false;
04651 }
04652
04653 QString fullfile = sgroup.FindFile(filename);
04654
04655 if (fullfile.isEmpty()) {
04656 LOG(VB_GENERAL, LOG_ERR,
04657 QString("Unable to find %1 in HandleDeleteFile()") .arg(filename));
04658 if (pbs)
04659 {
04660 retlist << "0";
04661 SendResponse(pbs->getSocket(), retlist);
04662 }
04663 return false;
04664 }
04665
04666 QFile checkFile(fullfile);
04667 bool followLinks = gCoreContext->GetNumSetting("DeletesFollowLinks", 0);
04668 int fd = -1;
04669 off_t size = 0;
04670
04671
04672
04673
04674 const QFileInfo info(fullfile);
04675 size = info.size();
04676 fd = DeleteFile(fullfile, followLinks);
04677
04678 if ((fd < 0) && checkFile.exists())
04679 {
04680 LOG(VB_GENERAL, LOG_ERR, QString("Error deleting file: %1.")
04681 .arg(fullfile));
04682 if (pbs)
04683 {
04684 retlist << "0";
04685 SendResponse(pbs->getSocket(), retlist);
04686 }
04687 return false;
04688 }
04689
04690 if (pbs)
04691 {
04692 retlist << "1";
04693 SendResponse(pbs->getSocket(), retlist);
04694 }
04695
04696
04697 if (fd >= 0)
04698 {
04699
04700 TruncateThread *truncateThread =
04701 new TruncateThread(this, fullfile, fd, size);
04702 truncateThread->run();
04703 }
04704
04705 return true;
04706 }
04707
04708
04709 void MainServer::HandleCutMapQuery(const QString &chanid,
04710 const QString &starttime,
04711 PlaybackSock *pbs, bool commbreak)
04712 {
04713 MythSocket *pbssock = NULL;
04714 if (pbs)
04715 pbssock = pbs->getSocket();
04716
04717 frm_dir_map_t markMap;
04718 frm_dir_map_t::const_iterator it;
04719 QDateTime recstartdt;
04720 recstartdt.setTime_t(starttime.toULongLong());
04721 QStringList retlist;
04722 int rowcnt = 0;
04723
04724 const ProgramInfo pginfo(chanid.toUInt(), recstartdt);
04725
04726 if (pginfo.GetChanID())
04727 {
04728 if (commbreak)
04729 pginfo.QueryCommBreakList(markMap);
04730 else
04731 pginfo.QueryCutList(markMap);
04732
04733 for (it = markMap.begin(); it != markMap.end(); ++it)
04734 {
04735 rowcnt++;
04736 QString intstr = QString("%1").arg(*it);
04737 retlist << intstr;
04738 retlist << QString::number(it.key());
04739 }
04740 }
04741
04742 if (rowcnt > 0)
04743 retlist.prepend(QString("%1").arg(rowcnt));
04744 else
04745 retlist << "-1";
04746
04747 if (pbssock)
04748 SendResponse(pbssock, retlist);
04749
04750 return;
04751 }
04752
04753 void MainServer::HandleCommBreakQuery(const QString &chanid,
04754 const QString &starttime,
04755 PlaybackSock *pbs)
04756 {
04757
04758
04759
04760
04761
04762
04763
04764
04765 return HandleCutMapQuery(chanid, starttime, pbs, true);
04766 }
04767
04768 void MainServer::HandleCutlistQuery(const QString &chanid,
04769 const QString &starttime,
04770 PlaybackSock *pbs)
04771 {
04772
04773
04774
04775
04776
04777
04778
04779
04780 return HandleCutMapQuery(chanid, starttime, pbs, false);
04781 }
04782
04783
04784 void MainServer::HandleBookmarkQuery(const QString &chanid,
04785 const QString &starttime,
04786 PlaybackSock *pbs)
04787
04788
04789
04790
04791
04792
04793 {
04794 MythSocket *pbssock = NULL;
04795 if (pbs)
04796 pbssock = pbs->getSocket();
04797
04798 QDateTime recstartts;
04799 recstartts.setTime_t(starttime.toULongLong());
04800
04801 uint64_t bookmark = ProgramInfo::QueryBookmark(
04802 chanid.toUInt(), recstartts);
04803
04804 QStringList retlist;
04805 retlist << QString::number(bookmark);
04806
04807 if (pbssock)
04808 SendResponse(pbssock, retlist);
04809
04810 return;
04811 }
04812
04813
04814 void MainServer::HandleSetBookmark(QStringList &tokens,
04815 PlaybackSock *pbs)
04816 {
04817
04818
04819
04820
04821
04822
04823
04824 MythSocket *pbssock = NULL;
04825 if (pbs)
04826 pbssock = pbs->getSocket();
04827
04828 QString chanid = tokens[1];
04829 QString starttime = tokens[2];
04830 long long bookmark = tokens[3].toLongLong();
04831
04832 QDateTime recstartts;
04833 recstartts.setTime_t(starttime.toULongLong());
04834 QStringList retlist;
04835
04836 ProgramInfo pginfo(chanid.toUInt(), recstartts);
04837
04838 if (pginfo.GetChanID())
04839 {
04840 pginfo.SaveBookmark(bookmark);
04841 retlist << "OK";
04842 }
04843 else
04844 retlist << "FAILED";
04845
04846 if (pbssock)
04847 SendResponse(pbssock, retlist);
04848
04849 return;
04850 }
04851
04852 void MainServer::HandleSettingQuery(QStringList &tokens, PlaybackSock *pbs)
04853 {
04854
04855
04856
04857 MythSocket *pbssock = NULL;
04858 if (pbs)
04859 pbssock = pbs->getSocket();
04860
04861 QString hostname = tokens[1];
04862 QString setting = tokens[2];
04863 QStringList retlist;
04864
04865 QString retvalue = gCoreContext->GetSettingOnHost(setting, hostname, "-1");
04866
04867 retlist << retvalue;
04868 if (pbssock)
04869 SendResponse(pbssock, retlist);
04870
04871 return;
04872 }
04873
04874 void MainServer::HandleDownloadFile(const QStringList &command,
04875 PlaybackSock *pbs)
04876 {
04877 bool synchronous = (command[0] == "DOWNLOAD_FILE_NOW");
04878 QString srcURL = command[1];
04879 QString storageGroup = command[2];
04880 QString filename = command[3];
04881 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false);
04882 QString outDir = sgroup.FindNextDirMostFree();
04883 QString outFile;
04884 QStringList retlist;
04885
04886 MythSocket *pbssock = NULL;
04887 if (pbs)
04888 pbssock = pbs->getSocket();
04889
04890 if (filename.isEmpty())
04891 {
04892 QFileInfo finfo(srcURL);
04893 filename = finfo.fileName();
04894 }
04895
04896 if (outDir.isEmpty())
04897 {
04898 LOG(VB_GENERAL, LOG_ERR,
04899 QString("Unable to determine directory "
04900 "to write to in %1 write command").arg(command[0]));
04901 retlist << "downloadfile_directory_not_found";
04902 if (pbssock)
04903 SendResponse(pbssock, retlist);
04904 return;
04905 }
04906
04907 if ((filename.contains("/../")) ||
04908 (filename.startsWith("../")))
04909 {
04910 LOG(VB_GENERAL, LOG_ERR,
04911 QString("ERROR: %1 write filename '%2' does not pass "
04912 "sanity checks.") .arg(command[0]).arg(filename));
04913 retlist << "downloadfile_filename_dangerous";
04914 if (pbssock)
04915 SendResponse(pbssock, retlist);
04916 return;
04917 }
04918
04919 outFile = outDir + "/" + filename;
04920
04921 if (synchronous)
04922 {
04923 if (GetMythDownloadManager()->download(srcURL, outFile))
04924 {
04925 retlist << "OK";
04926 retlist << gCoreContext->GetMasterHostPrefix(storageGroup)
04927 + filename;
04928 }
04929 else
04930 retlist << "ERROR";
04931 }
04932 else
04933 {
04934 QMutexLocker locker(&m_downloadURLsLock);
04935 m_downloadURLs[outFile] =
04936 gCoreContext->GetMasterHostPrefix(storageGroup) +
04937 StorageGroup::GetRelativePathname(outFile);
04938
04939 GetMythDownloadManager()->queueDownload(srcURL, outFile, this);
04940 retlist << "OK";
04941 retlist << gCoreContext->GetMasterHostPrefix(storageGroup) + filename;
04942 }
04943
04944 if (pbssock)
04945 SendResponse(pbssock, retlist);
04946 }
04947
04948 void MainServer::HandleSetSetting(QStringList &tokens,
04949 PlaybackSock *pbs)
04950 {
04951
04952 MythSocket *pbssock = NULL;
04953 if (pbs)
04954 pbssock = pbs->getSocket();
04955
04956 QString hostname = tokens[1];
04957 QString setting = tokens[2];
04958 QString svalue = tokens[3];
04959 QStringList retlist;
04960
04961 if (gCoreContext->SaveSettingOnHost(setting, svalue, hostname))
04962 retlist << "OK";
04963 else
04964 retlist << "ERROR";
04965
04966 if (pbssock)
04967 SendResponse(pbssock, retlist);
04968
04969 return;
04970 }
04971
04972 void MainServer::HandleScanVideos(PlaybackSock *pbs)
04973 {
04974 MythSocket *pbssock = pbs->getSocket();
04975
04976 QStringList retlist;
04977
04978 if (metadatafactory)
04979 {
04980 QStringList hosts;
04981 GetActiveBackends(hosts);
04982 metadatafactory->VideoScan(hosts);
04983 retlist << "OK";
04984 }
04985 else
04986 retlist << "ERROR";
04987
04988 if (pbssock)
04989 SendResponse(pbssock, retlist);
04990 }
04991
04992 void MainServer::HandleFileTransferQuery(QStringList &slist,
04993 QStringList &commands,
04994 PlaybackSock *pbs)
04995 {
04996 MythSocket *pbssock = pbs->getSocket();
04997
04998 int recnum = commands[1].toInt();
04999 QString command = slist[1];
05000
05001 QStringList retlist;
05002
05003 sockListLock.lockForRead();
05004 FileTransfer *ft = GetFileTransferByID(recnum);
05005 if (!ft)
05006 {
05007 if (command == "DONE")
05008 {
05009
05010
05011 retlist << "ok";
05012 }
05013 else
05014 {
05015 LOG(VB_GENERAL, LOG_ERR, QString("Unknown file transfer socket: %1")
05016 .arg(recnum));
05017 retlist << QString("ERROR: Unknown file transfer socket: %1")
05018 .arg(recnum);
05019 }
05020
05021 sockListLock.unlock();
05022 SendResponse(pbssock, retlist);
05023 return;
05024 }
05025
05026 ft->UpRef();
05027 sockListLock.unlock();
05028
05029 if (command == "IS_OPEN")
05030 {
05031 bool isopen = ft->isOpen();
05032
05033 retlist << QString::number(isopen);
05034 }
05035 else if (command == "REOPEN")
05036 {
05037 retlist << QString::number(ft->ReOpen(slist[2]));
05038 }
05039 else if (command == "DONE")
05040 {
05041 ft->Stop();
05042 retlist << "ok";
05043 }
05044 else if (command == "REQUEST_BLOCK")
05045 {
05046 int size = slist[2].toInt();
05047
05048 retlist << QString::number(ft->RequestBlock(size));
05049 }
05050 else if (command == "WRITE_BLOCK")
05051 {
05052 int size = slist[2].toInt();
05053
05054 retlist << QString::number(ft->WriteBlock(size));
05055 }
05056 else if (command == "SEEK")
05057 {
05058 long long pos = slist[2].toLongLong();
05059 int whence = slist[3].toInt();
05060 long long curpos = slist[4].toLongLong();
05061
05062 long long ret = ft->Seek(curpos, pos, whence);
05063 retlist << QString::number(ret);
05064 }
05065 else if (command == "SET_TIMEOUT")
05066 {
05067 bool fast = slist[2].toInt();
05068 ft->SetTimeout(fast);
05069 retlist << "ok";
05070 }
05071 else
05072 {
05073 LOG(VB_GENERAL, LOG_ERR, QString("Unknown command: %1").arg(command));
05074 retlist << "ok";
05075 }
05076
05077 ft->DownRef();
05078
05079 SendResponse(pbssock, retlist);
05080 }
05081
05082 void MainServer::HandleGetRecorderNum(QStringList &slist, PlaybackSock *pbs)
05083 {
05084 MythSocket *pbssock = pbs->getSocket();
05085
05086 int retval = -1;
05087
05088 QStringList::const_iterator it = slist.begin() + 1;
05089 ProgramInfo pginfo(it, slist.end());
05090
05091 EncoderLink *encoder = NULL;
05092
05093 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
05094 for (; iter != encoderList->end(); ++iter)
05095 {
05096 EncoderLink *elink = *iter;
05097
05098 if (elink->IsConnected() && elink->MatchesRecording(&pginfo))
05099 {
05100 retval = iter.key();
05101 encoder = elink;
05102 }
05103 }
05104
05105 QStringList strlist( QString::number(retval) );
05106
05107 if (encoder)
05108 {
05109 if (encoder->IsLocal())
05110 {
05111 strlist << gCoreContext->GetBackendServerIP();
05112 strlist << gCoreContext->GetSetting("BackendServerPort");
05113 }
05114 else
05115 {
05116 strlist << gCoreContext->GetBackendServerIP(encoder->GetHostName());
05117 strlist << gCoreContext->GetSettingOnHost("BackendServerPort",
05118 encoder->GetHostName(), "-1");
05119 }
05120 }
05121 else
05122 {
05123 strlist << "nohost";
05124 strlist << "-1";
05125 }
05126
05127 SendResponse(pbssock, strlist);
05128 }
05129
05130 void MainServer::HandleGetRecorderFromNum(QStringList &slist,
05131 PlaybackSock *pbs)
05132 {
05133 MythSocket *pbssock = pbs->getSocket();
05134
05135 int recordernum = slist[1].toInt();
05136 EncoderLink *encoder = NULL;
05137 QStringList strlist;
05138
05139 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recordernum);
05140
05141 if (iter != encoderList->end())
05142 encoder = (*iter);
05143
05144 if (encoder && encoder->IsConnected())
05145 {
05146 if (encoder->IsLocal())
05147 {
05148 strlist << gCoreContext->GetBackendServerIP();
05149 strlist << gCoreContext->GetSetting("BackendServerPort");
05150 }
05151 else
05152 {
05153 strlist << gCoreContext->GetBackendServerIP(encoder->GetHostName());
05154 strlist << gCoreContext->GetSettingOnHost("BackendServerPort",
05155 encoder->GetHostName(), "-1");
05156 }
05157 }
05158 else
05159 {
05160 strlist << "nohost";
05161 strlist << "-1";
05162 }
05163
05164 SendResponse(pbssock, strlist);
05165 }
05166
05167 void MainServer::HandleMessage(QStringList &slist, PlaybackSock *pbs)
05168 {
05169 if (slist.size() < 2)
05170 return;
05171
05172 MythSocket *pbssock = pbs->getSocket();
05173
05174 QString message = slist[1];
05175 QStringList extra_data;
05176 for (uint i = 2; i < (uint) slist.size(); i++)
05177 extra_data.push_back(slist[i]);
05178
05179 if (extra_data.empty())
05180 {
05181 MythEvent me(message);
05182 gCoreContext->dispatch(me);
05183 }
05184 else
05185 {
05186 MythEvent me(message, extra_data);
05187 gCoreContext->dispatch(me);
05188 }
05189
05190 QStringList retlist( "OK" );
05191
05192 SendResponse(pbssock, retlist);
05193 }
05194
05195 void MainServer::HandleSetVerbose(QStringList &slist, PlaybackSock *pbs)
05196 {
05197 MythSocket *pbssock = pbs->getSocket();
05198 QStringList retlist;
05199
05200 QString newverbose = slist[1];
05201 int len = newverbose.length();
05202 if (len > 12)
05203 {
05204 verboseArgParse(newverbose.right(len-12));
05205 logPropagateCalc();
05206
05207 LOG(VB_GENERAL, LOG_NOTICE,
05208 QString("Verbose mask changed, new mask is: %1")
05209 .arg(verboseString));
05210
05211 retlist << "OK";
05212 }
05213 else
05214 {
05215 LOG(VB_GENERAL, LOG_ERR, QString("Invalid SET_VERBOSE string: '%1'")
05216 .arg(newverbose));
05217 retlist << "Failed";
05218 }
05219
05220 SendResponse(pbssock, retlist);
05221 }
05222
05223 void MainServer::HandleSetLogLevel(QStringList &slist, PlaybackSock *pbs)
05224 {
05225 MythSocket *pbssock = pbs->getSocket();
05226 QStringList retlist;
05227 QString newstring = slist[1];
05228 LogLevel_t newlevel = LOG_UNKNOWN;
05229
05230 int len = newstring.length();
05231 if (len > 14)
05232 {
05233 newlevel = logLevelGet(newstring.right(len-14));
05234 if (newlevel != LOG_UNKNOWN)
05235 {
05236 logLevel = newlevel;
05237 logPropagateCalc();
05238 LOG(VB_GENERAL, LOG_NOTICE,
05239 QString("Log level changed, new level is: %1")
05240 .arg(logLevelGetName(logLevel)));
05241
05242 retlist << "OK";
05243 }
05244 }
05245
05246 if (newlevel == LOG_UNKNOWN)
05247 {
05248 LOG(VB_GENERAL, LOG_ERR, QString("Invalid SET_VERBOSE string: '%1'")
05249 .arg(newstring));
05250 retlist << "Failed";
05251 }
05252
05253 SendResponse(pbssock, retlist);
05254 }
05255
05256 void MainServer::HandleIsRecording(QStringList &slist, PlaybackSock *pbs)
05257 {
05258 (void)slist;
05259
05260 MythSocket *pbssock = pbs->getSocket();
05261 int RecordingsInProgress = 0;
05262 int LiveTVRecordingsInProgress = 0;
05263 QStringList retlist;
05264
05265 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
05266 for (; iter != encoderList->end(); ++iter)
05267 {
05268 EncoderLink *elink = *iter;
05269 if (elink->IsBusyRecording()) {
05270 RecordingsInProgress++;
05271
05272 ProgramInfo *info = elink->GetRecording();
05273 if (info && info->GetRecordingGroup() == "LiveTV")
05274 LiveTVRecordingsInProgress++;
05275
05276 delete info;
05277 }
05278 }
05279
05280 retlist << QString::number(RecordingsInProgress);
05281 retlist << QString::number(LiveTVRecordingsInProgress);
05282
05283 SendResponse(pbssock, retlist);
05284 }
05285
05286 void MainServer::HandleGenPreviewPixmap(QStringList &slist, PlaybackSock *pbs)
05287 {
05288 MythSocket *pbssock = pbs->getSocket();
05289
05290 if (slist.size() < 3)
05291 {
05292 LOG(VB_GENERAL, LOG_ERR, LOC + "Too few params in pixmap request");
05293 QStringList outputlist("ERROR");
05294 outputlist += "TOO_FEW_PARAMS";
05295 SendResponse(pbssock, outputlist);
05296 return;
05297 }
05298
05299 bool time_fmt_sec = true;
05300 long long time = -1;
05301 QString outputfile;
05302 int width = -1;
05303 int height = -1;
05304 bool has_extra_data = false;
05305
05306 QString token = slist[1];
05307 if (token.isEmpty())
05308 {
05309 LOG(VB_GENERAL, LOG_ERR, LOC +
05310 "Failed to parse pixmap request. Token absent");
05311 QStringList outputlist("ERROR");
05312 outputlist += "TOKEN_ABSENT";
05313 SendResponse(pbssock, outputlist);
05314 return;
05315 }
05316
05317 QStringList::const_iterator it = slist.begin() + 2;
05318 QStringList::const_iterator end = slist.end();
05319 ProgramInfo pginfo(it, end);
05320 bool ok = pginfo.HasPathname();
05321 if (!ok)
05322 {
05323 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to parse pixmap request. "
05324 "ProgramInfo missing pathname");
05325 QStringList outputlist("BAD");
05326 outputlist += "NO_PATHNAME";
05327 SendResponse(pbssock, outputlist);
05328 return;
05329 }
05330 if (token.toLower() == "do_not_care")
05331 {
05332 token = QString("%1:%2")
05333 .arg(pginfo.MakeUniqueKey()).arg(random());
05334 }
05335 if (it != slist.end())
05336 (time_fmt_sec = ((*it).toLower() == "s")), ++it;
05337 if (it != slist.end())
05338 (time = (*it).toLongLong()), ++it;
05339 if (it != slist.end())
05340 (outputfile = *it), ++it;
05341 outputfile = (outputfile == "<EMPTY>") ? QString::null : outputfile;
05342 if (it != slist.end())
05343 {
05344 width = (*it).toInt(&ok); ++it;
05345 width = (ok) ? width : -1;
05346 }
05347 if (it != slist.end())
05348 {
05349 height = (*it).toInt(&ok); ++it;
05350 height = (ok) ? height : -1;
05351 has_extra_data = true;
05352 }
05353 QSize outputsize = QSize(width, height);
05354
05355 if (has_extra_data)
05356 {
05357 LOG(VB_PLAYBACK, LOG_INFO,
05358 QString("HandleGenPreviewPixmap got extra data\n\t\t\t"
05359 "%1%2 %3x%4 '%5'")
05360 .arg(time).arg(time_fmt_sec?"s":"f")
05361 .arg(width).arg(height).arg(outputfile));
05362 }
05363
05364 pginfo.SetPathname(GetPlaybackURL(&pginfo));
05365
05366 m_previewRequestedBy[token] = pbs->getHostname();
05367
05368 if ((ismaster) &&
05369 (pginfo.GetHostname() != gCoreContext->GetHostName()) &&
05370 (!masterBackendOverride || !pginfo.IsLocal()))
05371 {
05372 PlaybackSock *slave = GetSlaveByHostname(pginfo.GetHostname());
05373
05374 if (slave)
05375 {
05376 QStringList outputlist;
05377 if (has_extra_data)
05378 {
05379 outputlist = slave->GenPreviewPixmap(
05380 token, &pginfo, time_fmt_sec, time, outputfile, outputsize);
05381 }
05382 else
05383 {
05384 outputlist = slave->GenPreviewPixmap(token, &pginfo);
05385 }
05386
05387 slave->DownRef();
05388
05389 if (outputlist.empty() || outputlist[0] != "OK")
05390 m_previewRequestedBy.remove(token);
05391
05392 SendResponse(pbssock, outputlist);
05393 return;
05394 }
05395 LOG(VB_GENERAL, LOG_ERR, LOC +
05396 QString("HandleGenPreviewPixmap() "
05397 "Couldn't find backend for:\n\t\t\t%1")
05398 .arg(pginfo.toString(ProgramInfo::kTitleSubtitle)));
05399 }
05400
05401 if (!pginfo.IsLocal())
05402 {
05403 LOG(VB_GENERAL, LOG_ERR, LOC + "HandleGenPreviewPixmap: Unable to "
05404 "find file locally, unable to make preview image.");
05405 QStringList outputlist( "ERROR" );
05406 outputlist += "FILE_INACCESSIBLE";
05407 SendResponse(pbssock, outputlist);
05408 m_previewRequestedBy.remove(token);
05409 return;
05410 }
05411
05412 if (has_extra_data)
05413 {
05414 PreviewGeneratorQueue::GetPreviewImage(
05415 pginfo, outputsize, outputfile, time, time_fmt_sec, token);
05416 }
05417 else
05418 {
05419 PreviewGeneratorQueue::GetPreviewImage(pginfo, token);
05420 }
05421
05422 QStringList outputlist("OK");
05423 if (!outputfile.isEmpty())
05424 outputlist += outputfile;
05425 SendResponse(pbssock, outputlist);
05426 }
05427
05428 void MainServer::HandlePixmapLastModified(QStringList &slist, PlaybackSock *pbs)
05429 {
05430 MythSocket *pbssock = pbs->getSocket();
05431
05432 QStringList::const_iterator it = slist.begin() + 1;
05433 ProgramInfo pginfo(it, slist.end());
05434
05435 pginfo.SetPathname(GetPlaybackURL(&pginfo));
05436
05437 QStringList strlist;
05438
05439 if (ismaster &&
05440 (pginfo.GetHostname() != gCoreContext->GetHostName()) &&
05441 (!masterBackendOverride || !pginfo.IsLocal()))
05442 {
05443 PlaybackSock *slave = GetSlaveByHostname(pginfo.GetHostname());
05444
05445 if (slave)
05446 {
05447 QDateTime slavetime = slave->PixmapLastModified(&pginfo);
05448 slave->DownRef();
05449
05450 strlist = (slavetime.isValid()) ?
05451 QStringList(QString::number(slavetime.toTime_t())) :
05452 QStringList("BAD");
05453
05454 SendResponse(pbssock, strlist);
05455 return;
05456 }
05457 else
05458 {
05459 LOG(VB_GENERAL, LOG_ERR, LOC +
05460 QString("HandlePixmapLastModified() "
05461 "Couldn't find backend for:\n\t\t\t%1")
05462 .arg(pginfo.toString(ProgramInfo::kTitleSubtitle)));
05463 }
05464 }
05465
05466 if (!pginfo.IsLocal())
05467 {
05468 LOG(VB_GENERAL, LOG_ERR,
05469 "MainServer: HandlePixmapLastModified: Unable to "
05470 "find file locally, unable to get last modified date.");
05471 QStringList outputlist( "BAD" );
05472 SendResponse(pbssock, outputlist);
05473 return;
05474 }
05475
05476 QString filename = pginfo.GetPathname() + ".png";
05477
05478 QFileInfo finfo(filename);
05479
05480 if (finfo.exists())
05481 {
05482 QDateTime lastmodified = finfo.lastModified();
05483 strlist = QStringList(QString::number(lastmodified.toTime_t()));
05484 }
05485 else
05486 strlist = QStringList( "BAD" );
05487
05488 SendResponse(pbssock, strlist);
05489 }
05490
05491 void MainServer::HandlePixmapGetIfModified(
05492 const QStringList &slist, PlaybackSock *pbs)
05493 {
05494 QStringList strlist;
05495
05496 MythSocket *pbssock = pbs->getSocket();
05497 if (slist.size() < (3 + NUMPROGRAMLINES))
05498 {
05499 strlist = QStringList("ERROR");
05500 strlist += "1: Parameter list too short";
05501 SendResponse(pbssock, strlist);
05502 return;
05503 }
05504
05505 QDateTime cachemodified;
05506 if (slist[1].toInt() != -1)
05507 cachemodified.setTime_t(slist[1].toInt());
05508
05509 int max_file_size = slist[2].toInt();
05510
05511 QStringList::const_iterator it = slist.begin() + 3;
05512 ProgramInfo pginfo(it, slist.end());
05513
05514 if (!pginfo.HasPathname())
05515 {
05516 strlist = QStringList("ERROR");
05517 strlist += "2: Invalid ProgramInfo";
05518 SendResponse(pbssock, strlist);
05519 return;
05520 }
05521
05522 pginfo.SetPathname(GetPlaybackURL(&pginfo) + ".png");
05523 if (pginfo.IsLocal())
05524 {
05525 QFileInfo finfo(pginfo.GetPathname());
05526 if (finfo.exists())
05527 {
05528 size_t fsize = finfo.size();
05529 QDateTime lastmodified = finfo.lastModified();
05530 bool out_of_date = !cachemodified.isValid() ||
05531 (lastmodified > cachemodified);
05532
05533 if (out_of_date && (fsize > 0) && ((ssize_t)fsize < max_file_size))
05534 {
05535 QByteArray data;
05536 QFile file(pginfo.GetPathname());
05537 bool open_ok = file.open(QIODevice::ReadOnly);
05538 if (open_ok)
05539 data = file.readAll();
05540
05541 if (data.size())
05542 {
05543 LOG(VB_FILE, LOG_INFO, QString("Read preview file '%1'")
05544 .arg(pginfo.GetPathname()));
05545 strlist += QString::number(lastmodified.toTime_t());
05546 strlist += QString::number(data.size());
05547 strlist += QString::number(qChecksum(data.constData(),
05548 data.size()));
05549 strlist += QString(data.toBase64());
05550 }
05551 else
05552 {
05553 LOG(VB_GENERAL, LOG_ERR,
05554 QString("Failed to read preview file '%1'")
05555 .arg(pginfo.GetPathname()));
05556
05557 strlist = QStringList("ERROR");
05558 strlist +=
05559 QString("3: Failed to read preview file '%1'%2")
05560 .arg(pginfo.GetPathname())
05561 .arg((open_ok) ? "" : " open failed");
05562 }
05563 }
05564 else if (out_of_date && (max_file_size > 0))
05565 {
05566 if (fsize >= (size_t) max_file_size)
05567 {
05568 strlist = QStringList("WARNING");
05569 strlist += QString("1: Preview file too big %1 > %2")
05570 .arg(fsize).arg(max_file_size);
05571 }
05572 else
05573 {
05574 strlist = QStringList("ERROR");
05575 strlist += "4: Preview file is invalid";
05576 }
05577 }
05578 else
05579 {
05580 strlist += QString::number(lastmodified.toTime_t());
05581 }
05582
05583 SendResponse(pbssock, strlist);
05584 return;
05585 }
05586 }
05587
05588
05589 if (ismaster && pginfo.GetHostname() != gCoreContext->GetHostName())
05590 {
05591 PlaybackSock *slave = GetSlaveByHostname(pginfo.GetHostname());
05592 if (!slave)
05593 {
05594 strlist = QStringList("ERROR");
05595 strlist +=
05596 "5: Could not locate mythbackend that made this recording";
05597 SendResponse(pbssock, strlist);
05598 return;
05599 }
05600
05601 strlist = slave->ForwardRequest(slist);
05602
05603 slave->DownRef(); slave = NULL;
05604
05605 if (!strlist.empty())
05606 {
05607 SendResponse(pbssock, strlist);
05608 return;
05609 }
05610 }
05611
05612 strlist = QStringList("WARNING");
05613 strlist += "2: Could not locate requested file";
05614 SendResponse(pbssock, strlist);
05615 }
05616
05617 void MainServer::HandleBackendRefresh(MythSocket *socket)
05618 {
05619 QStringList retlist( "OK" );
05620 SendResponse(socket, retlist);
05621 }
05622
05623 void MainServer::HandleBlockShutdown(bool blockShutdown, PlaybackSock *pbs)
05624 {
05625 pbs->setBlockShutdown(blockShutdown);
05626
05627 MythSocket *socket = pbs->getSocket();
05628 QStringList retlist( "OK" );
05629 SendResponse(socket, retlist);
05630 }
05631
05632 void MainServer::deferredDeleteSlot(void)
05633 {
05634 QMutexLocker lock(&deferredDeleteLock);
05635
05636 if (deferredDeleteList.empty())
05637 return;
05638
05639 DeferredDeleteStruct dds = deferredDeleteList.front();
05640 while (dds.ts.secsTo(QDateTime::currentDateTime()) > 30)
05641 {
05642 delete dds.sock;
05643 deferredDeleteList.pop_front();
05644 if (deferredDeleteList.empty())
05645 return;
05646 dds = deferredDeleteList.front();
05647 }
05648 }
05649
05650 void MainServer::DeletePBS(PlaybackSock *sock)
05651 {
05652 DeferredDeleteStruct dds;
05653 dds.sock = sock;
05654 dds.ts = QDateTime::currentDateTime();
05655
05656 QMutexLocker lock(&deferredDeleteLock);
05657 deferredDeleteList.push_back(dds);
05658 }
05659
05660 void MainServer::connectionClosed(MythSocket *socket)
05661 {
05662 sockListLock.lockForWrite();
05663
05664 vector<PlaybackSock *>::iterator it = playbackList.begin();
05665 for (; it != playbackList.end(); ++it)
05666 {
05667 PlaybackSock *pbs = (*it);
05668 MythSocket *sock = pbs->getSocket();
05669 if (sock == socket && pbs == masterServer)
05670 {
05671 playbackList.erase(it);
05672 sockListLock.unlock();
05673 masterServer->DownRef();
05674 masterServer = NULL;
05675 MythEvent me("LOCAL_RECONNECT_TO_MASTER");
05676 gCoreContext->dispatch(me);
05677 return;
05678 }
05679 else if (sock == socket)
05680 {
05681 QList<uint> disconnectedSlaves;
05682 bool needsReschedule = false;
05683
05684 if (ismaster && pbs->isSlaveBackend())
05685 {
05686 LOG(VB_GENERAL, LOG_ERR,
05687 QString("Slave backend: %1 no longer connected")
05688 .arg(pbs->getHostname()));
05689
05690 bool isFallingAsleep = true;
05691 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
05692 for (; iter != encoderList->end(); ++iter)
05693 {
05694 EncoderLink *elink = *iter;
05695 if (elink->GetSocket() == pbs)
05696 {
05697 if (!elink->IsFallingAsleep())
05698 isFallingAsleep = false;
05699
05700 elink->SetSocket(NULL);
05701 if (m_sched)
05702 disconnectedSlaves.push_back(elink->GetCardID());
05703 }
05704 }
05705 if (m_sched && !isFallingAsleep)
05706 needsReschedule = true;
05707
05708 QString message = QString("LOCAL_SLAVE_BACKEND_OFFLINE %1")
05709 .arg(pbs->getHostname());
05710 MythEvent me(message);
05711 gCoreContext->dispatch(me);
05712
05713 MythEvent me2("RECORDING_LIST_CHANGE");
05714 gCoreContext->dispatch(me2);
05715
05716 gCoreContext->SendSystemEvent(
05717 QString("SLAVE_DISCONNECTED HOSTNAME %1")
05718 .arg(pbs->getHostname()));
05719 }
05720 else if (ismaster && pbs->getHostname() != "tzcheck")
05721 {
05722 gCoreContext->SendSystemEvent(
05723 QString("CLIENT_DISCONNECTED HOSTNAME %1")
05724 .arg(pbs->getHostname()));
05725 }
05726
05727 LiveTVChain *chain;
05728 if ((chain = GetExistingChain(sock)))
05729 {
05730 chain->DelHostSocket(sock);
05731 if (chain->HostSocketCount() == 0)
05732 {
05733 QMap<int, EncoderLink *>::iterator it =
05734 encoderList->begin();
05735 for (; it != encoderList->end(); ++it)
05736 {
05737 EncoderLink *enc = *it;
05738 if (enc->IsLocal())
05739 {
05740 while (enc->GetState() == kState_ChangingState)
05741 usleep(500);
05742
05743 if (enc->IsBusy() &&
05744 enc->GetChainID() == chain->GetID())
05745 {
05746 enc->StopLiveTV();
05747 }
05748 }
05749 }
05750 DeleteChain(chain);
05751 }
05752 }
05753
05754 pbs->SetDisconnected();
05755 playbackList.erase(it);
05756
05757 PlaybackSock *testsock = GetPlaybackBySock(socket);
05758 if (testsock)
05759 LOG(VB_GENERAL, LOG_ERR, "Playback sock still exists?");
05760
05761 sockListLock.unlock();
05762
05763
05764
05765 SendSlaveDisconnectedEvent(disconnectedSlaves, needsReschedule);
05766
05767 pbs->DownRef();
05768 return;
05769 }
05770 }
05771
05772 vector<FileTransfer *>::iterator ft = fileTransferList.begin();
05773 for (; ft != fileTransferList.end(); ++ft)
05774 {
05775 MythSocket *sock = (*ft)->getSocket();
05776 if (sock == socket)
05777 {
05778 (*ft)->DownRef();
05779 fileTransferList.erase(ft);
05780 sockListLock.unlock();
05781 return;
05782 }
05783 }
05784
05785 sockListLock.unlock();
05786
05787 LOG(VB_GENERAL, LOG_WARNING, LOC +
05788 QString("Unknown socket closing MythSocket(0x%1)")
05789 .arg((uint64_t)socket,0,16));
05790 }
05791
05792 PlaybackSock *MainServer::GetSlaveByHostname(const QString &hostname)
05793 {
05794 if (!ismaster)
05795 return NULL;
05796
05797 sockListLock.lockForRead();
05798
05799 vector<PlaybackSock *>::iterator iter = playbackList.begin();
05800 for (; iter != playbackList.end(); ++iter)
05801 {
05802 PlaybackSock *pbs = *iter;
05803 if (pbs->isSlaveBackend() &&
05804 ((pbs->getHostname().toLower() == hostname.toLower()) ||
05805 (gCoreContext->IsThisHost(hostname, pbs->getHostname()))))
05806 {
05807 sockListLock.unlock();
05808 pbs->UpRef();
05809 return pbs;
05810 }
05811 }
05812
05813 sockListLock.unlock();
05814
05815 return NULL;
05816 }
05817
05818 PlaybackSock *MainServer::GetMediaServerByHostname(const QString &hostname)
05819 {
05820 if (!ismaster)
05821 return NULL;
05822
05823 QReadLocker rlock(&sockListLock);
05824
05825 vector<PlaybackSock *>::iterator iter = playbackList.begin();
05826 for (; iter != playbackList.end(); ++iter)
05827 {
05828 PlaybackSock *pbs = *iter;
05829 if (pbs->isMediaServer() &&
05830 ((pbs->getHostname().toLower() == hostname.toLower()) ||
05831 (gCoreContext->IsThisHost(hostname, pbs->getHostname()))))
05832 {
05833 pbs->UpRef();
05834 return pbs;
05835 }
05836 }
05837
05838 return NULL;
05839 }
05840
05842 PlaybackSock *MainServer::GetPlaybackBySock(MythSocket *sock)
05843 {
05844 PlaybackSock *retval = NULL;
05845
05846 vector<PlaybackSock *>::iterator it = playbackList.begin();
05847 for (; it != playbackList.end(); ++it)
05848 {
05849 if (sock == (*it)->getSocket())
05850 {
05851 retval = (*it);
05852 break;
05853 }
05854 }
05855
05856 return retval;
05857 }
05858
05860 FileTransfer *MainServer::GetFileTransferByID(int id)
05861 {
05862 FileTransfer *retval = NULL;
05863
05864 vector<FileTransfer *>::iterator it = fileTransferList.begin();
05865 for (; it != fileTransferList.end(); ++it)
05866 {
05867 if (id == (*it)->getSocket()->socket())
05868 {
05869 retval = (*it);
05870 break;
05871 }
05872 }
05873
05874 return retval;
05875 }
05876
05878 FileTransfer *MainServer::GetFileTransferBySock(MythSocket *sock)
05879 {
05880 FileTransfer *retval = NULL;
05881
05882 vector<FileTransfer *>::iterator it = fileTransferList.begin();
05883 for (; it != fileTransferList.end(); ++it)
05884 {
05885 if (sock == (*it)->getSocket())
05886 {
05887 retval = (*it);
05888 break;
05889 }
05890 }
05891
05892 return retval;
05893 }
05894
05895 LiveTVChain *MainServer::GetExistingChain(const QString &id)
05896 {
05897 QMutexLocker lock(&liveTVChainsLock);
05898
05899 vector<LiveTVChain*>::iterator it = liveTVChains.begin();
05900 for (; it != liveTVChains.end(); ++it)
05901 {
05902 if ((*it)->GetID() == id)
05903 return *it;
05904 }
05905
05906 return NULL;
05907 }
05908
05909 LiveTVChain *MainServer::GetExistingChain(const MythSocket *sock)
05910 {
05911 QMutexLocker lock(&liveTVChainsLock);
05912
05913 vector<LiveTVChain*>::iterator it = liveTVChains.begin();
05914 for (; it != liveTVChains.end(); ++it)
05915 {
05916 if ((*it)->IsHostSocket(sock))
05917 return *it;
05918 }
05919
05920 return NULL;
05921 }
05922
05923 LiveTVChain *MainServer::GetChainWithRecording(const ProgramInfo &pginfo)
05924 {
05925 QMutexLocker lock(&liveTVChainsLock);
05926
05927 vector<LiveTVChain*>::iterator it = liveTVChains.begin();
05928 for (; it != liveTVChains.end(); ++it)
05929 {
05930 if ((*it)->ProgramIsAt(pginfo) >= 0)
05931 return *it;
05932 }
05933
05934 return NULL;
05935 }
05936
05937 void MainServer::AddToChains(LiveTVChain *chain)
05938 {
05939 QMutexLocker lock(&liveTVChainsLock);
05940
05941 if (chain)
05942 liveTVChains.push_back(chain);
05943 }
05944
05945 void MainServer::DeleteChain(LiveTVChain *chain)
05946 {
05947 QMutexLocker lock(&liveTVChainsLock);
05948
05949 if (!chain)
05950 return;
05951
05952 vector<LiveTVChain*> newChains;
05953
05954 vector<LiveTVChain*>::iterator it = liveTVChains.begin();
05955 for (; it != liveTVChains.end(); ++it)
05956 {
05957 if (*it != chain)
05958 newChains.push_back(*it);
05959 }
05960 liveTVChains = newChains;
05961
05962 delete chain;
05963 }
05964
05965 void MainServer::SetExitCode(int exitCode, bool closeApplication)
05966 {
05967 m_exitCode = exitCode;
05968 if (closeApplication)
05969 QCoreApplication::exit(m_exitCode);
05970 }
05971
05972 QString MainServer::LocalFilePath(const QUrl &url, const QString &wantgroup)
05973 {
05974 QString lpath = url.path();
05975
05976 if (url.hasFragment())
05977 lpath += "#" + url.fragment();
05978
05979 if (lpath.section('/', -2, -2) == "channels")
05980 {
05981
05982 QString querytext;
05983
05984 QString file = lpath.section('/', -1);
05985 lpath = "";
05986
05987 MSqlQuery query(MSqlQuery::InitCon());
05988 query.prepare("SELECT icon FROM channel WHERE icon LIKE :FILENAME ;");
05989 query.bindValue(":FILENAME", QString("%/") + file);
05990
05991 if (query.exec() && query.next())
05992 {
05993 lpath = query.value(0).toString();
05994 }
05995 else
05996 {
05997 MythDB::DBError("Icon path", query);
05998 }
05999 }
06000 else
06001 {
06002 lpath = lpath.section('/', -1);
06003
06004 QString fpath = lpath;
06005 if (fpath.right(4) == ".png")
06006 fpath = fpath.left(fpath.length() - 4);
06007
06008 ProgramInfo pginfo(fpath);
06009 if (pginfo.GetChanID())
06010 {
06011 QString pburl = GetPlaybackURL(&pginfo);
06012 if (pburl.left(1) == "/")
06013 {
06014 lpath = pburl.section('/', 0, -2) + "/" + lpath;
06015 LOG(VB_FILE, LOG_INFO,
06016 QString("Local file path: %1").arg(lpath));
06017 }
06018 else
06019 {
06020 LOG(VB_GENERAL, LOG_ERR,
06021 QString("ERROR: LocalFilePath unable to find local "
06022 "path for '%1', found '%2' instead.")
06023 .arg(lpath).arg(pburl));
06024 lpath = "";
06025 }
06026 }
06027 else if (!lpath.isEmpty())
06028 {
06029
06030 QString opath = lpath;
06031 StorageGroup sgroup;
06032
06033 if (!wantgroup.isEmpty())
06034 {
06035 sgroup.Init(wantgroup);
06036 lpath = url.toString();
06037 }
06038 else
06039 {
06040 lpath = QFileInfo(lpath).fileName();
06041 }
06042
06043 QString tmpFile = sgroup.FindFile(lpath);
06044 if (!tmpFile.isEmpty())
06045 {
06046 lpath = tmpFile;
06047 LOG(VB_FILE, LOG_INFO,
06048 QString("LocalFilePath(%1 '%2'), found file through "
06049 "exhaustive search at '%3'")
06050 .arg(url.toString()).arg(opath).arg(lpath));
06051 }
06052 else
06053 {
06054 LOG(VB_GENERAL, LOG_ERR, QString("ERROR: LocalFilePath "
06055 "unable to find local path for '%1'.") .arg(opath));
06056 lpath = "";
06057 }
06058
06059 }
06060 else
06061 {
06062 lpath = "";
06063 }
06064 }
06065
06066 return lpath;
06067 }
06068
06069 void MainServer::reconnectTimeout(void)
06070 {
06071 MythSocket *masterServerSock = new MythSocket();
06072
06073 QString server = gCoreContext->GetSetting("MasterServerIP", "127.0.0.1");
06074 int port = gCoreContext->GetNumSetting("MasterServerPort", 6543);
06075
06076 LOG(VB_GENERAL, LOG_NOTICE, QString("Connecting to master server: %1:%2")
06077 .arg(server).arg(port));
06078
06079 if (!masterServerSock->connect(server, port))
06080 {
06081 LOG(VB_GENERAL, LOG_NOTICE, "Connection to master server timed out.");
06082 masterServerReconnect->start(kMasterServerReconnectTimeout);
06083 masterServerSock->DownRef();
06084 return;
06085 }
06086
06087 if (masterServerSock->state() != MythSocket::Connected)
06088 {
06089 LOG(VB_GENERAL, LOG_ERR, "Could not connect to master server.");
06090 masterServerReconnect->start(kMasterServerReconnectTimeout);
06091 masterServerSock->DownRef();
06092 return;
06093 }
06094
06095 LOG(VB_GENERAL, LOG_NOTICE, "Connected successfully");
06096
06097 QString str = QString("ANN SlaveBackend %1 %2")
06098 .arg(gCoreContext->GetHostName())
06099 .arg(gCoreContext->GetBackendServerIP());
06100
06101 masterServerSock->Lock();
06102
06103 QStringList strlist( str );
06104
06105 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
06106 for (; iter != encoderList->end(); ++iter)
06107 {
06108 EncoderLink *elink = *iter;
06109 elink->CancelNextRecording(true);
06110 ProgramInfo *pinfo = elink->GetRecording();
06111 if (pinfo)
06112 {
06113 pinfo->ToStringList(strlist);
06114 delete pinfo;
06115 }
06116 else
06117 {
06118 ProgramInfo dummy;
06119 dummy.ToStringList(strlist);
06120 }
06121 }
06122
06123 if (!masterServerSock->writeStringList(strlist) ||
06124 !masterServerSock->readStringList(strlist) ||
06125 strlist.empty() || strlist[0] == "ERROR")
06126 {
06127 masterServerSock->Unlock();
06128 masterServerSock->DownRef();
06129 masterServerSock = NULL;
06130 if (strlist.empty())
06131 {
06132 LOG(VB_GENERAL, LOG_ERR, LOC +
06133 "Failed to open master server socket, timeout");
06134 }
06135 else
06136 {
06137 LOG(VB_GENERAL, LOG_ERR, LOC +
06138 "Failed to open master server socket" +
06139 ((strlist.size() >= 2) ?
06140 QString(", error was %1").arg(strlist[1]) :
06141 QString(", remote error")));
06142 }
06143 masterServerReconnect->start(kMasterServerReconnectTimeout);
06144 return;
06145 }
06146
06147 masterServerSock->setCallbacks(this);
06148
06149 masterServer = new PlaybackSock(this, masterServerSock, server,
06150 kPBSEvents_Normal);
06151 sockListLock.lockForWrite();
06152 playbackList.push_back(masterServer);
06153 sockListLock.unlock();
06154
06155 masterServerSock->Unlock();
06156
06157 autoexpireUpdateTimer->start(1000);
06158 }
06159
06160
06161
06162 bool MainServer::isClientConnected()
06163 {
06164 bool foundClient = false;
06165
06166 sockListLock.lockForRead();
06167
06168 foundClient |= !fileTransferList.empty();
06169
06170 vector<PlaybackSock *>::iterator it = playbackList.begin();
06171 for (; !foundClient && (it != playbackList.end()); ++it)
06172 {
06173
06174
06175 if (!(*it)->isSlaveBackend() && (*it)->getBlockShutdown())
06176 foundClient = true;
06177 }
06178
06179 sockListLock.unlock();
06180
06181 return (foundClient);
06182 }
06183
06185 void MainServer::ShutSlaveBackendsDown(QString &haltcmd)
06186 {
06187
06188
06189
06190 QStringList bcast( "SHUTDOWN_NOW" );
06191 bcast << haltcmd;
06192
06193 sockListLock.lockForRead();
06194
06195 vector<PlaybackSock *>::iterator it = playbackList.begin();
06196 for (; it != playbackList.end(); ++it)
06197 {
06198 if ((*it)->isSlaveBackend())
06199 (*it)->getSocket()->writeStringList(bcast);
06200 }
06201
06202 sockListLock.unlock();
06203 }
06204
06205 void MainServer::HandleSlaveDisconnectedEvent(const MythEvent &event)
06206 {
06207 if (event.ExtraDataCount() > 0 && m_sched)
06208 {
06209 bool needsReschedule = event.ExtraData(0).toUInt();
06210 for (int i = 1; i < event.ExtraDataCount(); i++)
06211 m_sched->SlaveDisconnected(event.ExtraData(i).toUInt());
06212
06213 if (needsReschedule)
06214 m_sched->ReschedulePlace("SlaveDisconnected");
06215 }
06216 }
06217
06218 void MainServer::SendSlaveDisconnectedEvent(
06219 const QList<uint> &cardids, bool needsReschedule)
06220 {
06221 QStringList extraData;
06222 extraData.push_back(
06223 QString::number(static_cast<uint>(needsReschedule)));
06224
06225 QList<uint>::const_iterator it;
06226 for (it = cardids.begin(); it != cardids.end(); ++it)
06227 extraData.push_back(QString::number(*it));
06228
06229 MythEvent me("LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE", extraData);
06230 gCoreContext->dispatch(me);
06231 }
06232
06233