SHARE
TWEET

ttysniff.c

wtfbbq Apr 27th, 2015 784 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. #include <sys/time.h>
  6. #include <unistd.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <termios.h>
  10. #include <unistd.h>
  11. #include <errno.h>
  12. #include <signal.h>
  13. #include <sys/select.h>
  14. #include <ctype.h>
  15. #include <getopt.h>
  16.  
  17. #define VERSION "2.6.0"
  18.  
  19. /* values for parity */
  20. #define SERIAL_NO_PARITY    1
  21. #define SERIAL_EVEN_PARITY  2
  22. #define SERIAL_ODD_PARITY   4
  23.  
  24. /* values for data bits */
  25. #define SERIAL_8_DATA_BITS  8
  26. #define SERIAL_7_DATA_BITS  7
  27.  
  28. /* values for stop bits */
  29. #define SERIAL_1_STOP_BITS  1
  30. #define SERIAL_2_STOP_BITS  2
  31.  
  32. /* flow control */
  33. #define FLOW_CONTROL_NONE     1
  34. #define FLOW_CONTROL_HARDWARE 2
  35. #define FLOW_CONTROL_SOFTWARE 3
  36.  
  37. #define FALSE 0
  38. #define TRUE  1
  39.  
  40. #define DEFAULT_BAUD_RATE  B9600
  41. //#define BAUD_RATE  B19200
  42. //#define BAUD_RATE  B38400
  43.            
  44. #define NON_BLOCKING_SERIAL
  45.  
  46. char serial_port[FILENAME_MAX+1];
  47. char logfile_name[FILENAME_MAX+1];
  48.  
  49. /* log output to file */
  50. int opt_log_output = FALSE;
  51.  
  52. /* dump output as hex */
  53. int opt_hex_output = TRUE;  
  54.  
  55. /* strip high bit from all chars sent to stdout */
  56. int opt_strip_high_bit = FALSE;  
  57.  
  58. /* default is to transmit CRLF on CR from stdin; when true, only the CR is
  59.  * transmitted to remote
  60.  */
  61. int opt_bare_cr = FALSE;  
  62.  
  63. speed_t baudrate = DEFAULT_BAUD_RATE;
  64.  
  65. /* at some point, could make these command line configurable */
  66. int parity = SERIAL_NO_PARITY;
  67. //int databits = SERIAL_7_DATA_BITS;
  68. int databits = SERIAL_8_DATA_BITS;
  69. int stopbits = SERIAL_1_STOP_BITS;
  70. //int flow_control = FLOW_CONTROL_SOFTWARE;
  71. //int flow_control = FLOW_CONTROL_HARDWARE;
  72. int flow_control = FLOW_CONTROL_NONE;
  73.  
  74. int main_quit = 0;
  75.  
  76. typedef struct {
  77.     const char *str;
  78.     int len; /* string length of str */
  79.     speed_t speed;
  80. } BaudRate;
  81.  
  82. BaudRate baudrates[] = {
  83.     { "110",  3, B110   },
  84.     { "150",  3, B150   },
  85.     { "300",  3, B300   },
  86.     { "600",  3, B600   },
  87.     { "1200", 4, B1200  },
  88.     { "2400", 4, B2400  },
  89.     { "4800", 4, B4800  },
  90.     { "9600", 4, B9600  },
  91.     { "19200",  5, B19200 },
  92.     { "38400",  5, B38400 },
  93.     { "57600",  5, B57600 },
  94.     { "115200", 6, B115200},
  95.     { "230400", 6, B230400},
  96. };
  97.  
  98. const unsigned char CR = '\r';
  99. const unsigned char LF = '\n';
  100.  
  101. #if defined linux || defined unix || defined __GNUC__
  102. void signal_term( int signum )
  103. {
  104.     fprintf( stderr, "signal!\n" );
  105.     main_quit = 1;
  106. }
  107.  
  108. void init_signals( void )
  109. {
  110.     int uerr;
  111.     struct sigaction sigterm;
  112.     struct sigaction sigint;
  113.  
  114.     memset( &sigterm, 0, sizeof(sigterm) );
  115.     sigterm.sa_handler = signal_term;
  116.     uerr = sigaction( SIGTERM, &sigterm, NULL );
  117.     if( uerr != 0 ) {
  118.         perror( "sigaction(SIGTERM)" );
  119.         exit(1);
  120.     }
  121.  
  122.     memset( &sigint, 0, sizeof(sigint) );
  123.     sigint.sa_handler = signal_term;
  124.     uerr = sigaction( SIGINT, &sigint, NULL );
  125.     if( uerr != 0 ) {
  126.         perror( "sigaction(SIGINT)" );
  127.         exit(1);
  128.     }
  129. }
  130. #endif
  131.  
  132. int serial_open_port( void )
  133. {
  134.     struct termios oldtio, newtio;
  135.     int fd;
  136.  
  137.     /* stupid human check -- I've been running this thing
  138.      *  as root (very very dumb) so I don't want to accidently
  139.      *  write DNP to /dev/hda1 or something equally tragic.
  140.      */
  141. //    if( strncmp( path, "/dev/ttyS", 9 ) != 0 ) {
  142. //        LOG_MESSAGE1( LOG_ERR, "\"%s\" not a serial port.", path );        
  143. //        return ERR_FAIL;
  144. //    }
  145.  
  146.     /* davep 29-Apr-2009 ; temp debug */
  147.     fprintf( stderr, "%s opening %s\n", __FUNCTION__, serial_port );
  148.  
  149.     /* open serial port */
  150. #ifdef NON_BLOCKING_SERIAL
  151.     fd = open( serial_port, O_RDWR|O_NOCTTY|O_NONBLOCK );
  152. #else
  153.     fd = open( serial_port, O_RDWR|O_NOCTTY );
  154. #endif
  155.     if( fd < 0 ) {
  156.         fprintf( stderr, "open() of %s failed : %s",
  157.                 serial_port, strerror(errno) );
  158.         return -1;
  159.     }
  160.  
  161.     /* davep 29-Apr-2009 ; temp debug */
  162.     fprintf( stderr, "%s opened %s successfully\n", __FUNCTION__, serial_port );
  163.  
  164.     tcgetattr( fd, &oldtio );
  165.  
  166.     memset( &newtio, 0, sizeof(newtio) );
  167.  
  168.     newtio.c_cflag = CLOCAL | CREAD ;
  169.  
  170.     /* parity */
  171.     switch( parity ) {
  172.         case SERIAL_NO_PARITY :
  173.             /* nothing -- default is no parity */
  174.             break;
  175.  
  176.         case SERIAL_EVEN_PARITY :
  177.             newtio.c_cflag |= PARENB;
  178.             break;
  179.  
  180.         case SERIAL_ODD_PARITY :
  181.             newtio.c_cflag |= PARENB | PARODD;
  182.             break;
  183.  
  184.         default :
  185.             assert( 0 );
  186.     }
  187.  
  188.     /* data bits */
  189.     if( databits == SERIAL_8_DATA_BITS ) {
  190.         newtio.c_cflag |= CS8;
  191.     }
  192.     else if( databits == SERIAL_7_DATA_BITS ) {
  193.         newtio.c_cflag |= CS7;
  194.     }
  195.     else {
  196.         assert( 0 );
  197.     }
  198.  
  199.     /* stop bits */
  200.     if( stopbits == SERIAL_1_STOP_BITS ) {
  201.         /* nothing -- default is 1 stop bit */
  202.     }
  203.     else if( stopbits == SERIAL_2_STOP_BITS ) {
  204.         newtio.c_cflag |= CSTOPB;
  205.     }
  206.     else {
  207.         assert( 0 );
  208.     }
  209.  
  210.     /* hardware flow control? */
  211.     if( flow_control==FLOW_CONTROL_HARDWARE ) {
  212.         newtio.c_cflag |= CRTSCTS;
  213.     }
  214.  
  215.     newtio.c_lflag = 0;
  216.     /* no additional lflags */
  217.  
  218.     newtio.c_iflag = 0; // | IXON | IXANY | IXOFF | IMAXBEL;
  219.  
  220.     /* software flow control? */
  221.     if( flow_control==FLOW_CONTROL_SOFTWARE ) {
  222.         newtio.c_iflag |= (IXON | IXOFF | IXANY);
  223.     }
  224.  
  225.     newtio.c_oflag = 0;
  226.     /* no additional oflags */
  227.  
  228.     cfsetospeed( &newtio, baudrate );
  229.     cfsetispeed( &newtio, baudrate );
  230.  
  231. #ifndef NON_BLOCKING_SERIAL
  232.     newtio.c_cc[VMIN]  = 1; /* block until this many chars recvd */
  233.     newtio.c_cc[VTIME] = 0; /* inter character timer unused */
  234. #endif
  235.  
  236.     tcflush( fd, TCIFLUSH );
  237.     tcsetattr( fd, TCSANOW, &newtio );
  238.  
  239.     return fd;
  240. }
  241.  
  242. int baud_string_to_baud( char *baudstring, speed_t *baudrate )
  243. {
  244.     int i;
  245.  
  246.     for( i=0 ; i<sizeof(baudrates)/sizeof(BaudRate) ; i++ ) {
  247.         if(strncasecmp(baudrates[i].str,baudstring,baudrates[i].len )==0) {
  248.             /* found it */
  249.             *baudrate = baudrates[i].speed;
  250.             return 0;
  251.         }
  252.     }
  253.  
  254.     return -1;
  255. }
  256.  
  257. const char *baud_to_baud_string( speed_t baudrate )
  258. {
  259.     int i;
  260.  
  261.     for( i=0 ; i<sizeof(baudrates)/sizeof(BaudRate) ; i++ ) {
  262.         if( baudrates[i].speed == baudrate ) {
  263.             /* found it */
  264.             return baudrates[i].str;
  265.         }
  266.     }
  267.  
  268.     return NULL;
  269. }
  270.  
  271. void print_usage( void )
  272. {
  273.     int i;
  274.  
  275.     printf( "ttysniff %s (%s) - read and dump data from serial port.\n",
  276.             VERSION, __DATE__ );
  277.     printf( "usage: ttysniff [-options] [-l logfile] [-b baud] [-f flowcontrol] path\n" );
  278.     printf( "  -h                   show this help\n" );
  279.     printf( "  -p                   print data as readable (default is to print as hex)\n" );
  280.     printf( "  -l logfile           log traffic to binary file\n" );
  281.     printf( "  -b baudrate          set baud rate (default=%s)\n",
  282.             baud_to_baud_string(DEFAULT_BAUD_RATE) );
  283.     printf( "  -7                   7 data bits\n" );
  284.     printf( "  -8                   8 data bits (default)\n" );
  285.     printf( "  -f [hard|soft]       enable hardware (RTS/CTS) or software (XON/XOFF) flow control\n" );
  286.     printf( "                       (default is no flow control)\n" );
  287.     printf( "  --strip-high-bit     remove high bit (1<<8) from chars before printing\n" );
  288.     printf( "                       (doesn't effect logging to file or printing as hex)\n" );
  289.     printf( "  --bare-cr            don't send CRLF when reading CR from stdin\n" );
  290.     printf( "                       (default is to send CRLF when reading CR on input)\n" );
  291.     printf( "  path                 path to serial port (e.g., /dev/ttyS0)\n" );
  292.     printf( "\n" );
  293.     printf( "Available baud rates are: " );
  294.  
  295.     for( i=0 ; i<sizeof(baudrates)/sizeof(BaudRate) ; i++ ) {
  296.         printf( "%s ", baudrates[i].str );
  297.     }
  298.     printf( "\n" );
  299. }
  300.  
  301. static int str_match( const char *s1, const char *s2, size_t maxlen )
  302. {
  303.     /* Strings must exactly match and must be exactly maxlen. The maxlen+1
  304.      * finds strings longer than maxlen
  305.      */
  306.     if( strnlen(s1,maxlen+1)<=maxlen &&
  307.         strnlen(s2,maxlen+1)<=maxlen &&
  308.         strncmp(s1,s2,maxlen)==0 ){
  309.         return TRUE;
  310.     }
  311.     return FALSE;
  312. }
  313.  
  314. static int parse_long_option( const char *long_opt_name,
  315.                               const char *long_opt_value )
  316. {
  317.     if( str_match( long_opt_name, "strip-high-bit", 14 ) ) {
  318.         opt_strip_high_bit = TRUE;
  319.     }
  320.     else if( str_match( long_opt_name, "bare-cr", 8 ) ) {
  321.         /* don't send a LF to remote side when read CR from stdin */
  322.         opt_bare_cr = TRUE;
  323.     }
  324.  
  325.     return 0;
  326. }
  327.  
  328. int parse_args( int argc, char *argv[] )
  329. {
  330.     int retcode;
  331.     char c;
  332.     static struct option long_options[] = {
  333.         { "strip-high-bit", 0, 0, 0 },   /* strip high bit (c&0x7f) */
  334.         { "bare-cr", 0, 0, 0 }, /* don't send extra LF on reading CR from stdin */
  335.         { 0, 0, 0, 0 },
  336.     };
  337.     int long_index;
  338.  
  339.     if( argc == 1 ) {
  340.         print_usage();
  341.         exit(1);
  342.     }
  343.  
  344.     baudrate = DEFAULT_BAUD_RATE;
  345.  
  346.     while( 1 ) {
  347.         c = getopt_long( argc, argv, "hpl:b:78f:", long_options, &long_index );
  348.  
  349.         if( c==-1 )
  350.             break;
  351.  
  352.         switch( c ) {
  353.             case 0 :
  354.                 /* handle long option */
  355.                 retcode = parse_long_option( long_options[long_index].name, optarg );
  356.                 if( retcode != 0 ) {
  357.                     exit(1);
  358.                 }
  359.                 break;
  360.  
  361.             case 'h' :
  362.                 print_usage();
  363.                 exit(1);
  364.                 break;
  365.  
  366.             case 'l' :
  367.                 /* save output file name */
  368.                 strncpy( logfile_name, optarg, FILENAME_MAX );
  369.                 opt_log_output = TRUE;
  370.                 break;
  371.  
  372.             case 'p' :
  373.                 opt_hex_output = FALSE;
  374.                 break;
  375.  
  376.             case 'b' :
  377.                 /* convert baud rate string to baud rate number */
  378.                 if( baud_string_to_baud( optarg, &baudrate ) != 0 ) {
  379.                     fprintf( stderr, "Invalid baud rate \"%s\"\n", optarg );
  380.                     exit(1);
  381.                 }
  382.                 break;
  383.  
  384.             case '7' :
  385.                 /* 7 data bits */
  386.                 databits = SERIAL_7_DATA_BITS;
  387.                 break;
  388.  
  389.             case '8' :
  390.                 /* 8 data bits */
  391.                 databits = SERIAL_8_DATA_BITS;
  392.                 break;
  393.  
  394.             case 'f' :
  395.                 if( strncmp( optarg, "hard", 4 ) == 0 ) {
  396.                     flow_control = FLOW_CONTROL_HARDWARE;
  397.                 }
  398.                 else if( strncmp( optarg, "soft", 4 ) == 0 ) {
  399.                     flow_control = FLOW_CONTROL_SOFTWARE;
  400.                 }
  401.                 else {
  402.                     fprintf( stderr, "Invalid flow control \"%s\"\n", optarg );
  403.                     exit(1);
  404.                 }
  405.                 break;
  406.  
  407.             case '?' :
  408.                 print_usage();
  409.                 exit(1);
  410.                 break;
  411.  
  412.             default :
  413.                 break;
  414.         }
  415.     }
  416.  
  417.    
  418.     /* get serial port name */
  419.     if (optind < argc) {
  420.         strncpy( serial_port, argv[optind++], FILENAME_MAX );
  421.     }
  422.     else {
  423.         fprintf( stderr, "You must specify a serial port.\n" );
  424.         print_usage();
  425.         exit(1);
  426.     }
  427.  
  428.     /* XXX temp debug */
  429. //    fprintf( stderr, "baud=%ld port=%s\n", baudrate, serial_port );
  430.  
  431.     return 0;
  432. }
  433.  
  434. int
  435. terminal_to_raw( struct termios *save_tios )
  436. {
  437.     struct termios newtios;
  438.     int stdin_fileno;
  439.     int retcode;
  440.  
  441.     stdin_fileno = fileno(stdin);
  442.  
  443.     /* get a copy well save until we restore stdin at exit */
  444.     retcode = tcgetattr( stdin_fileno, save_tios );
  445.     if( retcode < 0 ) {
  446.         fprintf( stderr, "tcgetattr() failed to get stdin term attributes: %d %s\n",
  447.                 errno, strerror(errno));
  448.         return -1;
  449.     }
  450.  
  451.     /* get them again so we can muck with them */
  452.     retcode = tcgetattr( stdin_fileno, &newtios );
  453.     if( retcode < 0 ) {
  454.         fprintf( stderr, "tcgetattr() failed to get stdin term attributes: %d %s\n",
  455.                 errno, strerror(errno));
  456.         return -1;
  457.     }
  458.  
  459.     cfmakeraw( &newtios );
  460.  
  461.     /* set stdin to raw */
  462.     retcode = tcsetattr( stdin_fileno, TCSANOW, &newtios );
  463.     if( retcode < 0 ) {
  464.         fprintf( stderr, "tcsetattr() failed to set stdin term attributes: %d %s\n",
  465.                 errno, strerror(errno));
  466.         return -1;
  467.     }
  468.  
  469.     return 0;
  470. }
  471.  
  472. int main( int argc, char *argv[] )
  473. {
  474.     int err;
  475.     FILE *binfile=NULL;
  476.     int stdin_fileno, fd;
  477.     unsigned char databyte;
  478.     int count;
  479.     fd_set read_fds;
  480.     int max_fd;
  481.     struct termios stdin_tios;
  482.    
  483.     parse_args( argc, argv );
  484.  
  485.     init_signals();
  486.  
  487.     if( opt_log_output ) {
  488.         binfile = fopen( logfile_name, "a" );
  489.         if( binfile == NULL ) {
  490.             fprintf( stderr, "Unable to open log file \"%s\" : %s\n",
  491.                     logfile_name, strerror(errno) );
  492.             exit(1);
  493.         }
  494.         setbuf( binfile, NULL );
  495.     }
  496.  
  497.     fd = serial_open_port();
  498.     if( fd < 0 ) {
  499.         /* serial_open_port() displays error */
  500.         exit(1);
  501.     }
  502.  
  503.     /* set the terminal to raw so we can get single keystrokes */
  504.     fprintf( stderr, "setting stdin to raw...\n" );
  505.     memset( &stdin_tios, 0, sizeof(struct termios) );
  506.     terminal_to_raw( &stdin_tios );
  507.  
  508.     /* davep 20-July-06 ; XXX TEMPORARY FOR DEBUGGING kick off some activity */
  509. //    write( fd, "scan var print\r\n", 16 );
  510.  
  511.     setbuf( stdout, NULL );
  512.     count = 0;
  513.     stdin_fileno = fileno(stdin);
  514.     while( !main_quit ) {
  515.  
  516. //        fprintf( stderr, "stdin=%d\n", fileno(stdin));
  517.  
  518.         FD_ZERO( &read_fds );
  519.         FD_SET( stdin_fileno, &read_fds );
  520.         FD_SET( fd, &read_fds );
  521.  
  522.         max_fd = stdin_fileno;
  523.         if( fd > max_fd ) {
  524.             max_fd = fd;
  525.         }
  526.  
  527.         err = select( max_fd+1, &read_fds, NULL, NULL, NULL );
  528.         if( err<0 ) {
  529.             fprintf( stderr, "select err=%d errno=%d (%s)\r\n", err, errno,
  530.                     strerror(errno) );
  531.             continue;
  532.         }
  533.        
  534.         if( FD_ISSET( stdin_fileno, &read_fds ) ) {
  535.             err = read( stdin_fileno, &databyte, 1 );
  536.             if( err <= 0 ) {
  537.                 fprintf( stderr, "read stdin err=%d errno=%d (%s)\r\n",
  538.                         err, errno, strerror(errno) );
  539.                 break;
  540.             }
  541.  
  542.             if( databyte == 0x03 ) {
  543.                 /* Ctrl-C? */
  544.                 break;
  545.             }
  546.  
  547.             write( fd, &databyte, 1 );
  548.             if( databyte==CR && !opt_bare_cr ) {
  549.                 write( fd, &LF, 1 );
  550.             }
  551.         }
  552.  
  553.         if( FD_ISSET( fd, &read_fds ) ) {
  554.             err = read( fd, &databyte, 1 );
  555.             if( err <= 0 ) {
  556.                 fprintf( stderr, "read fd err=%d errno=%d (%s)\r\n",
  557.                         err, errno, strerror(errno) );
  558.                 break;
  559.             }
  560.  
  561.             if( opt_hex_output ) {
  562.                 /* There is something weird in printf().  I want to see 0x00 and it
  563.                  * prints "0" instead.  Messes up my pretty screen alignment.
  564.                  */
  565.                 if( databyte ) {
  566. #ifdef PRINT_0X
  567.                     printf( "%#04x ", databyte );
  568.                 }
  569.                 else {
  570.                     printf( "0x00 " );
  571.                 }
  572. #else
  573.                     printf( "%02x ", databyte );
  574.                 }
  575.                 else {
  576.                     printf( "00 " );
  577.                 }
  578. #endif
  579.             }
  580.             else {
  581.                 /* buyer beware -- just print whatever we get */
  582.  
  583.                 /* added some simple "filtering"; TODO add regex filtering so
  584.                  * really annoying stuff never hits human eyeballs
  585.                  */
  586.                 int printit = 1;
  587.  
  588.                 /* davep 10-May-2012 ; bell pissing me off */
  589.                 if( databyte==0x07 ) {
  590.                     printit = 0;
  591.                 }
  592.  
  593.                 /* davep 19-aug-2011 ; found a platform that uses \n\r not \r\n
  594.                  * (buh?) so filter out the 0x0d
  595.                  */
  596.                 if( databyte==0x0a ) {
  597.                     printf( "%c", 0x0d );
  598.                 }
  599.                 if( databyte==0x0d ) {
  600.                     printit = 0;
  601.                 }
  602.  
  603.                 /* davep 27-Oct-2011 ; quiet down people doing obnoxious stuff like:
  604.                  *
  605.                  *  ******** color_pipe_set_config ENTRY config: 0x5fae64
  606.                  *
  607.                  */
  608. //                if( databyte == '*' ) {
  609. //                    printit = 0;
  610. //                }
  611.  
  612.                 /* davep 01-Aug-2010 ; I did the 0x7f a while ago (strips the
  613.                  * 8th bit) because our newer hardware has issues and the 8th
  614.                  * bit flips up and down randomly.
  615.                  */
  616.                 if( printit ) {
  617.                     if( opt_strip_high_bit ) {
  618.                         printf( "%c", databyte&0x7f );
  619.                     }
  620.                     else {
  621.                         printf( "%c", databyte );
  622.                     }
  623.                 }
  624.  
  625.             }
  626.  
  627.             if( opt_log_output ) {
  628.                 fwrite( &databyte, 1, 1, binfile );
  629.                 fflush( binfile );
  630.             }
  631.         }
  632.  
  633.         count++;
  634.     }
  635.  
  636.     close( fd );
  637.    
  638.     if( opt_log_output ) {
  639.         fclose( binfile );
  640.     }
  641.  
  642.     /* set stdin back to starting point */  
  643.     fprintf( stderr, "restoring stdin to previous state\r\n" );
  644.     err = tcsetattr( stdin_fileno, TCSANOW, &stdin_tios );
  645.     if( err < 0 ) {
  646.         fprintf( stderr, "tcsetattr() failed to set stdin term attributes: %d %s\n",
  647.                 errno, strerror(errno));
  648.         /* keep going and hope for the best */
  649.     }
  650.  
  651.     printf( "\nbye!\n" );
  652.     return 0;
  653. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top