|
MythTV
0.26-pre
|
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: */
1.7.6.1