static void terminate_pending_irp_threads(SERIAL_DEVICE* serial) { ULONG_PTR* ids; int i, nbIds; nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids); WLog_Print(serial->log, WLOG_DEBUG, "Terminating %d IRP thread(s)", nbIds); for (i = 0; i < nbIds; i++) { HANDLE irpThread; ULONG_PTR id = ids[i]; irpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)id); TerminateThread(irpThread, 0); if (WaitForSingleObject(irpThread, INFINITE) == WAIT_FAILED) { WLog_ERR(TAG, "WaitForSingleObject failed!"); continue; } CloseHandle(irpThread); WLog_Print(serial->log, WLOG_DEBUG, "IRP thread terminated, CompletionId %p", (void*) id); } ListDictionary_Clear(serial->IrpThreads); }
LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { rdpdrPlugin *rdpdr; PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam; rdpdr = (rdpdrPlugin *)GetWindowLongPtr(hWnd, GWLP_USERDATA); switch(Msg) { case WM_DEVICECHANGE: switch (wParam) { case DBT_DEVICEARRIVAL: if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME) { PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; DWORD unitmask = lpdbv->dbcv_unitmask; int i; char drive_path[4] = { 'c', ':', '/', '\0'}; for (i = 0; i < 26; i++) { if (unitmask & 0x01) { RDPDR_DRIVE* drive; drive_path[0] = 'A' + i; drive = (RDPDR_DRIVE*) malloc(sizeof(RDPDR_DRIVE)); ZeroMemory(drive, sizeof(RDPDR_DRIVE)); drive->Type = RDPDR_DTYP_FILESYSTEM; drive->Path = _strdup(drive_path); drive_path[1] = '\0'; drive->Name = _strdup(drive_path); devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive); rdpdr_send_device_list_announce_request(rdpdr, TRUE); } unitmask = unitmask >> 1; } } break; case DBT_DEVICEREMOVECOMPLETE: if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME) { PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; DWORD unitmask = lpdbv->dbcv_unitmask; int i, j, count; char drive_name_upper, drive_name_lower; ULONG_PTR *keys; DEVICE_DRIVE_EXT *device_ext; UINT32 ids[1]; for (i = 0; i < 26; i++) { if (unitmask & 0x01) { drive_name_upper = 'A' + i; drive_name_lower = 'a' + i; count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys); for (j = 0; j < count; j++) { device_ext = (DEVICE_DRIVE_EXT *)ListDictionary_GetItemValue(rdpdr->devman->devices, (void *)keys[j]); if (device_ext->path[0] == drive_name_upper || device_ext->path[0] == drive_name_lower) { devman_unregister_device(rdpdr->devman, (void *)keys[j]); ids[0] = keys[j]; rdpdr_send_device_list_remove_request(rdpdr, 1, ids); break; } } } unitmask = unitmask >> 1; } } break; default: break; }
static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp) { IRP_THREAD_DATA* data = NULL; HANDLE irpThread; HANDLE previousIrpThread; uintptr_t key; /* for a test/debug purpose, uncomment the code below to get a * single thread for all IRPs. NB: two IRPs could not be * processed at the same time, typically two concurent * Read/Write operations could block each other. */ /* serial_process_irp(serial, irp); */ /* irp->Complete(irp); */ /* return; */ /* NOTE: for good or bad, this implementation relies on the * server to avoid a flooding of requests. see also _purge(). */ EnterCriticalSection(&serial->TerminatingIrpThreadsLock); while (serial->IrpThreadToBeTerminatedCount > 0) { /* Cleaning up termitating and pending irp * threads. See also: irp_thread_func() */ HANDLE irpThread; ULONG_PTR* ids; int i, nbIds; nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids); for (i = 0; i < nbIds; i++) { /* Checking if ids[i] is terminating or pending */ DWORD waitResult; ULONG_PTR id = ids[i]; irpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)id); /* FIXME: not quite sure a zero timeout is a good thing to check whether a thread is stil alived or not */ waitResult = WaitForSingleObject(irpThread, 0); if (waitResult == WAIT_OBJECT_0) { /* terminating thread */ /* WLog_Print(serial->log, WLOG_DEBUG, "IRP thread with CompletionId=%"PRIuz" naturally died", id); */ CloseHandle(irpThread); ListDictionary_Remove(serial->IrpThreads, (void*)id); serial->IrpThreadToBeTerminatedCount--; } else if (waitResult != WAIT_TIMEOUT) { /* unexpected thread state */ WLog_Print(serial->log, WLOG_WARN, "WaitForSingleObject, got an unexpected result=0x%"PRIX32"\n", waitResult); assert(FALSE); } /* pending thread (but not yet terminating thread) if waitResult == WAIT_TIMEOUT */ } if (serial->IrpThreadToBeTerminatedCount > 0) { WLog_Print(serial->log, WLOG_DEBUG, "%"PRIu32" IRP thread(s) not yet terminated", serial->IrpThreadToBeTerminatedCount); Sleep(1); /* 1 ms */ } } LeaveCriticalSection(&serial->TerminatingIrpThreadsLock); /* NB: At this point and thanks to the synchronization we're * sure that the incoming IRP uses well a recycled * CompletionId or the server sent again an IRP already posted * which didn't get yet a response (this later server behavior * at least observed with IOCTL_SERIAL_WAIT_ON_MASK and * mstsc.exe). * * FIXME: behavior documented somewhere? behavior not yet * observed with FreeRDP). */ key = irp->CompletionId; previousIrpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)key); if (previousIrpThread) { /* Thread still alived <=> Request still pending */ WLog_Print(serial->log, WLOG_DEBUG, "IRP recall: IRP with the CompletionId=%"PRIu32" not yet completed!", irp->CompletionId); assert(FALSE); /* unimplemented */ /* TODO: asserts that previousIrpThread handles well * the same request by checking more details. Need an * access to the IRP object used by previousIrpThread */ /* TODO: taking over the pending IRP or sending a kind * of wake up signal to accelerate the pending * request * * To be considered: * if (IoControlCode == IOCTL_SERIAL_WAIT_ON_MASK) { * pComm->PendingEvents |= SERIAL_EV_FREERDP_*; * } */ irp->Discard(irp); return; } if (ListDictionary_Count(serial->IrpThreads) >= MAX_IRP_THREADS) { WLog_Print(serial->log, WLOG_WARN, "Number of IRP threads threshold reached: %d, keep on anyway", ListDictionary_Count(serial->IrpThreads)); assert(FALSE); /* unimplemented */ /* TODO: MAX_IRP_THREADS has been thought to avoid a * flooding of pending requests. Use * WaitForMultipleObjects() when available in winpr * for threads. */ } /* error_handle to be used ... */ data = (IRP_THREAD_DATA*)calloc(1, sizeof(IRP_THREAD_DATA)); if (data == NULL) { WLog_Print(serial->log, WLOG_WARN, "Could not allocate a new IRP_THREAD_DATA."); goto error_handle; } data->serial = serial; data->irp = irp; /* data freed by irp_thread_func */ irpThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)irp_thread_func, (void*)data, 0, NULL); if (irpThread == INVALID_HANDLE_VALUE) { WLog_Print(serial->log, WLOG_WARN, "Could not allocate a new IRP thread."); goto error_handle; } key = irp->CompletionId; if (!ListDictionary_Add(serial->IrpThreads, (void*)key, irpThread)) { WLog_ERR(TAG, "ListDictionary_Add failed!"); goto error_handle; } return; error_handle: irp->IoStatus = STATUS_NO_MEMORY; irp->Complete(irp); free(data); }
static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard) { int index; int keyCount; ULONG_PTR* pKeys; SCARDCONTEXT hContext; SMARTCARD_CONTEXT* pContext; /** * On protocol termination, the following actions are performed: * For each context in rgSCardContextList, SCardCancel is called causing all SCardGetStatusChange calls to be processed. * After that, SCardReleaseContext is called on each context and the context MUST be removed from rgSCardContextList. */ /** * Call SCardCancel on existing contexts, unblocking all outstanding SCardGetStatusChange calls. */ if (ListDictionary_Count(smartcard->rgSCardContextList) > 0) { pKeys = NULL; keyCount = ListDictionary_GetKeys(smartcard->rgSCardContextList, &pKeys); for (index = 0; index < keyCount; index++) { pContext = (SMARTCARD_CONTEXT*) ListDictionary_GetItemValue(smartcard->rgSCardContextList, (void*) pKeys[index]); if (!pContext) continue; hContext = pContext->hContext; if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS) { SCardCancel(hContext); } } free(pKeys); } /** * Call SCardReleaseContext on remaining contexts and remove them from rgSCardContextList. */ if (ListDictionary_Count(smartcard->rgSCardContextList) > 0) { pKeys = NULL; keyCount = ListDictionary_GetKeys(smartcard->rgSCardContextList, &pKeys); for (index = 0; index < keyCount; index++) { pContext = (SMARTCARD_CONTEXT*) ListDictionary_Remove(smartcard->rgSCardContextList, (void*) pKeys[index]); if (!pContext) continue; hContext = pContext->hContext; if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS) { SCardReleaseContext(hContext); if (MessageQueue_PostQuit(pContext->IrpQueue, 0) && (WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED)) WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError()); CloseHandle(pContext->thread); MessageQueue_Free(pContext->IrpQueue); free(pContext); } } free(pKeys); } }
VOID DumpThreadHandles(void) { char** msg; size_t used, i; void* stack = winpr_backtrace(20); WLog_DBG(TAG, "---------------- Called from ----------------------------"); msg = winpr_backtrace_symbols(stack, &used); for (i = 0; i < used; i++) { WLog_DBG(TAG, "[%d]: %s", i, msg[i]); } free(msg); winpr_backtrace_free(stack); WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------"); if (!thread_list) { WLog_DBG(TAG, "All threads properly shut down and disposed of."); } else { ULONG_PTR* keys = NULL; ListDictionary_Lock(thread_list); int x, count = ListDictionary_GetKeys(thread_list, &keys); WLog_DBG(TAG, "Dumping %d elements", count); for (x = 0; x < count; x++) { WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*) keys[x]); WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x); msg = winpr_backtrace_symbols(thread->create_stack, &used); for (i = 0; i < used; i++) { WLog_DBG(TAG, "[%d]: %s", i, msg[i]); } free(msg); if (thread->started) { WLog_DBG(TAG, "Thread [%d] still running!", x); } else { WLog_DBG(TAG, "Thread [%d] exited at:", x); msg = winpr_backtrace_symbols(thread->exit_stack, &used); for (i = 0; i < used; i++) WLog_DBG(TAG, "[%d]: %s", i, msg[i]); free(msg); } } free(keys); ListDictionary_Unlock(thread_list); } WLog_DBG(TAG, "---------------- End Dumping thread handles -------------"); }
static void smartcard_init(DEVICE* device) { int index; int keyCount; ULONG_PTR* pKeys; SCARDCONTEXT hContext; SMARTCARD_CONTEXT* pContext; SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device; /** * On protocol termination, the following actions are performed: * For each context in rgSCardContextList, SCardCancel is called causing all outstanding messages to be processed. * After there are no more outstanding messages, SCardReleaseContext is called on each context and the context MUST * be removed from rgSCardContextList. */ /** * Call SCardCancel on existing contexts, unblocking all outstanding IRPs. */ if (ListDictionary_Count(smartcard->rgSCardContextList) > 0) { pKeys = NULL; keyCount = ListDictionary_GetKeys(smartcard->rgSCardContextList, &pKeys); for (index = 0; index < keyCount; index++) { pContext = (SMARTCARD_CONTEXT*) ListDictionary_GetItemValue(smartcard->rgSCardContextList, (void*) pKeys[index]); if (!pContext) continue; hContext = pContext->hContext; if (SCardIsValidContext(hContext)) { SCardCancel(hContext); } } free(pKeys); } /** * Call SCardReleaseContext on remaining contexts and remove them from rgSCardContextList. */ if (ListDictionary_Count(smartcard->rgSCardContextList) > 0) { pKeys = NULL; keyCount = ListDictionary_GetKeys(smartcard->rgSCardContextList, &pKeys); for (index = 0; index < keyCount; index++) { pContext = (SMARTCARD_CONTEXT*) ListDictionary_Remove(smartcard->rgSCardContextList, (void*) pKeys[index]); if (!pContext) continue; hContext = pContext->hContext; if (SCardIsValidContext(hContext)) { SCardReleaseContext(hContext); } } free(pKeys); } }