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
00097
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 ))
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);
00178 else
00179 rf = new RemoteFile(pathname, false, true);
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);
00190 else
00191 rb = RingBuffer::Create(
00192 pathname, false, true,
00193 RingBuffer::kDefaultOpenTimeout, true);
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 }
00542
00544
00545