windows-nt/Source/XPSP1/NT/termsrv/admtools/chgport/chgport.c
2020-09-26 16:20:57 +08:00

784 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 1998-1999 Microsoft Corporation
/******************************************************************************
*
* CHGPORT.C
*
* Change serial port mapping.
*
*
*
*******************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winstaw.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <utilsub.h>
#include <string.h>
#include <printfoa.h>
#include <locale.h>
#include "chgport.h"
/*
* 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;
setlocale(LC_ALL, ".OCP");
/*
* 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];
PWCHAR pFixedPort = NULL;
// Get the NT name of the destination and make sure it's a serial device
if (!GetNTObjectName(pwcDestPort, wcdest, sizeof(wcdest)/sizeof(WCHAR)) &&
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 DeviceNames[4096];
WCHAR TargetPath[4096];
PWCH pwch;
PCOMNAME pComList = NULL;
PCOMNAME pEntry, pPrev;
DWORD dwBufferSize = 2048;
WCHAR *DeviceNames = malloc(dwBufferSize);
if (!DeviceNames) {
ErrorPrintf(IDS_ERROR_MALLOC);
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());
}
}
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) &&
(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;
}