// チャンネル設定 const BOOL CBonTuner::SetChannel(const DWORD dwSpace, const DWORD dwChannel) { // 有効なチャンネルか if(dwSpace > 0 || (dwChannel >= PIPE_NODE_NUM)) return FALSE; // 一旦クローズ CloseTuner(); // バッファ確保 if(!(m_pIoReqBuff = AllocIoReqBuff(ASYNCBUFFSIZE))){ return FALSE; } // バッファ位置同期 m_pIoPushReq = m_pIoReqBuff; m_pIoPopReq = m_pIoReqBuff; m_pIoGetReq = m_pIoReqBuff; m_dwBusyReqNum = 0; m_dwReadyReqNum = 0; try{ // ドライバオープン TCHAR szName[MAX_PATH]; ::wsprintf(szName, PIPE_NAME, dwChannel); m_hPipe = ::CreateNamedPipe(szName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, sizeof(m_pIoReqBuff->RxdBuff), sizeof(m_pIoReqBuff->RxdBuff), 3000, NULL); if (m_hPipe == INVALID_HANDLE_VALUE) { ::OutputDebugString(TEXT("BonDriver_Pipe: CBonTuner::OpenTuner() CreateNamedPipe error\n")); throw 1UL; } // イベント作成 if(!(m_hOnStreamEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL)))throw 2UL; // スレッド起動 DWORD dwPushIoThreadID = 0UL, dwPopIoThreadID = 0UL; m_hPushIoThread = ::CreateThread(NULL, 0UL, CBonTuner::PushIoThread, this, CREATE_SUSPENDED, &dwPopIoThreadID); m_hPopIoThread = ::CreateThread(NULL, 0UL, CBonTuner::PopIoThread, this, CREATE_SUSPENDED, &dwPushIoThreadID); if(!m_hPushIoThread || !m_hPopIoThread){ if(m_hPushIoThread){ ::TerminateThread(m_hPushIoThread, 0UL); ::CloseHandle(m_hPushIoThread); m_hPushIoThread = NULL; } if(m_hPopIoThread){ ::TerminateThread(m_hPopIoThread, 0UL); ::CloseHandle(m_hPopIoThread); m_hPopIoThread = NULL; } throw 3UL; } // スレッド開始 m_bLoopIoThread = TRUE; if(::ResumeThread(m_hPushIoThread) == 0xFFFFFFFFUL || ::ResumeThread(m_hPopIoThread) == 0xFFFFFFFFUL)throw 4UL; // ミューテックス作成 if(!(m_hMutex = ::CreateMutex(NULL, TRUE, MUTEX_NAME)))throw 5UL; } catch(const DWORD dwErrorStep){ // エラー発生 TCHAR szDebugOut[1024]; ::wsprintf(szDebugOut, TEXT("BonDriver_Pipe: CBonTuner::OpenTuner() dwErrorStep = %lu\n"), dwErrorStep); ::OutputDebugString(szDebugOut); CloseTuner(); return FALSE; } // チャンネル情報更新 m_dwCurSpace = dwSpace; m_dwCurChannel = dwChannel; // TSデータパージ PurgeTsStream(); return TRUE; }
DWORD cProxyServer::Process() { HANDLE hThread[2]; hThread[0] = ::CreateThread(NULL, 0, cProxyServer::Sender, this, 0, NULL); if (hThread[0] == NULL) return 1; ::SetThreadPriority(hThread[0], g_ThreadPrioritySender); hThread[1] = ::CreateThread(NULL, 0, cProxyServer::Receiver, this, 0, NULL); if (hThread[1] == NULL) { m_Error.Set(); ::WaitForSingleObject(hThread[0], INFINITE); ::CloseHandle(hThread[0]); return 2; } HANDLE h[3] = { m_Error, m_fifoRecv.GetEventHandle(), g_ShutdownEvent }; for (;;) { DWORD dwRet = ::WaitForMultipleObjects(3, h, FALSE, INFINITE); switch (dwRet) { case WAIT_OBJECT_0: goto end; case WAIT_OBJECT_0 + 1: { #ifdef STRICT_LOCK LOCK(g_Lock); #endif cPacketHolder *pPh; m_fifoRecv.Pop(&pPh); switch (pPh->GetCommand()) { case eSelectBonDriver: { if (pPh->GetBodyLength() <= sizeof(char)) makePacket(eSelectBonDriver, FALSE); else { LPCSTR p = (LPCSTR)(pPh->m_pPacket->payload); if (::strlen(p) > (sizeof(m_strBonDriver) - 1)) makePacket(eSelectBonDriver, FALSE); else { BOOL bFind = FALSE; #ifndef STRICT_LOCK LOCK(g_Lock); #endif for (std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); it != g_InstanceList.end(); ++it) { if (::strcmp(p, (*it)->m_strBonDriver) == 0) { bFind = TRUE; m_hModule = (*it)->m_hModule; ::strcpy(m_strBonDriver, (*it)->m_strBonDriver); m_pIBon = (*it)->m_pIBon; // (*it)->m_pIBonがNULLの可能性はゼロではない m_pIBon2 = (*it)->m_pIBon2; m_pIBon3 = (*it)->m_pIBon3; break; } } BOOL bSuccess; if (!bFind) { bSuccess = SelectBonDriver(p); if (bSuccess) { g_InstanceList.push_back(this); ::strcpy(m_strBonDriver, p); } } else { g_InstanceList.push_back(this); bSuccess = TRUE; } makePacket(eSelectBonDriver, bSuccess); #ifdef HAVE_UI if (bSuccess) ::InvalidateRect(g_hWnd, NULL, TRUE); #endif } } break; } case eCreateBonDriver: { if (m_pIBon == NULL) { BOOL bFind = FALSE; BOOL bLoop = FALSE; { #ifndef STRICT_LOCK LOCK(g_Lock); #endif for (std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); it != g_InstanceList.end(); ++it) { if (*it == this) continue; if (m_hModule == (*it)->m_hModule) { if ((*it)->m_pIBon != NULL) { bFind = TRUE; // ここに来るのはかなりのレアケースのハズ m_pIBon = (*it)->m_pIBon; m_pIBon2 = (*it)->m_pIBon2; m_pIBon3 = (*it)->m_pIBon3; break; } else { // ここに来るのは上より更にレアケース、あるいはクライアントが // BonDriver_Proxy.dllを要求し、サーバ側のBonDriver_Proxy.dllも // 同じサーバに対して自分自身を要求する無限ループ状態だけのハズ // なお、STRICT_LOCKが定義してある場合は、そもそもデッドロックを // 起こすので、後者の状況は発生しない // 気休めの雑なチェック if (!::_memicmp(m_strBonDriver, "BonDriver_Proxy", 15)) { bLoop = TRUE; break; } // 無限ループ状態以外の場合は一応リストの最後まで検索してみて、 // それでも見つからなかったらCreateBonDriver()をやらせてみる } } } } if (!bFind && !bLoop) { if ((CreateBonDriver() != NULL) && (m_pIBon2 != NULL)) makePacket(eCreateBonDriver, TRUE); else { makePacket(eCreateBonDriver, FALSE); m_Error.Set(); } } else { if (!bLoop) makePacket(eCreateBonDriver, TRUE); else { makePacket(eCreateBonDriver, FALSE); m_Error.Set(); } } } else makePacket(eCreateBonDriver, TRUE); break; } case eOpenTuner: { BOOL bFind = FALSE; { #ifndef STRICT_LOCK LOCK(g_Lock); #endif for (std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); it != g_InstanceList.end(); ++it) { if (*it == this) continue; if ((m_pIBon != NULL) && (m_pIBon == (*it)->m_pIBon)) { if ((*it)->m_bTunerOpen) { bFind = TRUE; m_bTunerOpen = TRUE; break; } } } } if (!bFind) m_bTunerOpen = OpenTuner(); makePacket(eOpenTuner, m_bTunerOpen); break; } case eCloseTuner: { BOOL bFind = FALSE; { #ifndef STRICT_LOCK LOCK(g_Lock); #endif for (std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); it != g_InstanceList.end(); ++it) { if (*it == this) continue; if ((m_pIBon != NULL) && (m_pIBon == (*it)->m_pIBon)) { if ((*it)->m_bTunerOpen) { bFind = TRUE; break; } } } } if (!bFind) { if (m_hTsRead) { m_pTsReaderArg->StopTsRead = TRUE; ::WaitForSingleObject(m_hTsRead, INFINITE); ::CloseHandle(m_hTsRead); delete m_pTsReaderArg; } CloseTuner(); } else { if (m_hTsRead) { #ifndef STRICT_LOCK LOCK(g_Lock); #endif m_pTsReaderArg->TsLock.Enter(); std::list<cProxyServer *>::iterator it = m_pTsReaderArg->TsReceiversList.begin(); while (it != m_pTsReaderArg->TsReceiversList.end()) { if (*it == this) { m_pTsReaderArg->TsReceiversList.erase(it); break; } ++it; } m_pTsReaderArg->TsLock.Leave(); // このインスタンスはチャンネル排他権を持っているか? if (m_bChannelLock == 0xff) { // 持っていた場合は、排他権取得待ちのインスタンスは存在しているか? if (m_pTsReaderArg->WaitExclusivePrivList.size() > 0) { // 存在する場合は、リスト先頭のインスタンスに排他権を引き継ぎ、リストから削除 cProxyServer *p = m_pTsReaderArg->WaitExclusivePrivList.front(); m_pTsReaderArg->WaitExclusivePrivList.pop_front(); p->m_bChannelLock = 0xff; } } else { // 持っていない場合は、排他権取得待ちリストに自身が含まれているかもしれないので削除 m_pTsReaderArg->WaitExclusivePrivList.remove(this); } // 可能性は低いがゼロではない… if (m_pTsReaderArg->TsReceiversList.empty()) { m_pTsReaderArg->StopTsRead = TRUE; ::WaitForSingleObject(m_hTsRead, INFINITE); ::CloseHandle(m_hTsRead); delete m_pTsReaderArg; } } } m_bChannelLock = 0; m_hTsRead = NULL; m_pTsReaderArg = NULL; m_bTunerOpen = FALSE; break; } case ePurgeTsStream: { if (m_hTsRead) { m_pTsReaderArg->TsLock.Enter(); if (m_pTsReaderArg->TsReceiversList.size() <= 1) { PurgeTsStream(); m_pTsReaderArg->pos = 0; } m_pTsReaderArg->TsLock.Leave(); makePacket(ePurgeTsStream, TRUE); } else makePacket(ePurgeTsStream, FALSE); break; } case eRelease: m_Error.Set(); break; case eEnumTuningSpace: { if (pPh->GetBodyLength() != sizeof(DWORD)) makePacket(eEnumTuningSpace, _T("")); else { LPCTSTR p = EnumTuningSpace(::ntohl(*(DWORD *)(pPh->m_pPacket->payload))); if (p) makePacket(eEnumTuningSpace, p); else makePacket(eEnumTuningSpace, _T("")); } break; } case eEnumChannelName: { if (pPh->GetBodyLength() != (sizeof(DWORD) * 2)) makePacket(eEnumChannelName, _T("")); else { LPCTSTR p = EnumChannelName(::ntohl(*(DWORD *)(pPh->m_pPacket->payload)), ::ntohl(*(DWORD *)&(pPh->m_pPacket->payload[sizeof(DWORD)]))); if (p) makePacket(eEnumChannelName, p); else makePacket(eEnumChannelName, _T("")); } break; } case eSetChannel2: { if (pPh->GetBodyLength() != ((sizeof(DWORD) * 2) + sizeof(BYTE))) makePacket(eSetChannel2, (DWORD)0xff); else { m_bChannelLock = pPh->m_pPacket->payload[sizeof(DWORD) * 2]; BOOL bLocked = FALSE; cProxyServer *pHavePriv = NULL; { #ifndef STRICT_LOCK LOCK(g_Lock); #endif for (std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); it != g_InstanceList.end(); ++it) { if (*it == this) continue; if ((m_pIBon != NULL) && (m_pIBon == (*it)->m_pIBon)) { if ((*it)->m_bChannelLock > m_bChannelLock) bLocked = TRUE; else if ((*it)->m_bChannelLock == 0xff) { // 対象チューナに対して優先度255のインスタンスが既にいる状態で、このインスタンスが // 要求している優先度も255の場合、このインスタンスの優先度を暫定的に254にする // (そうしないと、優先度255のインスタンスもチャンネル変更できなくなる為) m_bChannelLock = 0xfe; bLocked = TRUE; pHavePriv = *it; } if ((m_hTsRead == NULL) && ((*it)->m_hTsRead != NULL)) { m_hTsRead = (*it)->m_hTsRead; m_pTsReaderArg = (*it)->m_pTsReaderArg; m_pTsReaderArg->TsLock.Enter(); m_pTsReaderArg->TsReceiversList.push_back(this); m_pTsReaderArg->TsLock.Leave(); } } } // このインスタンスの優先度が下げられた場合 if (pHavePriv != NULL) { if (m_hTsRead) { // 排他権取得待ちリストにまだ自身が含まれていなければ追加 BOOL bFind = FALSE; std::list<cProxyServer *>::iterator it = m_pTsReaderArg->WaitExclusivePrivList.begin(); while (it != m_pTsReaderArg->WaitExclusivePrivList.end()) { if (*it == this) { bFind = TRUE; break; } ++it; } if (!bFind) m_pTsReaderArg->WaitExclusivePrivList.push_back(this); } else { // このインスタンスの優先度が下げられたが、排他権を持っているインスタンスへの配信が // 開始されていない場合は、そのインスタンスから排他権を奪う // こうする事が挙動として望ましいのかどうかは微妙だが、そもそもここに来るのは、 // 当該インスタンスでのSetChannel()の失敗後、何もせずに接続だけ続けている状態であり、 // 可能性としてはゼロではないものの、かなりのレアケースに限られるはず pHavePriv->m_bChannelLock = 0; m_bChannelLock = 0xff; } } } if (bLocked) makePacket(eSetChannel2, (DWORD)0x01); else { if (m_hTsRead) m_pTsReaderArg->TsLock.Enter(); BOOL b = SetChannel(::ntohl(*(DWORD *)(pPh->m_pPacket->payload)), ::ntohl(*(DWORD *)&(pPh->m_pPacket->payload[sizeof(DWORD)]))); if (m_hTsRead) { // 一旦ロックを外すとチャンネル変更前のデータが送信されない事を保証できなくなる為、 // チャンネル変更前のデータの破棄とCNRの更新指示はここで行う if (b) { m_pTsReaderArg->pos = 0; m_pTsReaderArg->ChannelChanged = TRUE; } m_pTsReaderArg->TsLock.Leave(); } if (b) { makePacket(eSetChannel2, (DWORD)0x00); if (m_hTsRead == NULL) { #ifndef STRICT_LOCK // すぐ上で検索してるのになぜ再度検索するのかと言うと、同じBonDriverを要求している複数の // クライアントから、ほぼ同時のタイミングで最初のeSetChannel2をリクエストされた場合の為 // eSetChannel2全体をまとめてロックすれば必要無くなるが、BonDriver_Proxyがロードされ、 // それが自分自身に接続してきた場合デッドロックする事になる // なお、同様の理由でeCreateBonDriver, eOpenTuner, eCloseTunerのロックは実は不完全 // しかし、自分自身への再帰接続を行わないならば完全なロックも可能 // 実際の所、テスト用途以外で自分自身への再接続が必要になる状況と言うのはまず無いと // 思うので、STRICT_LOCKが定義してある場合は完全なロックを行う事にする // ただしそのかわりに、BonDriver_Proxyをロードし、そこからのプロキシチェーンのどこかで // 自分自身に再帰接続した場合はデッドロックとなるので注意 BOOL bFind = FALSE; LOCK(g_Lock); for (std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); it != g_InstanceList.end(); ++it) { if (*it == this) continue; if (m_pIBon == (*it)->m_pIBon) { if ((*it)->m_hTsRead != NULL) { bFind = TRUE; m_hTsRead = (*it)->m_hTsRead; m_pTsReaderArg = (*it)->m_pTsReaderArg; m_pTsReaderArg->TsLock.Enter(); m_pTsReaderArg->TsReceiversList.push_back(this); m_pTsReaderArg->TsLock.Leave(); break; } } } if (!bFind) { #endif m_pTsReaderArg = new stTsReaderArg(); m_pTsReaderArg->TsReceiversList.push_back(this); m_pTsReaderArg->pIBon = m_pIBon; m_hTsRead = ::CreateThread(NULL, 0, cProxyServer::TsReader, m_pTsReaderArg, 0, NULL); if (m_hTsRead == NULL) { delete m_pTsReaderArg; m_pTsReaderArg = NULL; m_Error.Set(); } else ::SetThreadPriority(m_hTsRead, g_ThreadPriorityTsReader); #ifndef STRICT_LOCK } #endif } } else makePacket(eSetChannel2, (DWORD)0xff); } } break; } case eGetTotalDeviceNum: makePacket(eGetTotalDeviceNum, GetTotalDeviceNum()); break; case eGetActiveDeviceNum: makePacket(eGetActiveDeviceNum, GetActiveDeviceNum()); break; case eSetLnbPower: { if (pPh->GetBodyLength() != sizeof(BYTE)) makePacket(eSetLnbPower, FALSE); else makePacket(eSetLnbPower, SetLnbPower((BOOL)(pPh->m_pPacket->payload[0]))); break; } case eGetClientInfo: { union { SOCKADDR_STORAGE ss; SOCKADDR_IN si4; SOCKADDR_IN6 si6; }; char addr[INET6_ADDRSTRLEN], buf[512], info[1024], *p, *exinfo; int port, len, num = 0; size_t left, size; p = info; p[0] = '\0'; left = size = sizeof(info); exinfo = NULL; std::list<cProxyServer *>::iterator it = g_InstanceList.begin(); while (it != g_InstanceList.end()) { len = sizeof(ss); if (::getpeername((*it)->m_s, (SOCKADDR *)&ss, &len) == 0) { if (ss.ss_family == AF_INET) { // IPv4 #ifdef _WIN64 ::inet_ntop(AF_INET, &(si4.sin_addr), addr, sizeof(addr)); #else ::lstrcpyA(addr, ::inet_ntoa(si4.sin_addr)); #endif port = ::ntohs(si4.sin_port); } else { // IPv6 #ifdef _WIN64 ::inet_ntop(AF_INET6, &(si6.sin6_addr), addr, sizeof(addr)); #else char *cp = addr; for (int i = 0; i < 16; i += 2) cp += ::wsprintfA(cp, "%02x%02x%c", si6.sin6_addr.s6_addr[i], si6.sin6_addr.s6_addr[i + 1], (i != 14) ? ':' : '\0'); #endif port = ::ntohs(si6.sin6_port); } } else { ::lstrcpyA(addr, "unknown host..."); port = 0; } len = ::wsprintfA(buf, "%02d: [%s]:[%d] / [%s]\n", num, addr, port, (*it)->m_strBonDriver); if ((size_t)len >= left) { left += size; size *= 2; if (exinfo != NULL) { char *bp = exinfo; exinfo = new char[size]; ::lstrcpyA(exinfo, bp); delete[] bp; } else { exinfo = new char[size]; ::lstrcpyA(exinfo, info); } p = exinfo + ::lstrlenA(exinfo); } ::lstrcpyA(p, buf); p += len; left -= len; num++; ++it; } if (exinfo != NULL) { size = (p - exinfo) + 1; p = exinfo; } else { size = (p - info) + 1; p = info; } cPacketHolder *ph = new cPacketHolder(eGetClientInfo, size); ::memcpy(ph->m_pPacket->payload, p, size); m_fifoSend.Push(ph); if (exinfo != NULL) delete[] exinfo; break; } default: break; } delete pPh; break; } case WAIT_OBJECT_0 + 2: // 終了要求 // fall-through default: // 何かのエラー m_Error.Set(); goto end; } } end: ::WaitForMultipleObjects(2, hThread, TRUE, INFINITE); ::CloseHandle(hThread[0]); ::CloseHandle(hThread[1]); return 0; }