int SSDPCache::RemoveStale() { int nCount = 0; TaskTime ttNow; QStringList lstKeys; gettimeofday( (&ttNow), NULL ); Lock(); // ---------------------------------------------------------------------- // Iterate through all Type URI's and build list of stale entries keys // ---------------------------------------------------------------------- for (SSDPCacheEntriesMap::Iterator it = m_cache.begin(); it != m_cache.end(); ++it ) { SSDPCacheEntries *pEntries = *it; if (pEntries != NULL) { pEntries->AddRef(); nCount += pEntries->RemoveStale( ttNow ); if (pEntries->Count() == 0) lstKeys.append( it.key() ); pEntries->Release(); } } Unlock(); nCount = lstKeys.count(); // ---------------------------------------------------------------------- // Iterate through list of keys and remove them. // (This avoids issues when removing from a QMap while iterating it) // ---------------------------------------------------------------------- for ( QStringList::Iterator itKey = lstKeys.begin(); itKey != lstKeys.end(); ++itKey ) { SSDPCacheEntriesMap::iterator it = m_cache.find( *itKey ); if (it == m_cache.end()) continue; if (*it) { (*it)->Release(); m_cache.erase(it); } } return nCount; }
void BackendSelection::Init(void) { SSDPCacheEntries *pEntries = SSDPCache::Instance()->Find(gBackendURI); if (pEntries) { EntryMap ourMap; pEntries->GetEntryMap(ourMap); pEntries->Release(); EntryMap::const_iterator it; for (it = ourMap.begin(); it != ourMap.end(); ++it) AddItem(*it); // this does an (*it)->Release() } }
DeviceLocation *SSDPCache::Find( const QString &sURI, const QString &sUSN ) { DeviceLocation *pEntry = NULL; SSDPCacheEntries *pEntries = Find( sURI ); if (pEntries != NULL) { pEntries->AddRef(); pEntry = pEntries->Find( sUSN ); pEntries->Release(); } return pEntry; }
void SSDPCache::Add( const QString &sURI, const QString &sUSN, const QString &sLocation, long sExpiresInSecs ) { // -------------------------------------------------------------- // Calculate when this cache entry should expire. // -------------------------------------------------------------- TaskTime ttExpires; gettimeofday ( (&ttExpires), NULL ); AddSecondsToTaskTime( ttExpires, sExpiresInSecs ); // -------------------------------------------------------------- // Get a Pointer to a Entries QDict... (Create if not found) // -------------------------------------------------------------- SSDPCacheEntries *pEntries = Find( sURI ); if (pEntries == NULL) { pEntries = new SSDPCacheEntries(); pEntries->AddRef(); m_cache.insert( sURI, pEntries ); } pEntries->AddRef(); // -------------------------------------------------------------- // See if the Entries Collection contains our USN... (Create if not found) // -------------------------------------------------------------- DeviceLocation *pEntry = pEntries->Find( sUSN ); if (pEntry == NULL) { pEntry = new DeviceLocation( sURI, sUSN, sLocation, ttExpires ); Lock(); pEntries->Insert( sUSN, pEntry ); Unlock(); NotifyAdd( sURI, sUSN, sLocation ); } else { pEntry->AddRef(); pEntry->m_sLocation = sLocation; pEntry->m_ttExpires = ttExpires; pEntry->Release(); } pEntries->Release(); }
void BackendSelection::Init(void) { EntryMap::Iterator it; EntryMap ourMap; DeviceLocation *pDevLoc; SSDPCacheEntries *pEntries = SSDPCache::Instance()->Find( gBackendURI ); if (!pEntries) { VERBOSE(VB_GENERAL, "Found zero backends, bailing"); return; } pEntries->AddRef(); pEntries->Lock(); EntryMap *pMap = pEntries->GetEntryMap(); for (it = pMap->begin(); it != pMap->end(); ++it) { pDevLoc = (DeviceLocation *)*it; if (!pDevLoc) continue; pDevLoc->AddRef(); ourMap.insert(pDevLoc->m_sUSN, pDevLoc); } pEntries->Unlock(); pEntries->Release(); for (it = ourMap.begin(); it != ourMap.end(); ++it) { pDevLoc = (DeviceLocation *)*it; AddItem(pDevLoc); // this does a Release() } }
void SSDPCache::Remove( const QString &sURI, const QString &sUSN ) { Lock(); // -------------------------------------------------------------- // Get a Pointer to a Entries QDict... (Create if not found) // -------------------------------------------------------------- SSDPCacheEntriesMap::Iterator it = m_cache.find( sURI ); if (it != m_cache.end()) { SSDPCacheEntries *pEntries = *it; if (pEntries != NULL) { pEntries->AddRef(); pEntries->Remove( sUSN ); if (pEntries->Count() == 0) { pEntries->Release(); m_cache.erase(it); } pEntries->Release(); } } Unlock(); // -=>TODO: // Should this only by notified if we actually had any entry removed? NotifyRemove( sURI, sUSN ); }
void SSDPCache::Dump() { int nCount = 0; if (VERBOSE_LEVEL_CHECK(VB_UPNP)) { Lock(); // ---------------------------------------------------------------------- // Build List of items to be removed // ---------------------------------------------------------------------- VERBOSE( VB_UPNP, "===============================================================================" ); VERBOSE( VB_UPNP, QString( " URI (type) - Found: %1 Entries - %2 have been Allocated. " ) .arg( m_cache.count() ) .arg( SSDPCacheEntries::g_nAllocated )); VERBOSE( VB_UPNP, " \t\tUSN (unique id)\t\t | Expires\t | Location" ); VERBOSE( VB_UPNP, "-------------------------------------------------------------------------------" ); for (SSDPCacheEntriesMap::Iterator it = m_cache.begin(); it != m_cache.end(); ++it ) { SSDPCacheEntries *pEntries = *it; if (pEntries != NULL) { VERBOSE( VB_UPNP, it.key() ); pEntries->Lock(); EntryMap *pMap = pEntries->GetEntryMap(); for (EntryMap::Iterator itEntry = pMap->begin(); itEntry != pMap->end(); ++itEntry ) { DeviceLocation *pEntry = *itEntry; if (pEntry != NULL) { nCount++; pEntry->AddRef(); VERBOSE( VB_UPNP, QString( " * \t\t%1\t | %2\t | %3 " ) .arg( pEntry->m_sUSN ) .arg( pEntry->ExpiresInSecs() ) .arg( pEntry->m_sLocation )); pEntry->Release(); } } VERBOSE( VB_UPNP, " "); pEntries->Unlock(); } } VERBOSE( VB_UPNP, "-------------------------------------------------------------------------------" ); VERBOSE( VB_UPNP, QString( " Found: %1 Entries - %2 have been Allocated. " ) .arg( nCount ) .arg( DeviceLocation::g_nAllocated )); VERBOSE( VB_UPNP, "===============================================================================" ); Unlock(); } }
/** * If there is only a single UPnP backend, use it. * * This does <i>not</i> prompt for PIN entry. If the backend requires one, * it will fail, and the caller needs to put up a UI to ask for one. */ int MythContextPrivate::UPnPautoconf(const int milliSeconds) { SSDPCacheEntries *backends = NULL; int count; QString loc = "UPnPautoconf() - "; QTime timer; SSDP::Instance()->PerformSearch( gBackendURI ); for (timer.start(); timer.elapsed() < milliSeconds; usleep(25000)) { backends = SSDP::Instance()->Find( gBackendURI ); if (backends) { backends->AddRef(); break; } putchar('.'); } putchar('\n'); if (!backends) { VERBOSE(VB_GENERAL, loc + "No UPnP backends found"); return 0; } count = backends->Count(); switch (count) { case 0: VERBOSE(VB_IMPORTANT, loc + "No UPnP backends found, but SSDP::Find() not NULL!"); break; case 1: VERBOSE(VB_GENERAL, loc + "Found one UPnP backend"); break; default: VERBOSE(VB_GENERAL, (loc + "More than one UPnP backend found (%1)").arg(count)); } if (count != 1) { backends->Release(); return count; } // Get this backend's location: backends->Lock(); DeviceLocation *BE = *(backends->GetEntryMap()->begin()); backends->Unlock(); backends->Release(); // We don't actually know the backend's access PIN, so this will // only work for ones that have PIN access disabled (i.e. 0000) if (UPnPconnect(BE, QString::null)) return 1; return -1; // Try to force chooser & PIN }
virtual bool ProcessRequest(HttpWorkerThread *pThread, HTTPRequest *pRequest) { (void)pThread; if (!pRequest) return false; if (pRequest->m_sBaseUrl != "/") return false; if (pRequest->m_sMethod == "getDeviceDesc") return false; pRequest->m_eResponseType = ResponseTypeHTML; pRequest->m_mapRespHeaders["Cache-Control"] = "no-cache=\"Ext\", max-age = 5000"; SSDPCacheEntries* cache = NULL; QString ipaddress = QString(); if (!UPnp::g_IPAddrList.isEmpty()) ipaddress = UPnp::g_IPAddrList.at(0); QString shortdateformat = gCoreContext->GetSetting("ShortDateFormat", "M/d"); QString timeformat = gCoreContext->GetSetting("TimeFormat", "h:mm AP"); QString hostname = gCoreContext->GetHostName(); QDateTime qdtNow = QDateTime::currentDateTime(); QString masterhost = gCoreContext->GetMasterHostName(); QString masterip = gCoreContext->GetSetting("MasterServerIP"); QString masterport = gCoreContext->GetSettingOnHost("BackendStatusPort", masterhost, "6544"); QTextStream stream ( &pRequest->m_response ); stream.setCodec("UTF-8"); stream << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n" << "<html xmlns=\"http://www.w3.org/1999/xhtml\"" << " xml:lang=\"en\" lang=\"en\">\r\n" << "<head>\r\n" << " <meta http-equiv=\"Content-Type\"" << "content=\"text/html; charset=UTF-8\" />\r\n" << " <title>MythFrontend Status - " << qdtNow.toString(shortdateformat) << " " << qdtNow.toString(timeformat) << " - " << MYTH_BINARY_VERSION << "</title>\r\n" << " <link rel=\"stylesheet\" href=\"/css/site.css\" type=\"text/css\">\r\n" << " <link rel=\"stylesheet\" href=\"/css/Status.css\" type=\"text/css\">\r\n" << "</head>\r\n" << "<body>\r\n\r\n" << " <h1 class=\"status\">MythFrontend Status</h1>\r\n"; // This frontend stream << " <div class=\"content\">\r\n" << " <h2 class=\"status\">This Frontend</h2>\r\n" << "Name : " << hostname << "<br />\r\n" << "Version : " << MYTH_BINARY_VERSION << "\r\n" << " </div>\r\n"; // Other frontends // This will not work with multiple frontends on the same machine (upnp // setup will fail on a second frontend anyway) and the ip address // filtering of the current frontend may not work in all situations cache = SSDP::Find("urn:schemas-mythtv-org:service:MythFrontend:1"); if (cache) { stream << " <div class=\"content\">\r\n" << " <h2 class=\"status\">Other Frontends</h2>\r\n"; EntryMap map; cache->GetEntryMap(map); cache->Release(); cache = NULL; QMapIterator< QString, DeviceLocation * > i(map); while (i.hasNext()) { i.next(); QUrl url(i.value()->m_sLocation); if (url.host() != ipaddress) { stream << "<br />" << url.host() << " (<a href=\"" << url.toString(QUrl::RemovePath) << "\">Status page</a>)\r\n"; } i.value()->Release(); } stream << " </div>\r\n"; } // Master backend stream << " <div class=\"content\">\r\n" << " <h2 class=\"status\">MythTV Backends</h2>\r\n" << "Master: " << masterhost << " (<a href=\"http://" << masterip << ":" << masterport << "\">Status page</a>)\r\n"; // Slave backends cache = SSDP::Find("urn:schemas-mythtv-org:device:SlaveMediaServer:1"); if (cache) { EntryMap map; cache->GetEntryMap(map); cache->Release(); cache = NULL; QMapIterator< QString, DeviceLocation * > i(map); while (i.hasNext()) { i.next(); QUrl url(i.value()->m_sLocation); stream << "<br />" << "Slave: " << url.host() << " (<a href=\"" << url.toString(QUrl::RemovePath) << "\">Status page</a>)\r\n"; i.value()->Release(); } } stream << " </div>\r\n"; stream << " <div class=\"content\">\r\n" << " <h2 class=\"status\">Services</h2>\r\n" << " <a href=\"MythFE/GetRemote\">Remote Control</a>\r\n" << " </div>\r\n"; double load[3]; if (getloadavg(load, 3) != -1) { stream << " <div class=\"content\">\r\n" << " <h2 class=\"status\">Machine Information</h2>\r\n" << " <div class=\"loadstatus\">\r\n" << " This machine's load average:" << "\r\n <ul>\r\n <li>" << "1 Minute: " << QString::number(load[0]) << "</li>\r\n" << " <li>5 Minutes: " << QString::number(load[1]) << "</li>\r\n" << " <li>15 Minutes: " << QString::number(load[2]) << "</li>\r\n </ul>\r\n" << " </div>\r\n" << " </div>\r\n"; } stream << "</body>\r\n</html>\r\n"; stream.flush(); return true; }
void SSDPExtension::GetDeviceList( HTTPRequest *pRequest ) { SSDPCache* pCache = SSDPCache::Instance(); int nCount = 0; NameValues list; VERBOSE( VB_UPNP, "SSDPExtension::GetDeviceList" ); pCache->Lock(); QString sXML = ""; QTextStream os( &sXML, QIODevice::WriteOnly ); for (SSDPCacheEntriesMap::Iterator it = pCache->Begin(); it != pCache->End(); ++it ) { SSDPCacheEntries *pEntries = *it; if (pEntries != NULL) { os << "<Device uri='" << it.key() << "'>" << endl; pEntries->Lock(); EntryMap *pMap = pEntries->GetEntryMap(); for (EntryMap::Iterator itEntry = pMap->begin(); itEntry != pMap->end(); ++itEntry ) { DeviceLocation *pEntry = *itEntry; if (pEntry != NULL) { nCount++; pEntry->AddRef(); os << "<Service usn='" << pEntry->m_sUSN << "' expiresInSecs='" << pEntry->ExpiresInSecs() << "' url='" << pEntry->m_sLocation << "' />" << endl; pEntry->Release(); } } os << "</Device>" << endl; pEntries->Unlock(); } } os << flush; list.push_back( NameValue("DeviceCount" , pCache->Count() )); list.push_back( NameValue("DevicesAllocated" , SSDPCacheEntries::g_nAllocated)); list.push_back( NameValue("CacheEntriesFound" , nCount )); list.push_back( NameValue("CacheEntriesAllocated", DeviceLocation::g_nAllocated )); list.push_back( NameValue("DeviceList" , sXML )); pCache->Unlock(); pRequest->FormatActionResponse( list ); pRequest->m_eResponseType = ResponseTypeXML; pRequest->m_nResponseStatus = 200; }
/** * If there is only a single UPnP backend, use it. * * This does <i>not</i> prompt for PIN entry. If the backend requires one, * it will fail, and the caller needs to put up a UI to ask for one. */ int MythContextPrivate::UPnPautoconf(const int milliSeconds) { LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs") .arg(milliSeconds / 1000)); SSDP::Instance()->PerformSearch(gBackendURI, milliSeconds / 1000); // Search for a total of 'milliSeconds' ms, sending new search packet // about every 250 ms until less than one second remains. MythTimer totalTime; totalTime.start(); MythTimer searchTime; searchTime.start(); while (totalTime.elapsed() < milliSeconds) { usleep(25000); int ttl = milliSeconds - totalTime.elapsed(); if ((searchTime.elapsed() > 249) && (ttl > 1000)) { LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs") .arg(ttl / 1000)); SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000); searchTime.start(); } } SSDPCacheEntries *backends = SSDP::Instance()->Find(gBackendURI); if (!backends) { LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found"); return 0; } int count = backends->Count(); if (count) { LOG(VB_GENERAL, LOG_INFO, QString("Found %1 UPnP backends").arg(count)); } else { LOG(VB_GENERAL, LOG_ERR, "No UPnP backends found, but SSDP::Find() not NULL"); } if (count != 1) { backends->DecrRef(); return count; } // Get this backend's location: DeviceLocation *BE = backends->GetFirst(); backends->DecrRef(); backends = NULL; // We don't actually know the backend's access PIN, so this will // only work for ones that have PIN access disabled (i.e. 0000) int ret = (UPnPconnect(BE, QString::null)) ? 1 : -1; BE->DecrRef(); return ret; }