// Copyright (c) 1998-1999 Microsoft Corporation /****************************************************************************** * * CHGPORT.C * * Change serial port mapping. * * * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chgport.h" // max length of the locale string #define MAX_LOCALE_STRING 64 /* * Global Data */ WCHAR user_string[MAX_IDS_LEN+1]; // parsed user input USHORT help_flag = FALSE; // User wants help USHORT fDelete = FALSE; // delete mapped port USHORT fquery = FALSE; // query the mapped ports PCOMNAME pValidNames = NULL; // list of valid com names in registry TOKMAP ptm[] = { {L" ", TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, user_string}, {L"/d", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &fDelete}, {L"/?", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &help_flag}, {L"/QUERY", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &fquery}, {L"/Q", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &fquery}, {0, 0, 0, 0, 0} }; /* * Constants */ #define DOSDEVICE_STRING L"\\DosDevices" /* * Local function prototypes. */ void Usage(BOOLEAN bError); BOOL DeleteMappedPort(PWCHAR user_string); BOOL GetPorts(PWCHAR user_string, PWCHAR pwcSrcPort, PWCHAR pwcDestPort, ULONG ulbufsize); BOOL MapPorts(PWCHAR pwcSrcPort, PWCHAR pwcDestPort); void ListSerialPorts(); BOOL IsSerialDevice(PWCHAR pwcName); ULONG GetNTObjectName(PWCHAR pwcDOSdev, PWCHAR pwcNTObjName, ULONG ulbufsize); ULONG AddComName(PCOMNAME *pComList, PWCHAR pwcNTName, PWCHAR pwcDOSName); void DelComName(PCOMNAME pEntry); PCOMNAME FindComName(PCOMNAME pComList, PWCHAR pwcName); BOOL IsVDMdeviceName(PWCHAR pwcName); /******************************************************************************* * * main * ******************************************************************************/ int __cdecl main(INT argc, CHAR **argv) { WCHAR **argvW; WCHAR wcSrcPort[MAX_PATH], wcDestPort[MAX_PATH]; ULONG ulSrcPort, ulDestPort, rc; INT i; WCHAR wszString[MAX_LOCALE_STRING + 1]; setlocale(LC_ALL, ".OCP"); // We don't want LC_CTYPE set the same as the others or else we will see // garbage output in the localized version, so we need to explicitly // set it to correct console output code page _snwprintf(wszString, sizeof(wszString)/sizeof(WCHAR), L".%d", GetConsoleOutputCP()); wszString[sizeof(wszString)/sizeof(WCHAR) - 1] = L'\0'; _wsetlocale(LC_CTYPE, wszString); SetThreadUILanguage(0); /* * Massage the command line. */ argvW = MassageCommandLine((DWORD)argc); if (argvW == NULL) { ErrorPrintf(IDS_ERROR_MALLOC); return(FAILURE); } /* * parse the cmd line without parsing the program name (argc-1, argv+1) */ rc = ParseCommandLine(argc-1, argvW+1, ptm, 0); /* * Check for error from ParseCommandLine */ if ( help_flag || (rc && !(rc & PARSE_FLAG_NO_PARMS)) ) { if ( !help_flag ) { Usage(TRUE); return(FAILURE); } else { Usage(FALSE); return(SUCCESS); } } //If we are not Running under Terminal Server, Return Error if(!AreWeRunningTerminalServices()) { ErrorPrintf(IDS_ERROR_NOT_TS); return (FAILURE); } if (fDelete) { DeleteMappedPort(user_string); } else if (*user_string) { GetPorts(user_string, wcSrcPort, wcDestPort, MAX_PATH); MapPorts(wcSrcPort, wcDestPort); } else { // query the mapped ports ListSerialPorts(); } // Free up the list of valid port names if (pValidNames) { PCOMNAME pEntry, pPrev; pEntry = pValidNames; while (pEntry) { pPrev = pEntry; pEntry = pEntry->com_pnext; DelComName(pPrev); } } return(SUCCESS); } /******************************************************************************* * * Usage * * Output the usage message for this utility. * * ENTRY: * bError (input) * TRUE if the 'invalid parameter(s)' message should preceed the usage * message and the output go to stderr; FALSE for no such error * string and output goes to stdout. * * EXIT: * * ******************************************************************************/ void Usage( BOOLEAN bError ) { if ( bError ) { ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS); } ErrorPrintf(IDS_HELP_USAGE1); ErrorPrintf(IDS_HELP_USAGE2); ErrorPrintf(IDS_HELP_USAGE3); ErrorPrintf(IDS_HELP_USAGE4); ErrorPrintf(IDS_HELP_USAGE5); } /* Usage() */ /******************************************************************************* * * DeleteMappedPort * * This routine deletes the specified mapped port * * * ENTRY: * PWCHAR pwcport (In): Pointer to port mapping to delete * * EXIT: * TRUE: port was deleted * FALSE: error deleting port * ******************************************************************************/ BOOL DeleteMappedPort(PWCHAR pwcport) { ULONG rc; PWCHAR pwch; WCHAR wcbuff[MAX_PATH]; // Check if this a serial device and if it is, remove it if (!GetNTObjectName(pwcport, wcbuff, sizeof(wcbuff)/sizeof(WCHAR)) && IsSerialDevice(wcbuff)) { if (DefineDosDevice(DDD_REMOVE_DEFINITION, pwcport, NULL)) { return(TRUE); } else { rc = GetLastError(); } } else { rc = ERROR_FILE_NOT_FOUND; } StringDwordErrorPrintf(IDS_ERROR_DEL_PORT_MAPPING, pwcport, rc); return(FALSE); } /******************************************************************************* * * GetPorts * * This routine converts the string to the source and destination ports * * * ENTRY: * PWCHAR pwcstring (In): Pointer to user string * PWCHAR pwcSrcPort (Out): Pointer to return source port * PWCHAR pwcSrcPort (Out): Pointer to return destination port * ULONG ulbufsize (In): Size of return buffers * * EXIT: * TRUE: string converted to source and destination ports * FALSE: error * ******************************************************************************/ BOOL GetPorts(PWCHAR pwcstring, PWCHAR pwcSrcPort, PWCHAR pwcDestPort, ULONG ulbufsize) { PWCHAR pwch; ULONG ulcnt; BOOL fSawEqual = FALSE; pwch = pwcstring; // find next non alphanumeric character for (ulcnt = 0; pwch[ulcnt] && iswalnum(pwch[ulcnt]); ulcnt++) { } // Get the source port if (pwch[ulcnt] && (ulcnt < ulbufsize)) { wcsncpy(pwcSrcPort, pwch, ulcnt); } else { return(FALSE); } pwcSrcPort[ulcnt] = L'\0'; pwch += ulcnt; // get to destination port while (*pwch && !iswalnum(*pwch)) { if (*pwch == L'=') { fSawEqual = TRUE; } pwch++; } // If the syntax is OK and there's room in the buffer, copy the dest. port if (*pwch && fSawEqual && (wcslen(pwch) < ulbufsize)) { wcscpy(pwcDestPort, pwch); } else { return(FALSE); } // remove the : if they entered comn: if (pwch = wcsrchr(pwcSrcPort, L':')) { *pwch = L'\0'; } if (pwch = wcsrchr(pwcDestPort, L':')) { *pwch = L'\0'; } return(TRUE); } /******************************************************************************* * * MapPorts * * This routine maps the source port number to the destination port. * * * ENTRY: * PWCHAR pwcSrcPort (In): Source port * PWCHAR pwcDestPort (In): Destination port * * EXIT: * TRUE: port was mapped * FALSE: error mapping port * ******************************************************************************/ BOOL MapPorts(PWCHAR pwcSrcPort, PWCHAR pwcDestPort) { ULONG rc = ERROR_FILE_NOT_FOUND; WCHAR wcdest[MAX_PATH], wcsrc[MAX_PATH]; // Get the NT name of the destination and make sure it's a serial device rc = GetNTObjectName(pwcDestPort, wcdest, sizeof(wcdest)/sizeof(WCHAR)); if ((rc == 0) && IsSerialDevice(wcdest)) { // see if this mapping already exists if (!GetNTObjectName(pwcSrcPort, wcsrc, sizeof(wcsrc)/sizeof(WCHAR)) && !_wcsicmp(wcdest, wcsrc)) { ErrorPrintf(IDS_ERROR_PORT_MAPPING_EXISTS, pwcSrcPort, pwcDestPort); return(FALSE); } if (DefineDosDevice(DDD_RAW_TARGET_PATH, pwcSrcPort, wcdest)) { return(TRUE); } else { rc = GetLastError(); } } StringDwordErrorPrintf(IDS_ERROR_CREATE_PORT_MAPPING, pwcSrcPort, rc); return(FALSE); } /******************************************************************************* * * GetNTObjectName * * This routine returns the NT object name for a DOS device. * * ENTRY: * PWCHAR pwcDOSdev (In): pointer to DOS device name * PWCHAR pwcNTObjName (Out): pointer for NT object name * ULONG ulbufsize (In): size (in wide chars) of object name buffer * * EXIT: * Success: * returns 0 * Failure: * returns error code * ******************************************************************************/ ULONG GetNTObjectName(PWCHAR pwcDOSdev, PWCHAR pwcNTObjName, ULONG ulbufsize) { WCHAR wcbuff[MAX_PATH]; PWCHAR pwch; // Make a copy of the name passed in wcscpy(wcbuff, pwcDOSdev); // Strip off any trailing colon (comn:) if (pwch = wcsrchr(wcbuff, L':')) { *pwch = L'\0'; } if (QueryDosDevice(pwcDOSdev, pwcNTObjName, ulbufsize)) { return(0); } else { return(GetLastError()); } } /******************************************************************************* * * ListSerialPorts * * This routine lists all of the mapped ports. * * ENTRY: * * EXIT: * ******************************************************************************/ void ListSerialPorts(void) { ULONG ulcnt, rc; WCHAR TargetPath[4096]; PWCH pwch; PCOMNAME pComList = NULL; PCOMNAME pEntry, pPrev; DWORD dwBufferSize = 2048; WCHAR *DeviceNames = malloc(dwBufferSize); if (!DeviceNames) { ErrorPrintf(IDS_ERROR_MALLOC); free(DeviceNames); return; } // // Get all of the defined DOS devices // // // QueryDosDevice function returns success even if buffer is too small! // Lets get around it // SetLastError(0); while ( (!QueryDosDevice(NULL, DeviceNames, dwBufferSize / sizeof(WCHAR))) || (GetLastError() == ERROR_INSUFFICIENT_BUFFER) ) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { SetLastError(0); free(DeviceNames); dwBufferSize *= 2; DeviceNames = malloc(dwBufferSize); if (!DeviceNames) { ErrorPrintf(IDS_ERROR_MALLOC); return; } } else { ErrorPrintf(IDS_ERROR_GETTING_COMPORTS, GetLastError()); free(DeviceNames); return; } } pwch = DeviceNames; // Go through each DOS device and get it's NT object name, then check if // it's a serial device, and if so display it while (*pwch) { rc = GetNTObjectName(pwch, TargetPath, sizeof(TargetPath)/sizeof(WCHAR)); if (rc) { ErrorPrintf(IDS_ERROR_GETTING_COMPORTS, rc); } else if (IsSerialDevice(TargetPath)) { AddComName(&pComList, TargetPath, pwch); } pwch += wcslen(pwch) + 1; } if (pComList) { // print out the entries pEntry = pComList; while (pEntry) { wprintf(L"%s = %s\n", pEntry->com_pwcDOSName, pEntry->com_pwcNTName); pPrev = pEntry; pEntry = pEntry->com_pnext; DelComName(pPrev); } } else { ErrorPrintf(IDS_ERROR_NO_SERIAL_PORTS); } free(DeviceNames); } /******************************************************************************* * * IsSerialDevice * * This routine checks if the NT file name is a serial device * * * ENTRY: * PWCHAR pwcName (In): Pointer to name to check * * EXIT: * TRUE: Is a serial device * FALSE: Not a serial device * ******************************************************************************/ BOOL IsSerialDevice(PWCHAR pwcName) { NTSTATUS Status; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; FILE_FS_DEVICE_INFORMATION FileFSDevInfo; OBJECT_ATTRIBUTES ObjFile; UNICODE_STRING UniFile; WCHAR wcbuff[MAX_PATH]; WCHAR wcvalue[MAX_PATH]; PWCHAR pwch; HKEY hKey; ULONG ulType, ulSize, ulcnt, ulValSize; BOOL fIsSerial = FALSE; if (IsVDMdeviceName(pwcName)) { return FALSE; } RtlInitUnicodeString(&UniFile, pwcName); InitializeObjectAttributes(&ObjFile, &UniFile, OBJ_CASE_INSENSITIVE, NULL, NULL); // // Open the device // Status = NtOpenFile(&Handle, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjFile, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); if (NT_SUCCESS(Status)) { Status = NtQueryVolumeInformationFile(Handle, &IoStatusBlock, &FileFSDevInfo, sizeof(FileFSDevInfo), FileFsDeviceInformation); // Check if this is actually a serial device or not if (NT_SUCCESS(Status)) { if ( (FileFSDevInfo.DeviceType == FILE_DEVICE_NETWORK_REDIRECTOR) || (FileFSDevInfo.DeviceType == FILE_DEVICE_SERIAL_PORT) ) { fIsSerial = TRUE; } } // Close the file handle NtClose(Handle); } else { // If we couldn't open the device, look for the name in the registry #ifdef DEBUG wprintf(L"Error opening: %s, error = %x\n", pwcName, Status); #endif // strip off the leading \device pwch = wcschr(pwcName+2, L'\\'); if (pwch != NULL) { pwch++; // If we haven't built the list of valid names from the registry, // build it. if (pValidNames == NULL) { // Open the serialcomm entry in the registry if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\DeviceMap\\SerialComm", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { ulValSize = ulSize = MAX_PATH; ulcnt = 0; // Put all of the valid entries into the valid names list while (!RegEnumValue (hKey, ulcnt++, wcvalue, &ulValSize, NULL, &ulType, (LPBYTE) wcbuff, &ulSize)) { if (ulType != REG_SZ) continue; AddComName(&pValidNames, wcvalue, wcbuff); ulValSize = ulSize = MAX_PATH; } RegCloseKey(hKey); } } // look for the name in the list of valid com names if (FindComName(pValidNames, pwch)) { fIsSerial = TRUE; } } } return(fIsSerial); } /***************************************************************************** * * AddComName * * This routines adds a new node onto the specified com port names. * * ENTRY: * PCOMNAME *pComList (In) - Pointer to list to add entry to * PWCHAR pwcNTName (In) - NT name of device * PWCHAR pwcDOSName (In) - DOW name of device * * EXIT: * SUCCESS: * return ERROR_SUCCESS * FAILURE: * returns error code * ****************************************************************************/ ULONG AddComName(PCOMNAME *pComList, PWCHAR pwcNTName, PWCHAR pwcDOSName) { PCOMNAME pnext, pprev, pnew; LONG rc = ERROR_SUCCESS; if (pnew = malloc(sizeof(COMNAME))) { // clear out the new entry memset(pnew, 0, sizeof(COMNAME)); // Allocate and initialize the NT name if (pnew->com_pwcNTName = malloc((wcslen(pwcNTName) + 1)*sizeof(WCHAR))) { wcscpy(pnew->com_pwcNTName, pwcNTName); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } // Allocate and initialize the DOS name if ((rc == ERROR_SUCCESS) && (pnew->com_pwcDOSName = malloc((wcslen(pwcDOSName) + 1)*sizeof(WCHAR)))) { wcscpy(pnew->com_pwcDOSName, pwcDOSName); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } } else { rc = ERROR_NOT_ENOUGH_MEMORY; } // If we allocate everything OK, add the node into the list if (rc == ERROR_SUCCESS) { pprev = NULL; pnext = *pComList; // Insert the entry into the list in ascending order while (pnext && ((rc = _wcsicmp(pwcDOSName, pnext->com_pwcDOSName)) > 0)) { pprev = pnext; pnext = pnext->com_pnext; } // just return if this name is already in the list if (pnext && (rc == 0)) { return(ERROR_SUCCESS); } // Insert this entry into the list pnew->com_pnext = pnext; // If this is going to the front of the list, update list pointer if (pprev == NULL) { *pComList = pnew; } else { pprev->com_pnext = pnew; } } else if (pnew) { // Didn't allocate everything, release the memory we got DelComName(pnew); } return(rc); } /***************************************************************************** * * DelComName * * This routines frees up the memory allocated to a com name node. * * ENTRY: * PCOMNAME pEntry (In) - Node to delete * * EXIT: * NONE * ****************************************************************************/ void DelComName(PCOMNAME pEntry) { if (pEntry) { if (pEntry->com_pwcNTName) { free(pEntry->com_pwcNTName); } if (pEntry->com_pwcDOSName) { free(pEntry->com_pwcDOSName); } free(pEntry); } } /***************************************************************************** * * FindComName * * This routines searches for the specified name in the com port list. * * ENTRY: * PCOMNAME pComList (In) - List to search * PWCHAR pwcName (In) - Name to search for * * EXIT: * SUCCESS: * returns pointer to node containing the specified name * FAILURE: * returns NULL (name not found) * ****************************************************************************/ PCOMNAME FindComName(PCOMNAME pComList, PWCHAR pwcName) { PCOMNAME pcom; pcom = pComList; while (pcom) { //Check if the name matches either the NT or DOS device name if (!_wcsicmp(pwcName, pcom->com_pwcDOSName) || !_wcsicmp(pwcName, pcom->com_pwcNTName)) { return(pcom); } pcom = pcom->com_pnext; } return(NULL); } BOOL IsVDMdeviceName(PWCHAR pwcName) { UINT index; UINT vdmlength = wcslen(L"VDM"); for (index = 0; (index+vdmlength-1) < wcslen(pwcName); index++) { if (_wcsnicmp(&pwcName[index], L"VDM", vdmlength) == 0) { return TRUE; } } return FALSE; }