MythTV  0.26-pre
mythiowrapper.cpp
Go to the documentation of this file.
00001 #if defined(_WIN32)
00002 #include <windows.h>
00003 #else
00004 #include <dlfcn.h>
00005 #endif
00006 #include <stdio.h>
00007 #include <sys/types.h>
00008 #include <sys/stat.h>
00009 #include <dirent.h>
00010 #include <fcntl.h>
00011 #include <unistd.h>
00012 
00013 #include <QFile>
00014 #include <QMap>
00015 #include <QUrl>
00016 #include <QReadWriteLock>
00017 
00018 #include "mythconfig.h"
00019 #include "compat.h"
00020 #include "mythcorecontext.h"
00021 #include "mythlogging.h"
00022 #include "remotefile.h"
00023 #include "ringbuffer.h"
00024 
00025 #include "mythiowrapper.h"
00026 
00027 #ifndef _MSC_VER
00028 #  define HAS_DIR
00029 #endif
00030 
00031 const int maxID = 1024 * 1024;
00032 
00033 QReadWriteLock            m_fileWrapperLock;
00034 QHash <int, RingBuffer *> m_ringbuffers;
00035 QHash <int, RemoteFile *> m_remotefiles;
00036 QHash <int, int>          m_localfiles;
00037 QHash <int, QString>      m_filenames;
00038 
00039 QReadWriteLock            m_dirWrapperLock;
00040 QHash <int, QStringList>  m_remotedirs;
00041 QHash <int, int>          m_remotedirPositions;
00042 QHash <int, QString>      m_dirnames;
00043 
00044 #ifdef HAS_DIR
00045 QHash <int, DIR *>        m_localdirs;
00046 #endif
00047 
00048 class Callback
00049 {
00050   public:
00051     Callback(void* object, callback_t callback)
00052       : m_object(object), m_callback(callback) { }
00053     void       *m_object;
00054     callback_t  m_callback;
00055 };
00056 
00057 QMutex                        m_callbackLock;
00058 QMultiHash<QString, Callback> m_fileOpenCallbacks;
00059 
00060 #define LOC     QString("mythiowrapper: ")
00061 
00063 
00064 extern "C" {
00065 
00066 static int getNextFileID(void)
00067 {
00068     int id = 100000;
00069 
00070     for (; id < maxID; ++id)
00071     {
00072         if ((!m_localfiles.contains(id)) &&
00073             (!m_remotefiles.contains(id)) &&
00074             (!m_ringbuffers.contains(id)))
00075             break;
00076     }
00077 
00078     if (id == maxID)
00079     {
00080         LOG(VB_GENERAL, LOG_ERR,
00081             LOC + "getNextFileID(), too many files are open.");
00082     }
00083 
00084     LOG(VB_FILE, LOG_DEBUG, LOC + QString("getNextFileID() = %1").arg(id));
00085 
00086     return id;
00087 }
00088 
00089 void mythfile_open_register_callback(const char *pathname, void* object,
00090                                      callback_t func)
00091 {
00092     m_callbackLock.lock();
00093     QString path(pathname);
00094     if (m_fileOpenCallbacks.contains(path))
00095     {
00096         // if we already have a callback registered for this path with this
00097         // object then remove the callback and return (i.e. end callback)
00098         QMutableHashIterator<QString,Callback> it(m_fileOpenCallbacks);
00099         while (it.hasNext())
00100         {
00101             it.next();
00102             if (object == it.value().m_object)
00103             {
00104                 it.remove();
00105                 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00106                     QString("Removing fileopen callback for %1").arg(path));
00107                 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00108                     QString("%1 callbacks remaining")
00109                         .arg(m_fileOpenCallbacks.size()));
00110                 m_callbackLock.unlock();
00111                 return;
00112             }
00113         }
00114     }
00115 
00116     Callback new_callback(object, func);
00117     m_fileOpenCallbacks.insert(path, new_callback);
00118     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00119         QString("Added fileopen callback for %1").arg(path));
00120     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 callbacks open")
00121         .arg(m_fileOpenCallbacks.size()));
00122 
00123     m_callbackLock.unlock();
00124 }
00125 
00126 int mythfile_check(int id)
00127 {
00128     LOG(VB_FILE, LOG_DEBUG, QString("mythfile_check(%1)").arg(id));
00129     int result = 0;
00130 
00131     m_fileWrapperLock.lockForWrite();
00132     if (m_localfiles.contains(id))
00133         result = 1;
00134     else if (m_remotefiles.contains(id))
00135         result = 1;
00136     else if (m_ringbuffers.contains(id))
00137         result = 1;
00138     m_fileWrapperLock.unlock();
00139 
00140     return result;
00141 }
00142 
00143 int mythfile_open(const char *pathname, int flags)
00144 {
00145     LOG(VB_FILE, LOG_DEBUG, QString("mythfile_open('%1', %2)")
00146             .arg(pathname).arg(flags));
00147 
00148     struct stat fileinfo;
00149     if (mythfile_stat(pathname, &fileinfo))
00150         return -1;
00151 
00152     if (S_ISDIR( fileinfo.st_mode )) // libmythdvdnav tries to open() a dir
00153         return errno = EISDIR, -1;
00154 
00155     int fileID = -1;
00156     if (strncmp(pathname, "myth://", 7))
00157     {
00158         int lfd = open(pathname, flags);
00159         if (lfd < 0)
00160             return -1;
00161 
00162         m_fileWrapperLock.lockForWrite();
00163         fileID = getNextFileID();
00164         m_localfiles[fileID] = lfd;
00165         m_filenames[fileID] = pathname;
00166         m_fileWrapperLock.unlock();
00167     }
00168     else
00169     {
00170         RingBuffer *rb = NULL;
00171         RemoteFile *rf = NULL;
00172 
00173         if ((fileinfo.st_size < 512) &&
00174             (fileinfo.st_mtime < (time(NULL) - 300)))
00175         {
00176             if (flags & O_WRONLY)
00177                 rf = new RemoteFile(pathname, true, false); // Writeable
00178             else
00179                 rf = new RemoteFile(pathname, false, true); // Read-Only
00180 
00181             if (!rf)
00182                 return -1;
00183         }
00184         else
00185         {
00186             if (flags & O_WRONLY)
00187                 rb = RingBuffer::Create(
00188                     pathname, true, false,
00189                     RingBuffer::kDefaultOpenTimeout, true); // Writeable
00190             else
00191                 rb = RingBuffer::Create(
00192                     pathname, false, true,
00193                     RingBuffer::kDefaultOpenTimeout, true); // Read-Only
00194 
00195             if (!rb)
00196                 return -1;
00197 
00198             rb->Start();
00199         }
00200 
00201         m_fileWrapperLock.lockForWrite();
00202         fileID = getNextFileID();
00203 
00204         if (rf)
00205             m_remotefiles[fileID] = rf;
00206         else if (rb)
00207             m_ringbuffers[fileID] = rb;
00208 
00209         m_filenames[fileID] = pathname;
00210         m_fileWrapperLock.unlock();
00211     }
00212 
00213     m_callbackLock.lock();
00214     if (!m_fileOpenCallbacks.isEmpty())
00215     {
00216         QString path(pathname);
00217         QHashIterator<QString,Callback> it(m_fileOpenCallbacks);
00218         while (it.hasNext())
00219         {
00220             it.next();
00221             if (path.startsWith(it.key()))
00222                 it.value().m_callback(it.value().m_object);
00223         }
00224     }
00225     m_callbackLock.unlock();
00226 
00227     return fileID;
00228 }
00229 
00230 int mythfile_close(int fileID)
00231 {
00232     int result = -1;
00233 
00234     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythfile_close(%1)").arg(fileID));
00235 
00236     m_fileWrapperLock.lockForRead();
00237     if (m_ringbuffers.contains(fileID))
00238     {
00239         RingBuffer *rb = m_ringbuffers[fileID];
00240         m_ringbuffers.remove(fileID);
00241         delete rb;
00242 
00243         result = 0;
00244     }
00245     else if (m_remotefiles.contains(fileID))
00246     {
00247         RemoteFile *rf = m_remotefiles[fileID];
00248         m_remotefiles.remove(fileID);
00249         delete rf;
00250 
00251         result = 0;
00252     }
00253     else if (m_localfiles.contains(fileID))
00254     {
00255         close(m_localfiles[fileID]);
00256         m_localfiles.remove(fileID);
00257         result = 0;
00258     }
00259     m_fileWrapperLock.unlock();
00260 
00261     return result;
00262 }
00263 
00264 #ifdef USING_MINGW
00265 #   undef  lseek
00266 #   define lseek  _lseeki64
00267 #   undef  off_t
00268 #   define off_t off64_t
00269 #endif
00270 off_t mythfile_seek(int fileID, off_t offset, int whence)
00271 {
00272     off_t result = -1;
00273 
00274     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythfile_seek(%1, %2, %3)")
00275                                       .arg(fileID).arg(offset).arg(whence));
00276 
00277     m_fileWrapperLock.lockForRead();
00278     if (m_ringbuffers.contains(fileID))
00279         result = m_ringbuffers[fileID]->Seek(offset, whence);
00280     else if (m_remotefiles.contains(fileID))
00281         result = m_remotefiles[fileID]->Seek(offset, whence);
00282     else if (m_localfiles.contains(fileID))
00283         result = lseek(m_localfiles[fileID], offset, whence);
00284     m_fileWrapperLock.unlock();
00285 
00286     return result;
00287 }
00288 
00289 off_t mythfile_tell(int fileID)
00290 {
00291     off_t result = -1;
00292 
00293     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythfile_tell(%1)").arg(fileID));
00294 
00295     m_fileWrapperLock.lockForRead();
00296     if (m_ringbuffers.contains(fileID))
00297         result = m_ringbuffers[fileID]->Seek(0, SEEK_CUR);
00298     else if (m_remotefiles.contains(fileID))
00299         result = m_remotefiles[fileID]->Seek(0, SEEK_CUR);
00300     else if (m_localfiles.contains(fileID))
00301         result = lseek(m_localfiles[fileID], 0, SEEK_CUR);
00302     m_fileWrapperLock.unlock();
00303 
00304     return result;
00305 }
00306 #ifdef USING_MINGW
00307 #   undef  lseek
00308 #   undef  off_t
00309 #endif
00310 
00311 ssize_t mythfile_read(int fileID, void *buf, size_t count)
00312 {
00313     ssize_t result = -1;
00314 
00315     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythfile_read(%1, %2, %3)")
00316             .arg(fileID) .arg((long long)buf).arg(count));
00317 
00318     m_fileWrapperLock.lockForRead();
00319     if (m_ringbuffers.contains(fileID))
00320         result = m_ringbuffers[fileID]->Read(buf, count);
00321     else if (m_remotefiles.contains(fileID))
00322         result = m_remotefiles[fileID]->Read(buf, count);
00323     else if (m_localfiles.contains(fileID))
00324         result = read(m_localfiles[fileID], buf, count);
00325     m_fileWrapperLock.unlock();
00326 
00327     return result;
00328 }
00329 
00330 ssize_t mythfile_write(int fileID, void *buf, size_t count)
00331 {
00332     ssize_t result = -1;
00333 
00334     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythfile_write(%1, %2, %3)")
00335             .arg(fileID) .arg((long long)buf).arg(count));
00336 
00337     m_fileWrapperLock.lockForRead();
00338     if (m_ringbuffers.contains(fileID))
00339         result = m_ringbuffers[fileID]->Write(buf, count);
00340     else if (m_remotefiles.contains(fileID))
00341         result = m_remotefiles[fileID]->Write(buf, count);
00342     else if (m_localfiles.contains(fileID))
00343         result = write(m_localfiles[fileID], buf, count);
00344     m_fileWrapperLock.unlock();
00345 
00346     return result;
00347 }
00348 
00349 int mythfile_stat(const char *path, struct stat *buf)
00350 {
00351     LOG(VB_FILE, LOG_DEBUG, QString("mythfile_stat('%1', %2)")
00352             .arg(path).arg((long long)buf));
00353 
00354     if (!strncmp(path, "myth://", 7))
00355     {
00356         bool res = RemoteFile::Exists(path, buf);
00357         if (res)
00358             return 0;
00359     }
00360 
00361     return stat(path, buf);
00362 }
00363 
00364 int mythfile_stat_fd(int fileID, struct stat *buf)
00365 {
00366     LOG(VB_FILE, LOG_DEBUG, QString("mythfile_stat_fd(%1, %2)")
00367             .arg(fileID).arg((long long)buf));
00368 
00369     m_fileWrapperLock.lockForRead();
00370     if (!m_filenames.contains(fileID))
00371     {
00372         m_fileWrapperLock.unlock();
00373         return -1;
00374     }
00375     QString filename = m_filenames[fileID];
00376     m_fileWrapperLock.unlock();
00377 
00378     return mythfile_stat(filename.toLocal8Bit().constData(), buf);
00379 }
00380 
00381 int mythfile_exists(const char *path, const char *file)
00382 {
00383     LOG(VB_FILE, LOG_DEBUG, QString("mythfile_exists('%1', '%2')")
00384             .arg(path).arg(file));
00385 
00386     if (!strncmp(path, "myth://", 7))
00387         return RemoteFile::Exists(QString("%1/%2").arg(path).arg(file));
00388 
00389     return QFile::exists(QString("%1/%2").arg(path).arg(file));
00390 }
00391 
00393 
00394 #ifdef HAS_DIR
00395 
00396 static int getNextDirID(void)
00397 {
00398     int id = 100000;
00399 
00400     for (; id < maxID; ++id)
00401     {
00402         if (!m_localdirs.contains(id) && !m_remotedirs.contains(id))
00403             break;
00404     }
00405 
00406     if (id == maxID)
00407         LOG(VB_GENERAL, LOG_ERR, "ERROR: mythiowrapper getNextDirID(), too "
00408                                  "many files are open.");
00409 
00410     LOG(VB_FILE, LOG_DEBUG, LOC + QString("getNextDirID() = %1").arg(id));
00411 
00412     return id;
00413 }
00414 
00415 int mythdir_check(int id)
00416 {
00417     LOG(VB_FILE, LOG_DEBUG, QString("mythdir_check(%1)").arg(id));
00418     int result = 0;
00419 
00420     m_dirWrapperLock.lockForWrite();
00421     if (m_localdirs.contains(id))
00422         result = 1;
00423     else if (m_remotedirs.contains(id))
00424         result = 1;
00425     m_dirWrapperLock.unlock();
00426 
00427     return result;
00428 }
00429 
00430 int mythdir_opendir(const char *dirname)
00431 {
00432     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythdir_opendir(%1)").arg(dirname));
00433 
00434     int id = 0;
00435     if (strncmp(dirname, "myth://", 7))
00436     {
00437         DIR *dir = opendir(dirname);
00438 
00439         m_dirWrapperLock.lockForWrite();
00440         id = getNextDirID();
00441         m_localdirs[id] = dir;
00442         m_dirnames[id] = dirname;
00443         m_dirWrapperLock.unlock();
00444     }
00445     else
00446     {
00447         QStringList list;
00448         QUrl qurl(dirname);
00449         QString storageGroup = qurl.userName();
00450 
00451         list.clear();
00452 
00453         if (storageGroup.isEmpty())
00454             storageGroup = "Default";
00455 
00456         list << "QUERY_SG_GETFILELIST";
00457         list << qurl.host();
00458         list << storageGroup;
00459 
00460         QString path = qurl.path();
00461         if (!qurl.fragment().isEmpty())
00462             path += "#" + qurl.fragment();
00463 
00464         list << path;
00465         list << "1";
00466 
00467         bool ok = gCoreContext->SendReceiveStringList(list);
00468 
00469         if ((!ok) ||
00470             ((list.size() == 1) && (list[0] == "EMPTY LIST")))
00471             list.clear();
00472 
00473         m_dirWrapperLock.lockForWrite();
00474         id = getNextDirID();
00475         m_remotedirs[id] = list;
00476         m_remotedirPositions[id] = 0;
00477         m_dirnames[id] = dirname;
00478         m_dirWrapperLock.unlock();
00479     }
00480 
00481     return id;
00482 }
00483 
00484 int mythdir_closedir(int dirID)
00485 {
00486     int result = -1;
00487 
00488     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythdir_closedir(%1)").arg(dirID));
00489 
00490     m_dirWrapperLock.lockForRead();
00491     if (m_remotedirs.contains(dirID))
00492     {
00493         m_remotedirs.remove(dirID);
00494         m_remotedirPositions.remove(dirID);
00495         result = 0;
00496     }
00497     else if (m_localdirs.contains(dirID))
00498     {
00499         result = closedir(m_localdirs[dirID]);
00500 
00501         if (result == 0)
00502             m_localdirs.remove(dirID);
00503     }
00504     m_dirWrapperLock.unlock();
00505 
00506     return result;
00507 }
00508 
00509 char *mythdir_readdir(int dirID)
00510 {
00511     char *result = NULL;
00512 
00513     LOG(VB_FILE, LOG_DEBUG, LOC + QString("mythdir_readdir(%1)").arg(dirID));
00514 
00515     m_dirWrapperLock.lockForRead();
00516     if (m_remotedirs.contains(dirID))
00517     {
00518         int pos = m_remotedirPositions[dirID];
00519         if (m_remotedirs[dirID].size() >= (pos+1))
00520         {
00521             result = strdup(m_remotedirs[dirID][pos].toLocal8Bit().constData());
00522             pos++;
00523             m_remotedirPositions[dirID] = pos;
00524         }
00525     }
00526     else if (m_localdirs.contains(dirID))
00527     {
00528         int sz = offsetof(struct dirent, d_name) + FILENAME_MAX + 1;
00529         struct dirent *entry =
00530             reinterpret_cast<struct dirent*>(calloc(1, sz));
00531         struct dirent *r = NULL;
00532         if ((0 == readdir_r(m_localdirs[dirID], entry, &r)) && (NULL != r))
00533             result = strdup(r->d_name);
00534         free(entry);
00535     }
00536     m_dirWrapperLock.unlock();
00537 
00538     return result;
00539 }
00540 #endif
00541 } // extern "C"
00542 
00544 
00545 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends