1 /* 2 * CTTY.C - ctty (Change TTY) command. 3 * 4 * This command redirects the first three standard handles 5 * stdin, stdout, stderr to another terminal. 6 * 7 * 8 * History: 9 * 10 * 14 Aug 1998 (John P Price) 11 * - Created dummy command. 12 * 13 * 2000/01/14 ska 14 * + Added to change the first three handles to the given device name 15 * + Supports only redirection of stdin and stdout, e.g.: 16 * C:\> CTTY COM1 >file 17 * -or- 18 * C:\> echo Hallo | CTTY COM1 | echo du 19 * The CTTY command effects the commands on the _next_ line. 20 * 21 * 20 Oct 2016 (Hermes Belusca-Maito) 22 * Port it to NT. 23 */ 24 25 #include "precomp.h" 26 27 #if defined(INCLUDE_CMD_CTTY) && defined(FEATURE_REDIRECTION) 28 29 static WORD 30 CheckTerminalDeviceType(IN LPCTSTR pszName) 31 { 32 /* Console reserved "file" names */ 33 static const LPCWSTR DosLPTDevice = L"LPT"; 34 static const LPCWSTR DosCOMDevice = L"COM"; 35 static const LPCWSTR DosPRNDevice = L"PRN"; 36 static const LPCWSTR DosAUXDevice = L"AUX"; 37 static const LPCWSTR DosCONDevice = L"CON"; 38 static const LPCWSTR DosNULDevice = L"NUL"; 39 40 LPCWSTR DeviceName; 41 ULONG DeviceNameInfo; 42 WORD DeviceType = 0; // 0: Unknown; 1: CON; 2: COM etc... 43 44 #ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16 45 /* Convert from the current process/thread's codepage to UTF-16 */ 46 DWORD len = strlen(pszName) + 1; 47 WCHAR *buffer = cmd_alloc(len * sizeof(WCHAR)); 48 if (!buffer) 49 { 50 // SetLastError(ERROR_NOT_ENOUGH_MEMORY); 51 return FALSE; 52 } 53 len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP 54 0, pszName, (INT)len, buffer, (INT)len); 55 DeviceName = buffer; 56 #else 57 DeviceName = pszName; 58 #endif 59 60 /* 61 * Check whether we deal with a DOS device, and if so, 62 * strip the path till the file name. 63 * Therefore, things like \\.\CON or C:\some_path\COM1 64 * are transformed into CON or COM1, for example. 65 */ 66 DeviceNameInfo = RtlIsDosDeviceName_U(DeviceName); 67 if (DeviceNameInfo != 0) 68 { 69 DeviceName = (LPCWSTR)((ULONG_PTR)DeviceName + ((DeviceNameInfo >> 16) & 0xFFFF)); 70 71 if (_wcsnicmp(DeviceName, DosCONDevice, 3) == 0) 72 { 73 DeviceType = 1; 74 } 75 else 76 if ( _wcsnicmp(DeviceName, DosLPTDevice, 3) == 0 || 77 _wcsnicmp(DeviceName, DosCOMDevice, 3) == 0 || 78 _wcsnicmp(DeviceName, DosPRNDevice, 3) == 0 || 79 _wcsnicmp(DeviceName, DosAUXDevice, 3) == 0 || 80 _wcsnicmp(DeviceName, DosNULDevice, 3) == 0 ) 81 { 82 DeviceType = 2; 83 } 84 // else DeviceType = 0; 85 } 86 87 #ifndef _UNICODE 88 cmd_free(buffer); 89 #endif 90 91 return DeviceType; 92 } 93 94 /* 95 * See also redir.c!PerformRedirection(). 96 * 97 * The CTTY command allows only the usage of CON, COM, AUX, LPT, PRN and NUL 98 * DOS devices as valid terminal devices. Everything else is forbidden. 99 * 100 * CTTY does not set ERRORLEVEL on error. 101 */ 102 INT cmd_ctty(LPTSTR param) 103 { 104 static SECURITY_ATTRIBUTES SecAttr = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; 105 106 BOOL Success; 107 WORD DeviceType; 108 HANDLE hDevice, hStdHandles[3]; // hStdIn, hStdOut, hStdErr; 109 110 /* The user asked for help */ 111 if (_tcsncmp(param, _T("/?"), 2) == 0) 112 { 113 ConOutResPaging(TRUE, STRING_CTTY_HELP); 114 return 0; 115 } 116 117 if (!*param) 118 { 119 error_req_param_missing(); 120 return 1; 121 } 122 123 /* Check whether this is a valid terminal device name */ 124 DeviceType = CheckTerminalDeviceType(param); 125 if (DeviceType == 1) 126 { 127 /* 128 * Special case for CON device. 129 * 130 * We do not open CON with GENERIC_READ or GENERIC_WRITE as is, 131 * but instead we separately open CONIN$ and CONOUT$ with both 132 * GENERIC_READ | GENERIC_WRITE access. 133 * We do so because otherwise, opening in particular CON with GENERIC_WRITE 134 * only would open CONOUT$ with an handle not passing the IsConsoleHandle() 135 * test, meaning that we could not use the full console functionalities. 136 */ 137 BOOL bRetry = FALSE; 138 139 RetryOpenConsole: 140 /* 141 * If we previously failed in opening handles to the console, 142 * this means the existing console is almost destroyed. 143 * Close the existing console and allocate and open a new one. 144 */ 145 if (bRetry) 146 { 147 FreeConsole(); 148 if (!AllocConsole()) 149 return 1; 150 } 151 152 /* Attempt to retrieve a handle for standard input */ 153 hStdHandles[0] = CreateFile(_T("CONIN$"), 154 GENERIC_READ | GENERIC_WRITE, 155 FILE_SHARE_READ | FILE_SHARE_WRITE, 156 &SecAttr, 157 OPEN_EXISTING, 158 0, 159 NULL); 160 if (hStdHandles[0] == INVALID_HANDLE_VALUE) 161 { 162 // TODO: Error 163 // error_no_rw_device(param); 164 165 if (bRetry) 166 return 1; 167 bRetry = TRUE; 168 goto RetryOpenConsole; 169 } 170 171 /* Attempt to retrieve a handle for standard output. 172 * Note that GENERIC_READ is needed for IsConsoleHandle() to succeed afterwards. */ 173 hStdHandles[1] = CreateFile(_T("CONOUT$"), 174 GENERIC_READ | GENERIC_WRITE, 175 FILE_SHARE_READ | FILE_SHARE_WRITE, 176 &SecAttr, 177 OPEN_EXISTING, 178 0, 179 NULL); 180 if (hStdHandles[1] == INVALID_HANDLE_VALUE) 181 { 182 // TODO: Error 183 // error_no_rw_device(param); 184 185 CloseHandle(hStdHandles[0]); 186 187 if (bRetry) 188 return 1; 189 bRetry = TRUE; 190 goto RetryOpenConsole; 191 } 192 193 /* Duplicate a handle for standard error */ 194 Success = DuplicateHandle(GetCurrentProcess(), 195 hStdHandles[1], 196 GetCurrentProcess(), 197 &hStdHandles[2], 198 0, // GENERIC_WRITE, 199 TRUE, 200 DUPLICATE_SAME_ACCESS /* 0 */); 201 if (!Success) 202 { 203 // TODO: Error 204 // error_no_rw_device(param); 205 CloseHandle(hStdHandles[1]); 206 CloseHandle(hStdHandles[0]); 207 return 1; 208 } 209 } 210 else if (DeviceType == 2) 211 { 212 /* 213 * COM and the other devices can only be opened once. 214 * Since we need different handles, we need to duplicate them. 215 */ 216 217 /* Attempt to retrieve a handle to the device for read/write access */ 218 hDevice = CreateFile(param, 219 GENERIC_READ | GENERIC_WRITE, 220 FILE_SHARE_READ | FILE_SHARE_WRITE, 221 &SecAttr, 222 OPEN_EXISTING, 223 0, // FILE_FLAG_OVERLAPPED, // 0, 224 NULL); 225 if (hDevice == INVALID_HANDLE_VALUE) 226 { 227 // TODO: Error 228 // error_no_rw_device(param); 229 return 1; 230 } 231 232 /* Duplicate a handle for standard input */ 233 Success = DuplicateHandle(GetCurrentProcess(), 234 hDevice, 235 GetCurrentProcess(), 236 &hStdHandles[0], 237 GENERIC_READ, 238 TRUE, 239 0); 240 if (!Success) 241 { 242 // TODO: Error 243 // error_no_rw_device(param); 244 CloseHandle(hDevice); 245 return 1; 246 } 247 248 /* Duplicate a handle for standard output */ 249 Success = DuplicateHandle(GetCurrentProcess(), 250 hDevice, 251 GetCurrentProcess(), 252 &hStdHandles[1], 253 GENERIC_WRITE, 254 TRUE, 255 0); 256 if (!Success) 257 { 258 // TODO: Error 259 // error_no_rw_device(param); 260 CloseHandle(hStdHandles[0]); 261 CloseHandle(hDevice); 262 return 1; 263 } 264 265 /* Duplicate a handle for standard error */ 266 Success = DuplicateHandle(GetCurrentProcess(), 267 hDevice, 268 GetCurrentProcess(), 269 &hStdHandles[2], 270 GENERIC_WRITE, 271 TRUE, 272 0); 273 if (!Success) 274 { 275 // TODO: Error 276 // error_no_rw_device(param); 277 CloseHandle(hStdHandles[1]); 278 CloseHandle(hStdHandles[0]); 279 CloseHandle(hDevice); 280 return 1; 281 } 282 283 /* Now get rid of the main device handle */ 284 CloseHandle(hDevice); 285 } 286 else 287 { 288 // FIXME: Localize! 289 ConOutPrintf(L"Invalid device '%s'\n", param); 290 return 1; 291 } 292 293 #if 0 294 /* Now change the file descriptors: 295 0 := rdonly 296 1,2 := wronly 297 298 if CTTY is called within a pipe or its I/O is redirected, 299 oldinfd or oldoutfd is not equal to -1. In such case the 300 old*fd is modified in order to effect the file descriptor 301 after the redirections are restored. Otherwise a pipe or 302 redirection would left CTTY in a half-made status. 303 */ 304 // int failed; 305 failed = dup2(f, 2); /* no redirection support */ 306 if(oldinfd != -1) 307 dos_close(oldinfd); 308 oldinfd = f; 309 if(oldoutfd != -1) 310 dos_close(oldoutfd); 311 if((oldoutfd = dup(f)) == -1) 312 failed = 1; 313 314 if(failed) 315 error_ctty_dup(param); 316 317 return failed; 318 #endif 319 320 /* Now set the standard handles */ 321 322 hDevice = GetHandle(0); 323 if (hDevice != INVALID_HANDLE_VALUE) 324 CloseHandle(hDevice); 325 SetHandle(0, hStdHandles[0]); 326 327 hDevice = GetHandle(1); 328 if (hDevice != INVALID_HANDLE_VALUE) 329 CloseHandle(hDevice); 330 SetHandle(1, hStdHandles[1]); 331 332 hDevice = GetHandle(2); 333 if (hDevice != INVALID_HANDLE_VALUE) 334 CloseHandle(hDevice); 335 SetHandle(2, hStdHandles[2]); 336 337 return 0; 338 } 339 340 #endif /* INCLUDE_CMD_CTTY && FEATURE_REDIRECTION */ 341 342 /* EOF */ 343