[TFTPD] Fix compilation, and use the #define MAX_SERVERS where needed instead of...
[reactos.git] / base / shell / cmd / ctty.c
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 */