MythTV  0.26-pre
remoteutil.cpp
Go to the documentation of this file.
00001 #include <unistd.h>
00002 
00003 #include <QFileInfo>
00004 #include <QFile>
00005 #include <QDir>
00006 #include <QList>
00007 
00008 #include "compat.h"
00009 #include "remoteutil.h"
00010 #include "programinfo.h"
00011 #include "mythcorecontext.h"
00012 #include "storagegroup.h"
00013 #include "mythevent.h"
00014 #include "mythsocket.h"
00015 
00016 vector<ProgramInfo *> *RemoteGetRecordedList(int sort)
00017 {
00018     QString str = "QUERY_RECORDINGS ";
00019     if (sort < 0)
00020         str += "Descending";
00021     else if (sort > 0)
00022         str += "Ascending";
00023     else
00024         str += "Unsorted";
00025 
00026     QStringList strlist(str);
00027 
00028     vector<ProgramInfo *> *info = new vector<ProgramInfo *>;
00029 
00030     if (!RemoteGetRecordingList(*info, strlist))
00031     {
00032         delete info;
00033         return NULL;
00034     }
00035  
00036     return info;
00037 }
00038 
00039 bool RemoteGetLoad(float load[3])
00040 {
00041     QStringList strlist(QString("QUERY_LOAD"));
00042 
00043     if (gCoreContext->SendReceiveStringList(strlist))
00044     {
00045         load[0] = strlist[0].toFloat();
00046         load[1] = strlist[1].toFloat();
00047         load[2] = strlist[2].toFloat();
00048         return true;
00049     }
00050 
00051     return false;
00052 }
00053 
00054 bool RemoteGetUptime(time_t &uptime)
00055 {
00056     QStringList strlist(QString("QUERY_UPTIME"));
00057 
00058     if (!gCoreContext->SendReceiveStringList(strlist))
00059         return false;
00060 
00061     if (!strlist[0].at(0).isNumber())
00062         return false;
00063 
00064     if (sizeof(time_t) == sizeof(int))
00065         uptime = strlist[0].toUInt();
00066     else if (sizeof(time_t) == sizeof(long))
00067         uptime = strlist[0].toULong();
00068     else if (sizeof(time_t) == sizeof(long long))
00069         uptime = strlist[0].toULongLong();
00070 
00071     return true;
00072 }
00073 
00074 bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
00075 {
00076     QStringList strlist(QString("QUERY_MEMSTATS"));
00077 
00078     if (gCoreContext->SendReceiveStringList(strlist))
00079     {
00080         totalMB = strlist[0].toInt();
00081         freeMB  = strlist[1].toInt();
00082         totalVM = strlist[2].toInt();
00083         freeVM  = strlist[3].toInt();
00084         return true;
00085     }
00086 
00087     return false;
00088 }
00089 
00090 bool RemoteCheckFile(const ProgramInfo *pginfo, bool checkSlaves)
00091 {
00092     QStringList strlist("QUERY_CHECKFILE");
00093     strlist << QString::number((int)checkSlaves);
00094     pginfo->ToStringList(strlist);
00095 
00096     if ((!gCoreContext->SendReceiveStringList(strlist)) ||
00097         (!strlist[0].toInt()))
00098         return false;
00099 
00100     // Only modify the pathname if the recording file is available locally on
00101     // this host
00102     QString localpath = strlist[1];
00103     QFile checkFile(localpath);
00104     if (checkFile.exists())
00105         pginfo->SetPathname(localpath);
00106 
00107     return true;
00108 }
00109 
00110 bool RemoteDeleteRecording(
00111     uint chanid, const QDateTime &recstartts, bool forceMetadataDelete,
00112     bool forgetHistory)
00113 {
00114     bool result = true;
00115     QString cmd =
00116         QString("DELETE_RECORDING %1 %2 %3 %4")
00117         .arg(chanid)
00118         .arg(recstartts.toString(Qt::ISODate))
00119         .arg(forceMetadataDelete ? "FORCE" : "NO_FORCE")
00120         .arg(forgetHistory ? "FORGET" : "NO_FORGET");
00121     QStringList strlist(cmd);
00122 
00123     if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
00124         result = false;
00125     else if (strlist[0].toInt() == -2)
00126         result = false;
00127 
00128     if (!result)
00129     {
00130         LOG(VB_GENERAL, LOG_ALERT, 
00131                  QString("Failed to delete recording %1:%2")
00132                      .arg(chanid).arg(recstartts.toString(Qt::ISODate)));
00133     }
00134 
00135     return result;
00136 }
00137 
00138 bool RemoteUndeleteRecording(uint chanid, const QDateTime &recstartts)
00139 {
00140     bool result = false;
00141 
00142 #if 0
00143     if (!gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0))
00144         return result;
00145 #endif
00146 
00147     QStringList strlist(QString("UNDELETE_RECORDING"));
00148     strlist.push_back(QString::number(chanid));
00149     strlist.push_back(recstartts.toString(Qt::ISODate));
00150 
00151     gCoreContext->SendReceiveStringList(strlist);
00152 
00153     if (strlist[0].toInt() == 0)
00154         result = true;
00155 
00156     return result;
00157 }
00158 
00159 void RemoteGetAllScheduledRecordings(vector<ProgramInfo *> &scheduledlist)
00160 {
00161     QStringList strList(QString("QUERY_GETALLSCHEDULED"));
00162     RemoteGetRecordingList(scheduledlist, strList);
00163 }
00164 
00165 void RemoteGetAllExpiringRecordings(vector<ProgramInfo *> &expiringlist)
00166 {
00167     QStringList strList(QString("QUERY_GETEXPIRING"));
00168     RemoteGetRecordingList(expiringlist, strList);
00169 }
00170 
00171 uint RemoteGetRecordingList(
00172     vector<ProgramInfo *> &reclist, QStringList &strList)
00173 {
00174     if (!gCoreContext->SendReceiveStringList(strList))
00175         return 0;
00176 
00177     int numrecordings = strList[0].toInt();
00178     if (numrecordings <= 0)
00179         return 0;
00180 
00181     if (numrecordings * NUMPROGRAMLINES + 1 > (int)strList.size())
00182     {
00183         LOG(VB_GENERAL, LOG_ERR, 
00184                  "RemoteGetRecordingList() list size appears to be incorrect.");
00185         return 0;
00186     }
00187 
00188     uint reclist_initial_size = (uint) reclist.size();
00189     QStringList::const_iterator it = strList.begin() + 1;
00190     for (int i = 0; i < numrecordings; i++)
00191     {
00192         ProgramInfo *pginfo = new ProgramInfo(it, strList.end());
00193             reclist.push_back(pginfo);
00194     }
00195 
00196     return ((uint) reclist.size()) - reclist_initial_size;
00197 }
00198 
00199 vector<ProgramInfo *> *RemoteGetConflictList(const ProgramInfo *pginfo)
00200 {
00201     QString cmd = QString("QUERY_GETCONFLICTING");
00202     QStringList strlist( cmd );
00203     pginfo->ToStringList(strlist);
00204 
00205     vector<ProgramInfo *> *retlist = new vector<ProgramInfo *>;
00206 
00207     RemoteGetRecordingList(*retlist, strlist);
00208     return retlist;
00209 }
00210 
00211 QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo)
00212 {
00213     QDateTime retdatetime;
00214 
00215     QStringList strlist( "QUERY_PIXMAP_LASTMODIFIED" );
00216     pginfo->ToStringList(strlist);
00217     
00218     if (!gCoreContext->SendReceiveStringList(strlist))
00219         return retdatetime;
00220 
00221     if (!strlist.empty() && strlist[0] != "BAD")
00222     {
00223         uint timet = strlist[0].toUInt();
00224         retdatetime.setTime_t(timet);
00225     }
00226         
00227     return retdatetime;
00228 }
00229 
00232 QDateTime RemoteGetPreviewIfModified(
00233     const ProgramInfo &pginfo, const QString &cachefile)
00234 {
00235     QString loc("RemoteGetPreviewIfModified: ");
00236     QDateTime cacheLastModified;
00237     QFileInfo cachefileinfo(cachefile);
00238     if (cachefileinfo.exists())
00239         cacheLastModified = cachefileinfo.lastModified();
00240 
00241     QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED");
00242     strlist << ((cacheLastModified.isValid()) ? // unix secs, UTC
00243                 QString::number(cacheLastModified.toTime_t()) : QString("-1"));
00244     strlist << QString::number(200 * 1024); // max size of preview file
00245     pginfo.ToStringList(strlist);
00246 
00247     if (!gCoreContext->SendReceiveStringList(strlist) ||
00248         strlist.empty() || strlist[0] == "ERROR")
00249     {
00250         LOG(VB_GENERAL, LOG_ERR, loc + "Remote error" +
00251             ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
00252 
00253         return QDateTime();
00254     }
00255 
00256     if (strlist[0] == "WARNING")
00257     {
00258         LOG(VB_NETWORK, LOG_WARNING, loc + "Remote warning" +
00259                  ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
00260 
00261         return QDateTime();
00262     }
00263 
00264     QDateTime retdatetime;
00265     qlonglong timet = strlist[0].toLongLong();
00266     if (timet >= 0)
00267         retdatetime.setTime_t(timet);
00268 
00269     if (strlist.size() < 4)
00270     {
00271         return retdatetime;
00272     }
00273 
00274     size_t  length     = strlist[1].toULongLong();
00275     quint16 checksum16 = strlist[2].toUInt();
00276     QByteArray data = QByteArray::fromBase64(strlist[3].toAscii());
00277     if ((size_t) data.size() < length)
00278     { // (note data.size() may be up to 3 bytes longer after decoding
00279         LOG(VB_GENERAL, LOG_ERR, loc +
00280             QString("Preview size check failed %1 < %2")
00281                 .arg(data.size()).arg(length));
00282         return QDateTime();
00283     }
00284     data.resize(length);
00285 
00286     if (checksum16 != qChecksum(data.constData(), data.size()))
00287     {
00288         LOG(VB_GENERAL, LOG_ERR, loc + "Preview checksum failed");
00289         return QDateTime();
00290     }
00291 
00292     QString pdir(cachefile.section("/", 0, -2));
00293     QDir cfd(pdir);
00294     if (!cfd.exists() && !cfd.mkdir(pdir))
00295     {
00296         LOG(VB_GENERAL, LOG_ERR, loc +
00297             QString("Unable to create remote cache directory '%1'")
00298                 .arg(pdir));
00299 
00300         return QDateTime();
00301     }
00302 
00303     QFile file(cachefile);
00304     if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
00305     {
00306         LOG(VB_GENERAL, LOG_ERR, loc +
00307             QString("Unable to open cached preview file for writing '%1'")
00308                 .arg(cachefile));
00309 
00310         return QDateTime();
00311     }
00312 
00313     off_t offset = 0;
00314     size_t remaining = length;
00315     uint failure_cnt = 0;
00316     while ((remaining > 0) && (failure_cnt < 5))
00317     {
00318         ssize_t written = file.write(data.data() + offset, remaining);
00319         if (written < 0)
00320         {
00321             failure_cnt++;
00322             usleep(50000);
00323             continue;
00324         }
00325 
00326         failure_cnt  = 0;
00327         offset      += written;
00328         remaining   -= written;
00329     }
00330 
00331     if (remaining)
00332     {
00333         LOG(VB_GENERAL, LOG_ERR, loc +
00334             QString("Failed to write cached preview file '%1'")
00335                 .arg(cachefile));
00336 
00337         file.resize(0); // in case unlink fails..
00338         file.remove();  // closes fd
00339         return QDateTime();
00340     }
00341 
00342     file.close();
00343 
00344     return retdatetime;
00345 }
00346 
00347 bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
00348 {
00349     QStringList strlist( "FILL_PROGRAM_INFO" );
00350     strlist << playbackhost;
00351     pginfo.ToStringList(strlist);
00352 
00353     if (gCoreContext->SendReceiveStringList(strlist))
00354     {
00355         ProgramInfo tmp(strlist);
00356         if (tmp.HasPathname() || tmp.GetChanID())
00357         {
00358             pginfo = tmp;
00359             return true;
00360         }
00361     }
00362 
00363     return false;
00364 }
00365 
00366 QStringList RemoteRecordings(void)
00367 {
00368     QStringList strlist("QUERY_ISRECORDING");
00369 
00370     if (!gCoreContext->SendReceiveStringList(strlist, false, false))
00371     {
00372         QStringList empty;
00373         empty << "0" << "0";
00374         return empty;
00375     }
00376 
00377     return strlist;
00378 }
00379 
00380 int RemoteGetRecordingMask(void)
00381 {
00382     int mask = 0;
00383 
00384     QString cmd = "QUERY_ISRECORDING";
00385 
00386     QStringList strlist( cmd );
00387 
00388     if (!gCoreContext->SendReceiveStringList(strlist))
00389         return mask;
00390 
00391     if (strlist.empty())
00392         return 0;
00393 
00394     int recCount = strlist[0].toInt();
00395 
00396     for (int i = 0, j = 0; j < recCount; i++)
00397     {
00398         cmd = QString("QUERY_RECORDER %1").arg(i + 1);
00399 
00400         strlist = QStringList( cmd );
00401         strlist << "IS_RECORDING";
00402 
00403         if (gCoreContext->SendReceiveStringList(strlist) && !strlist.empty())
00404         {
00405             if (strlist[0].toInt())
00406             {
00407                 mask |= 1<<i;
00408                 j++;       // count active recorder
00409             }
00410         }
00411         else
00412         {
00413             break;
00414         }
00415     }
00416 
00417     return mask;
00418 }
00419 
00420 int RemoteGetFreeRecorderCount(void)
00421 {
00422     QStringList strlist( "GET_FREE_RECORDER_COUNT" );
00423 
00424     if (!gCoreContext->SendReceiveStringList(strlist, true))
00425         return 0;
00426 
00427     if (strlist.empty())
00428         return 0;
00429 
00430     if (strlist[0] == "UNKNOWN_COMMAND")
00431     {
00432         LOG(VB_GENERAL, LOG_EMERG,
00433                  "Unknown command GET_FREE_RECORDER_COUNT, upgrade your "
00434                  "backend version.");
00435         return 0;
00436     }
00437 
00438     return strlist[0].toInt();
00439 }
00440 
00441 bool RemoteGetFileList(QString host, QString path, QStringList* list,
00442                        QString sgroup, bool fileNamesOnly)
00443 {
00444 
00445     // Make sure the list is empty when we get started
00446     list->clear();
00447 
00448     if (sgroup.isEmpty())
00449         sgroup = "Videos";
00450 
00451     *list << "QUERY_SG_GETFILELIST";
00452     *list << host;
00453     *list << StorageGroup::GetGroupToUse(host, sgroup);
00454     *list << path;
00455     *list << QString::number(fileNamesOnly);
00456 
00457     bool ok = false;
00458 
00459     if (gCoreContext->IsMasterBackend())
00460     {
00461         // since the master backend cannot connect back around to
00462         // itself, and the libraries do not have access to the list
00463         // of connected slave backends to query an existing connection
00464         // start up a new temporary connection directly to the slave
00465         // backend to query the file list
00466         QString ann = QString("ANN Playback %1 0")
00467                         .arg(gCoreContext->GetHostName());
00468         QString addr = gCoreContext->GetBackendServerIP(host);
00469         int port = gCoreContext->GetNumSettingOnHost("BackendServerPort",
00470                                                      host, 6543);
00471         bool mismatch = false;
00472 
00473         MythSocket *sock = gCoreContext->ConnectCommandSocket(
00474                                             addr, port, ann, &mismatch);
00475         if (sock)
00476         {
00477             ok = sock->SendReceiveStringList(*list);
00478             sock->DownRef();
00479         }
00480         else
00481             list->clear();
00482     }
00483     else
00484         ok = gCoreContext->SendReceiveStringList(*list);
00485 
00486 // Should the SLAVE UNREACH test be here ?
00487     return ok;
00488 }
00489 
00495 int RemoteCheckForRecording(const ProgramInfo *pginfo)
00496 {
00497     QStringList strlist( QString("CHECK_RECORDING") );
00498     pginfo->ToStringList(strlist);
00499 
00500     if (gCoreContext->SendReceiveStringList(strlist) && !strlist.empty())
00501         return strlist[0].toInt();
00502 
00503     return 0;
00504 }
00505 
00514 int RemoteGetRecordingStatus(
00515     const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
00516 {
00517     QDateTime curtime = QDateTime::currentDateTime();
00518 
00519     int retval = 0;
00520 
00521     if (pginfo)
00522     {
00523         if (curtime >= pginfo->GetScheduledStartTime().addSecs(-underrecsecs) &&
00524             curtime < pginfo->GetScheduledEndTime().addSecs(overrecsecs))
00525         {
00526             if (curtime >= pginfo->GetScheduledStartTime() &&
00527                 curtime < pginfo->GetScheduledEndTime())
00528                 retval = 1;
00529             else if (curtime < pginfo->GetScheduledStartTime() && 
00530                      RemoteCheckForRecording(pginfo) > 0)
00531                 retval = 2;
00532             else if (curtime > pginfo->GetScheduledEndTime() && 
00533                      RemoteCheckForRecording(pginfo) > 0)
00534                 retval = 3;
00535         }
00536     }
00537 
00538     return retval;
00539 }
00540 
00544 vector<ProgramInfo *> *RemoteGetCurrentlyRecordingList(void)
00545 {
00546     QString str = "QUERY_RECORDINGS ";
00547     str += "Recording";
00548     QStringList strlist( str );
00549 
00550     vector<ProgramInfo *> *reclist = new vector<ProgramInfo *>;
00551     vector<ProgramInfo *> *info = new vector<ProgramInfo *>;
00552     if (!RemoteGetRecordingList(*info, strlist))
00553     {
00554         if (info)
00555             delete info;
00556         return reclist;
00557     }
00558 
00559     ProgramInfo *p = NULL;
00560     vector<ProgramInfo *>::iterator it = info->begin();
00561     // make sure whatever RemoteGetRecordingList() returned
00562     // only has rsRecording shows
00563     for ( ; it != info->end(); ++it)
00564     {
00565         p = *it;
00566         if (p->GetRecordingStatus() == rsRecording ||
00567             (p->GetRecordingStatus() == rsRecorded &&
00568              p->GetRecordingGroup() == "LiveTV"))
00569         {
00570             reclist->push_back(new ProgramInfo(*p));
00571         }
00572     }
00573     
00574     while (!info->empty())
00575     {
00576         delete info->back();
00577         info->pop_back();
00578     }
00579     if (info)
00580         delete info;
00581 
00582     return reclist; 
00583 }
00584 
00588 bool RemoteGetActiveBackends(QStringList *list)
00589 {
00590     list->clear();
00591     *list << "QUERY_ACTIVE_BACKENDS";
00592 
00593     if (!gCoreContext->SendReceiveStringList(*list))
00594         return false;
00595 
00596     list->removeFirst();
00597     return true;
00598 }
00599 
00600 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends