FreeRDP
comm_io.c
1 
20 #include <winpr/config.h>
21 
22 #include <winpr/assert.h>
23 #include <errno.h>
24 #include <termios.h>
25 #include <unistd.h>
26 
27 #include <winpr/io.h>
28 #include <winpr/wlog.h>
29 #include <winpr/wtypes.h>
30 
31 #include "comm.h"
32 
33 BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
34 {
35  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
36 
37  if (!CommIsHandled(hDevice))
38  return FALSE;
39 
40  pComm->permissive = permissive;
41  return TRUE;
42 }
43 
44 /* Computes VTIME in deciseconds from Ti in milliseconds */
45 static UCHAR svtime(ULONG Ti)
46 {
47  /* FIXME: look for an equivalent math function otherwise let
48  * do the compiler do the optimization */
49  if (Ti == 0)
50  return 0;
51  else if (Ti < 100)
52  return 1;
53  else if (Ti > 25500)
54  return 255; /* 0xFF */
55  else
56  return (UCHAR)(Ti / 100);
57 }
58 
68 BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
69  LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
70 {
71  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
72  int biggestFd = -1;
73  fd_set read_set;
74  int nbFds = 0;
75  COMMTIMEOUTS* pTimeouts = NULL;
76  UCHAR vmin = 0;
77  UCHAR vtime = 0;
78  LONGLONG Tmax = 0;
79  struct timeval tmaxTimeout;
80  struct timeval* pTmaxTimeout = NULL;
81  struct termios currentTermios;
82  EnterCriticalSection(&pComm->ReadLock); /* KISSer by the function's beginning */
83 
84  if (!CommIsHandled(hDevice))
85  goto return_false;
86 
87  if (lpOverlapped != NULL)
88  {
89  SetLastError(ERROR_NOT_SUPPORTED);
90  goto return_false;
91  }
92 
93  if (lpNumberOfBytesRead == NULL)
94  {
95  SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
96  goto return_false;
97  }
98 
99  *lpNumberOfBytesRead = 0; /* will be ajusted if required ... */
100 
101  if (nNumberOfBytesToRead <= 0) /* N */
102  {
103  goto return_true; /* FIXME: or FALSE? */
104  }
105 
106  if (tcgetattr(pComm->fd, &currentTermios) < 0)
107  {
108  SetLastError(ERROR_IO_DEVICE);
109  goto return_false;
110  }
111 
112  if (currentTermios.c_lflag & ICANON)
113  {
114  CommLog_Print(WLOG_WARN, "Canonical mode not supported"); /* the timeout could not be set */
115  SetLastError(ERROR_NOT_SUPPORTED);
116  goto return_false;
117  }
118 
119  /* http://msdn.microsoft.com/en-us/library/hh439614%28v=vs.85%29.aspx
120  * http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx
121  *
122  * ReadIntervalTimeout | ReadTotalTimeoutMultiplier | ReadTotalTimeoutConstant | VMIN | VTIME |
123  * TMAX | 0 | 0 | 0 | N | 0 |
124  * INDEF | Blocks for N bytes available. 0< Ti <MAXULONG | 0 | 0 |
125  * N | Ti | INDEF | Blocks on first byte, then use Ti between bytes. MAXULONG | 0 | 0
126  * | 0 | 0 | 0 | Returns immediately with bytes available (don't block) MAXULONG |
127  * MAXULONG | 0< Tc <MAXULONG | N | 0 | Tc | Blocks on first byte
128  * during Tc or returns immediately whith bytes available MAXULONG | m |
129  * MAXULONG | | Invalid 0 | m | 0< Tc
130  * <MAXULONG | N | 0 | Tmax | Blocks on first byte during Tmax or returns
131  * immediately whith bytes available 0< Ti <MAXULONG | m | 0<
132  * Tc <MAXULONG | N | Ti | Tmax | Blocks on first byte, then use Ti between bytes.
133  * Tmax is used for the whole system call.
134  */
135  /* NB: timeouts are in milliseconds, VTIME are in deciseconds and is an unsigned char */
136  /* FIXME: double check whether open(pComm->fd_read_event, O_NONBLOCK) doesn't conflict with
137  * above use cases */
138  pTimeouts = &(pComm->timeouts);
139 
140  if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
141  (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
142  {
143  CommLog_Print(
144  WLOG_WARN,
145  "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
146  SetLastError(ERROR_INVALID_PARAMETER);
147  goto return_false;
148  }
149 
150  /* VMIN */
151 
152  if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
153  (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
154  {
155  vmin = 0;
156  }
157  else
158  {
159  /* N */
160  /* vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255;*/ /* 0xFF */
161  /* NB: we might wait endlessly with vmin=N, prefer to
162  * force vmin=1 and return with bytes
163  * available. FIXME: is a feature disarded here? */
164  vmin = 1;
165  }
166 
167  /* VTIME */
168 
169  if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
170  {
171  /* Ti */
172  vtime = svtime(pTimeouts->ReadIntervalTimeout);
173  }
174 
175  /* TMAX */
176  pTmaxTimeout = &tmaxTimeout;
177 
178  if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
179  (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
180  {
181  /* Tc */
182  Tmax = pTimeouts->ReadTotalTimeoutConstant;
183  }
184  else
185  {
186  /* Tmax */
187  Tmax = 1ll * nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier +
188  1ll * pTimeouts->ReadTotalTimeoutConstant;
189 
190  /* INDEFinitely */
191  if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) &&
192  (pTimeouts->ReadTotalTimeoutMultiplier == 0))
193  pTmaxTimeout = NULL;
194  }
195 
196  if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
197  {
198  currentTermios.c_cc[VMIN] = vmin;
199  currentTermios.c_cc[VTIME] = vtime;
200 
201  if (tcsetattr(pComm->fd, TCSANOW, &currentTermios) < 0)
202  {
203  CommLog_Print(WLOG_WARN,
204  "CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8
205  ", VTIME=%" PRIu8 "",
206  vmin, vtime);
207  SetLastError(ERROR_IO_DEVICE);
208  goto return_false;
209  }
210  }
211 
212  /* wait indefinitely if pTmaxTimeout is NULL */
213 
214  if (pTmaxTimeout != NULL)
215  {
216  ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
217 
218  if (Tmax > 0) /* return immdiately if Tmax == 0 */
219  {
220  pTmaxTimeout->tv_sec = Tmax / 1000; /* s */
221  pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
222  }
223  }
224 
225  /* FIXME: had expected eventfd_write() to return EAGAIN when
226  * there is no eventfd_read() but this not the case. */
227  /* discard a possible and no more relevant event */
228 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
229  eventfd_read(pComm->fd_read_event, NULL);
230 #endif
231  biggestFd = pComm->fd_read;
232 
233  if (pComm->fd_read_event > biggestFd)
234  biggestFd = pComm->fd_read_event;
235 
236  FD_ZERO(&read_set);
237  WINPR_ASSERT(pComm->fd_read_event < FD_SETSIZE);
238  WINPR_ASSERT(pComm->fd_read < FD_SETSIZE);
239  FD_SET(pComm->fd_read_event, &read_set);
240  FD_SET(pComm->fd_read, &read_set);
241  nbFds = select(biggestFd + 1, &read_set, NULL, NULL, pTmaxTimeout);
242 
243  if (nbFds < 0)
244  {
245  char ebuffer[256] = { 0 };
246  CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno,
247  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
248  SetLastError(ERROR_IO_DEVICE);
249  goto return_false;
250  }
251 
252  if (nbFds == 0)
253  {
254  /* timeout */
255  SetLastError(ERROR_TIMEOUT);
256  goto return_false;
257  }
258 
259  /* read_set */
260 
261  if (FD_ISSET(pComm->fd_read_event, &read_set))
262  {
263 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
264  eventfd_t event = 0;
265 
266  if (eventfd_read(pComm->fd_read_event, &event) < 0)
267  {
268  if (errno == EAGAIN)
269  {
270  WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */
271  /* keep on */
272  }
273  else
274  {
275  char ebuffer[256] = { 0 };
276  CommLog_Print(WLOG_WARN,
277  "unexpected error on reading fd_read_event, errno=[%d] %s\n", errno,
278  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
279  /* FIXME: goto return_false ? */
280  }
281 
282  WINPR_ASSERT(errno == EAGAIN);
283  }
284 
285  if (event == WINPR_PURGE_RXABORT)
286  {
287  SetLastError(ERROR_CANCELLED);
288  goto return_false;
289  }
290 
291  WINPR_ASSERT(event == WINPR_PURGE_RXABORT); /* no other expected event so far */
292 #endif
293  }
294 
295  if (FD_ISSET(pComm->fd_read, &read_set))
296  {
297  ssize_t nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
298 
299  if ((nbRead < 0) || (nbRead > nNumberOfBytesToRead))
300  {
301  char ebuffer[256] = { 0 };
302  CommLog_Print(WLOG_WARN,
303  "CommReadFile failed, ReadIntervalTimeout=%" PRIu32
304  ", ReadTotalTimeoutMultiplier=%" PRIu32
305  ", ReadTotalTimeoutConstant=%" PRIu32 " VMIN=%u, VTIME=%u",
306  pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier,
307  pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN],
308  currentTermios.c_cc[VTIME]);
309  CommLog_Print(
310  WLOG_WARN, "CommReadFile failed, nNumberOfBytesToRead=%" PRIu32 ", errno=[%d] %s",
311  nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
312 
313  if (errno == EAGAIN)
314  {
315  /* keep on */
316  goto return_true; /* expect a read-loop to be implemented on the server side */
317  }
318  else if (errno == EBADF)
319  {
320  SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
321  goto return_false;
322  }
323  else
324  {
325  WINPR_ASSERT(FALSE);
326  SetLastError(ERROR_IO_DEVICE);
327  goto return_false;
328  }
329  }
330 
331  if (nbRead == 0)
332  {
333  /* termios timeout */
334  SetLastError(ERROR_TIMEOUT);
335  goto return_false;
336  }
337 
338  *lpNumberOfBytesRead = (UINT32)nbRead;
339 
340  EnterCriticalSection(&pComm->EventsLock);
341  if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
342  {
343  if (pComm->eventChar != '\0' && memchr(lpBuffer, pComm->eventChar, nbRead))
344  pComm->PendingEvents |= SERIAL_EV_RXCHAR;
345  }
346  LeaveCriticalSection(&pComm->EventsLock);
347  goto return_true;
348  }
349 
350  WINPR_ASSERT(FALSE);
351  *lpNumberOfBytesRead = 0;
352 return_false:
353  LeaveCriticalSection(&pComm->ReadLock);
354  return FALSE;
355 return_true:
356  LeaveCriticalSection(&pComm->ReadLock);
357  return TRUE;
358 }
359 
367 BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
368  LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
369 {
370  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
371  struct timeval tmaxTimeout;
372  struct timeval* pTmaxTimeout = NULL;
373  EnterCriticalSection(&pComm->WriteLock); /* KISSer by the function's beginning */
374 
375  if (!CommIsHandled(hDevice))
376  goto return_false;
377 
378  if (lpOverlapped != NULL)
379  {
380  SetLastError(ERROR_NOT_SUPPORTED);
381  goto return_false;
382  }
383 
384  if (lpNumberOfBytesWritten == NULL)
385  {
386  SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
387  goto return_false;
388  }
389 
390  *lpNumberOfBytesWritten = 0; /* will be ajusted if required ... */
391 
392  if (nNumberOfBytesToWrite <= 0)
393  {
394  goto return_true; /* FIXME: or FALSE? */
395  }
396 
397  /* FIXME: had expected eventfd_write() to return EAGAIN when
398  * there is no eventfd_read() but this not the case. */
399  /* discard a possible and no more relevant event */
400 
401 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
402  eventfd_read(pComm->fd_write_event, NULL);
403 #endif
404 
405  /* ms */
406  LONGLONG Tmax = 1ll * nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
407  1ll * pComm->timeouts.WriteTotalTimeoutConstant;
408  /* NB: select() may update the timeout argument to indicate
409  * how much time was left. Keep the timeout variable out of
410  * the while() */
411  pTmaxTimeout = &tmaxTimeout;
412  ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
413 
414  if (Tmax > 0)
415  {
416  pTmaxTimeout->tv_sec = Tmax / 1000; /* s */
417  pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
418  }
419  else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
420  (pComm->timeouts.WriteTotalTimeoutConstant == 0))
421  {
422  pTmaxTimeout = NULL;
423  }
424 
425  /* else return immdiately */
426 
427  while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
428  {
429  int biggestFd = -1;
430  fd_set event_set;
431  fd_set write_set;
432  int nbFds = 0;
433  biggestFd = pComm->fd_write;
434 
435  if (pComm->fd_write_event > biggestFd)
436  biggestFd = pComm->fd_write_event;
437 
438  FD_ZERO(&event_set);
439  FD_ZERO(&write_set);
440  WINPR_ASSERT(pComm->fd_write_event < FD_SETSIZE);
441  WINPR_ASSERT(pComm->fd_write < FD_SETSIZE);
442  FD_SET(pComm->fd_write_event, &event_set);
443  FD_SET(pComm->fd_write, &write_set);
444  nbFds = select(biggestFd + 1, &event_set, &write_set, NULL, pTmaxTimeout);
445 
446  if (nbFds < 0)
447  {
448  char ebuffer[256] = { 0 };
449  CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno,
450  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
451  SetLastError(ERROR_IO_DEVICE);
452  goto return_false;
453  }
454 
455  if (nbFds == 0)
456  {
457  /* timeout */
458  SetLastError(ERROR_TIMEOUT);
459  goto return_false;
460  }
461 
462  /* event_set */
463 
464  if (FD_ISSET(pComm->fd_write_event, &event_set))
465  {
466 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
467  eventfd_t event = 0;
468 
469  if (eventfd_read(pComm->fd_write_event, &event) < 0)
470  {
471  if (errno == EAGAIN)
472  {
473  WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */
474  /* keep on */
475  }
476  else
477  {
478  char ebuffer[256] = { 0 };
479  CommLog_Print(WLOG_WARN,
480  "unexpected error on reading fd_write_event, errno=[%d] %s\n",
481  errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
482  /* FIXME: goto return_false ? */
483  }
484 
485  WINPR_ASSERT(errno == EAGAIN);
486  }
487 
488  if (event == WINPR_PURGE_TXABORT)
489  {
490  SetLastError(ERROR_CANCELLED);
491  goto return_false;
492  }
493 
494  WINPR_ASSERT(event == WINPR_PURGE_TXABORT); /* no other expected event so far */
495 #endif
496  }
497 
498  /* write_set */
499 
500  if (FD_ISSET(pComm->fd_write, &write_set))
501  {
502  ssize_t nbWritten = 0;
503  nbWritten = write(pComm->fd_write, ((const BYTE*)lpBuffer) + (*lpNumberOfBytesWritten),
504  nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
505 
506  if (nbWritten < 0)
507  {
508  char ebuffer[256] = { 0 };
509  CommLog_Print(WLOG_WARN,
510  "CommWriteFile failed after %" PRIu32
511  " bytes written, errno=[%d] %s\n",
512  *lpNumberOfBytesWritten, errno,
513  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
514 
515  if (errno == EAGAIN)
516  {
517  /* keep on */
518  continue;
519  }
520  else if (errno == EBADF)
521  {
522  SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
523  goto return_false;
524  }
525  else
526  {
527  WINPR_ASSERT(FALSE);
528  SetLastError(ERROR_IO_DEVICE);
529  goto return_false;
530  }
531  }
532 
533  *lpNumberOfBytesWritten += nbWritten;
534  }
535  } /* while */
536 
537  /* FIXME: this call to tcdrain() doesn't look correct and
538  * might hide a bug but was required while testing a serial
539  * printer. Its driver was expecting the modem line status
540  * SERIAL_MSR_DSR true after the sending which was never
541  * happenning otherwise. A purge was also done before each
542  * Write operation. The serial port was opened with:
543  * DesiredAccess=0x0012019F. The printer worked fine with
544  * mstsc. */
545  tcdrain(pComm->fd_write);
546 
547 return_true:
548  LeaveCriticalSection(&pComm->WriteLock);
549  return TRUE;
550 
551 return_false:
552  LeaveCriticalSection(&pComm->WriteLock);
553  return FALSE;
554 }