API
ttyIOUtils.cpp
Go to the documentation of this file.
1 /** \file ttyIOUtils.cpp
2  * \brief Utilities for i/o on a file descriptor pointing to a tty device.
3  * \author Jared R. Males (jaredmales@gmail.com)
4  *
5  * \ingroup tty_files
6  */
7 
8 #include "ttyIOUtils.hpp"
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <poll.h>
13 #include <termios.h>
14 
15 #include <mx/sys/timeUtils.hpp>
16 
17 #include "ttyErrors.hpp"
18 
19 
20 
21 namespace MagAOX
22 {
23 namespace tty
24 {
25 
26 int telnetCRLF( std::string & telnetStr, // [out] the string with \\r an \\n converted to \\r\\n
27  const std::string & inputStr // [in] the string to be converted
28  )
29 {
30  telnetStr.resize(inputStr.size());
31 
32  size_t N = inputStr.size();
33  size_t j = 0;
34  for(size_t i=0;i<N; ++i)
35  {
36  if(inputStr[i] != '\r' && inputStr[i] != '\n')
37  {
38  telnetStr[j] = inputStr[i];
39  }
40  else if(inputStr[i] == '\r')
41  {
42  telnetStr[j] = '\r';
43 
44  if(i < N-1)
45  {
46  if(inputStr[i+1] == '\n')
47  {
48  ++j;
49  telnetStr[j] = '\n';
50  ++i;
51  ++j; //i is incremented on continue, but j is not
52  continue;
53  }
54  }
55  telnetStr.push_back(' ');
56  ++j;
57  telnetStr[j] = '\n';
58  }
59  else if(inputStr[i] == '\n')
60  {
61  telnetStr[j] = '\r';
62  telnetStr.push_back(' ');
63  ++j;
64  telnetStr[j] = '\n';
65  }
66  ++j;
67  }
68 
69  return 0;
70 }
71 
72 int ttyOpenRaw( int & fileDescrip, // [out] the file descriptor. Set to 0 on an error.
73  std::string & deviceName, // [in] the device path name, e.g. /dev/ttyUSB0
74  speed_t speed // [in] indicates the baud rate (see http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html)
75  )
76 {
77  errno = 0;
78 
79  fileDescrip = ::open( deviceName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
80 
81 
82  struct termios termopt;
83  if( tcgetattr(fileDescrip, &termopt) < 0 )
84  {
85  close(fileDescrip);
86  fileDescrip = 0;
87  return TTY_E_TCGETATTR;
88  }
89 
90  if( cfsetispeed(&termopt, speed) < 0 )
91  {
92  close(fileDescrip);
93  fileDescrip = 0;
94  return TTY_E_SETISPEED;
95  }
96 
97  if( cfsetospeed(&termopt, speed) < 0 )
98  {
99  close(fileDescrip);
100  fileDescrip = 0;
101  return TTY_E_SETOSPEED;
102  }
103 
104  cfmakeraw(&termopt);
105 
106  if( tcsetattr(fileDescrip, TCSANOW, &termopt) < 0 )
107  {
108  close(fileDescrip);
109  fileDescrip = 0;
110  return TTY_E_TCSETATTR;
111  }
112 
113  return TTY_E_NOERROR;
114 }
115 
116 bool isEndOfTrans( const std::string & strRead, // [in] The read buffer to check
117  const std::string & eot // [in] The end-of-transmission string
118  )
119 {
120  //If buffRead isn't long enough yet.
121  if(eot.size() > strRead.size()) return false;
122 
123  //Now check from back, if any don't match it's false.
124  for(size_t i=0; i < eot.size(); ++i)
125  {
126  if( strRead[strRead.size()-1-i] != eot[eot.size()-1-i] ) return false;
127  }
128 
129  return true;
130 }
131 
132 int ttyWrite( const std::string & buffWrite, // [in] The characters to write to the tty.
133  int fd, // [in] The file descriptor of the open tty.
134  int timeoutWrite // [in] The timeout in milliseconds.
135  )
136 {
137  double t0;
138  struct pollfd pfd;
139 
140  errno = 0;
141  pfd.fd = fd;
142  pfd.events = POLLOUT;
143 
144  t0 = mx::sys::get_curr_time();
145 
146  size_t totWritten = 0;
147  while( totWritten < buffWrite.size())
148  {
149  int timeoutCurrent = timeoutWrite - (mx::sys::get_curr_time()-t0)*1000;
150  if(timeoutCurrent < 0) return TTY_E_TIMEOUTONWRITE;
151 
152  int rv = poll( &pfd, 1, timeoutCurrent);
153  if( rv == 0 ) return TTY_E_TIMEOUTONWRITEPOLL;
154  else if( rv < 0 ) return TTY_E_ERRORONWRITEPOLL;
155 
156  rv = write(fd, buffWrite.c_str()+totWritten, buffWrite.size()-totWritten);
157  if(rv < 0) return TTY_E_ERRORONWRITE;
158 
159  //sleep(1);
160  #ifdef TTY_DEBUG
161  std::cerr << "Wrote " << rv << " chars of " << buffWrite.size() << "\n";
162  #endif
163 
164  totWritten += rv;
165 
166  if( ( mx::sys::get_curr_time()-t0)*1000 > timeoutWrite ) return TTY_E_TIMEOUTONWRITE;
167  }
168 
169  return TTY_E_NOERROR;
170 }
171 
172 int ttyReadRaw( std::vector<unsigned char> & vecRead, // [out] The buffer in which to store the output.
173  int & readBytes, // [out] The number of bytes read.
174  int fd, // [in] The file descriptor of the open tty.
175  int timeoutRead // [in] The timeout in milliseconds.
176  )
177 {
178  int rv;
179 
180  struct pollfd pfd;
181 
182  errno = 0;
183 
184  pfd.fd = fd;
185  pfd.events = POLLIN;
186 
187 
188  rv = poll( &pfd, 1, timeoutRead);
189  if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
190  if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
191 
192  readBytes = 0;
193 
194  rv = read(fd, vecRead.data(), vecRead.size());
195  if( rv < 0 ) return TTY_E_ERRORONREAD;
196 
197 
198  readBytes = rv;
199 
200 
201  return TTY_E_NOERROR;
202 
203 
204 }
205 
206 int ttyRead( std::string & strRead, // [out] The string in which to store the output.
207  int bytes, // [in] the number of bytes to read
208  int fd, // [in] The file descriptor of the open tty.
209  int timeoutRead // [in] The timeout in milliseconds.
210  )
211 {
212  int rv;
213  int timeoutCurrent;
214  double t0;
215 
216  struct pollfd pfd;
217 
218  errno = 0;
219 
220  pfd.fd = fd;
221  pfd.events = POLLIN;
222 
223  strRead.clear();
224  char buffRead[TTY_BUFFSIZE];
225 
226  //Start timeout clock for reading.
227  t0 = mx::sys::get_curr_time();
228  timeoutCurrent = timeoutRead;
229 
230  //Now read the response up to the eot.
231  strRead.clear();
232 
233  rv = poll( &pfd, 1, timeoutCurrent);
234  if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
235  if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
236 
237  rv = read(fd, buffRead, TTY_BUFFSIZE);
238  if( rv < 0 ) return TTY_E_ERRORONREAD;
239 
240  strRead.append( buffRead, rv);
241 
242  int totBytes = rv;
243 
244  while( totBytes < bytes )
245  {
246  timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
247  if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
248 
249  rv = poll( &pfd, 1, timeoutCurrent);
250  if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
251  if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
252 
253  rv = read(fd, buffRead, TTY_BUFFSIZE);
254  if( rv < 0 ) return TTY_E_ERRORONREAD;
255  buffRead[rv] ='\0';
256 
257  strRead.append( buffRead, rv);
258  totBytes += rv;
259 
260  #ifdef TTY_DEBUG
261  std::cerr << "ttyRead: read " << rv << " bytes. buffRead=" << buffRead << "\n";
262  #endif
263  }
264 
265 
266  return TTY_E_NOERROR;
267 
268 
269 }
270 
271 int ttyRead( std::string & strRead, // [out] The string in which to store the output.
272  const std::string & eot, // [in] A sequence of characters which indicates the end of transmission.
273  int fd, // [in] The file descriptor of the open tty.
274  int timeoutRead // [in] The timeout in milliseconds.
275  )
276 {
277  int rv;
278  int timeoutCurrent;
279  double t0;
280 
281  struct pollfd pfd;
282 
283  errno = 0;
284 
285  pfd.fd = fd;
286  pfd.events = POLLIN;
287 
288  strRead.clear();
289  char buffRead[TTY_BUFFSIZE];
290 
291  //Start timeout clock for reading.
292  t0 = mx::sys::get_curr_time();
293  timeoutCurrent = timeoutRead;
294 
295  //Now read the response up to the eot.
296  strRead.clear();
297 
298  rv = poll( &pfd, 1, timeoutCurrent);
299  if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
300  if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
301 
302  rv = read(fd, buffRead, TTY_BUFFSIZE);
303  if( rv < 0 ) return TTY_E_ERRORONREAD;
304 
305  strRead.append( buffRead, rv);
306 
307  while( !isEndOfTrans(strRead, eot) )
308  {
309  timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
310  if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
311 
312  rv = poll( &pfd, 1, timeoutCurrent);
313  if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
314  if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
315 
316  rv = read(fd, buffRead, TTY_BUFFSIZE);
317  if( rv < 0 ) return TTY_E_ERRORONREAD;
318  buffRead[rv] ='\0';
319 
320  strRead.append( buffRead, rv);
321  #ifdef TTY_DEBUG
322  std::cerr << "ttyRead: read " << rv << " bytes. buffRead=" << buffRead << "\n";
323  #endif
324  }
325 
326 
327  return TTY_E_NOERROR;
328 
329 
330 }
331 
332 int ttyWriteRead( std::string & strRead, // [out] The string in which to store the output.
333  const std::string & strWrite, // [in] The characters to write to the tty.
334  const std::string & eot, // [in] A sequence of characters which indicates the end of transmission.
335  bool swallowEcho, // [in] If true, strWrite.size() characters are read after the write
336  int fd, // [in] The file descriptor of the open tty.
337  int timeoutWrite, // [in] The write timeout in milliseconds.
338  int timeoutRead // [in] The read timeout in milliseconds.
339  )
340 {
341  strRead.clear();
342 
343  int rv;
344 
345  //Write First
346  rv = ttyWrite( strWrite, fd, timeoutWrite);
347  if(rv != TTY_E_NOERROR) return rv;
348 
349 
350 
351  //Now read response from console
352  int timeoutCurrent;
353  double t0;
354 
355 
356  //Start timeout clock for reading.
357  t0 = mx::sys::get_curr_time();;
358 
359  if(swallowEcho)
360  {
361  struct pollfd pfd;
362  pfd.fd = fd;
363  pfd.events = POLLIN;
364 
365  size_t totrv = 0;
366  char buffRead[TTY_BUFFSIZE];
367 
368  //First swallow the echo.
369  while( totrv <= strWrite.size() )
370  {
371  timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
372  if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
373 
374  rv = poll( &pfd, 1, timeoutCurrent);
375  if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
376  if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
377 
378  rv = read(fd, buffRead, TTY_BUFFSIZE);
379  if( rv < 0 ) return TTY_E_ERRORONREAD;
380 
381  totrv += rv;
382  }
383  }
384 
385  timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
386  if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
387 
388  //Now read the response up to the eot.
389  return ttyRead(strRead, eot, fd, timeoutCurrent);
390 }
391 
392 
393 
394 } //namespace tty
395 } //namespace MagAOX
396 
int ttyReadRaw(std::vector< unsigned char > &vecRead, int &readBytes, int fd, int timeoutRead)
Read from a tty console indicated by a file-descriptor, up to a given number of bytes.
Definition: ttyIOUtils.cpp:172
int ttyWriteRead(std::string &strRead, const std::string &strWrite, const std::string &eot, bool swallowEcho, int fd, int timeoutWrite, int timeoutRead)
Write to a tty on an open file descriptor, then get the result.
Definition: ttyIOUtils.cpp:332
int ttyWrite(const std::string &buffWrite, int fd, int timeoutWrite)
Write to the tty console indicated by a file descriptor.
Definition: ttyIOUtils.cpp:132
int ttyRead(std::string &strRead, int bytes, int fd, int timeoutRead)
Read from a tty console indicated by a file-descriptor, until a given number of bytes are read.
Definition: ttyIOUtils.cpp:206
int ttyOpenRaw(int &fileDescrip, std::string &deviceName, speed_t speed)
Open a file as a raw-mode tty device.
Definition: ttyIOUtils.cpp:72
bool isEndOfTrans(const std::string &strRead, const std::string &eot)
Check if the end of the buffer contains the end-of-transmission string.
Definition: ttyIOUtils.cpp:116
std::ostream & cerr()
int telnetCRLF(std::string &telnetStr, const std::string &inputStr)
Replace lone \r and \n with \r\n for telnet-ness.
Definition: ttyIOUtils.cpp:26
Definition: dm.hpp:24
Error numbers for the tty utilities.
#define TTY_E_TCGETATTR
Definition: ttyErrors.hpp:16
#define TTY_E_SETOSPEED
Definition: ttyErrors.hpp:18
#define TTY_E_TIMEOUTONREAD
Definition: ttyErrors.hpp:27
#define TTY_E_SETISPEED
Definition: ttyErrors.hpp:17
#define TTY_E_TIMEOUTONREADPOLL
Definition: ttyErrors.hpp:24
#define TTY_E_ERRORONWRITE
Definition: ttyErrors.hpp:22
#define TTY_E_ERRORONWRITEPOLL
Definition: ttyErrors.hpp:21
#define TTY_E_TCSETATTR
Definition: ttyErrors.hpp:19
#define TTY_E_ERRORONREADPOLL
Definition: ttyErrors.hpp:25
#define TTY_E_ERRORONREAD
Definition: ttyErrors.hpp:26
#define TTY_E_TIMEOUTONWRITEPOLL
Definition: ttyErrors.hpp:20
#define TTY_E_TIMEOUTONWRITE
Definition: ttyErrors.hpp:23
#define TTY_E_NOERROR
Definition: ttyErrors.hpp:15
Utilities for i/o on a file descriptor pointing to a tty device.
#define TTY_BUFFSIZE
Definition: ttyIOUtils.hpp:23