3 * Copyright (C) 2001 Yan "Warrior" Gurtovoy (ymg@azstarnet.com)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 printf("\n\nSerial Line Sniffer. Version %s\n", VERSION);
30 printf("\tCopyright (C) 2001 Yan \"Warrior\" Gurtovoy (ymg@azstarnet.com)\n\n");
35 printf("Usage: slsnif [options] <port>\n\n");
36 printf("REQUIRED PARAMETERS:\n");
37 printf(" <port> - serial port to use (i.e /dev/ttyS0, /dev/ttyS1, etc.)\n\n");
39 printf(" -h (--help) - displays this help.\n");
40 printf(" -b (--bytes) - print number of bytes transmitted on every read.\n");
41 printf(" -n (--nolock) - don't try to lock the port.\n");
42 printf(" -t (--timestamp) - print timestamp for every transmission.\n");
43 printf(" -l (--log) <logfile> - file to store output in, defaults to stdout.\n");
44 printf(" -s (--speed) <speed> - baudrate to use, defaults to 9600 baud.\n");
45 printf(" -p (--port2) <port2> - serial port to use instead of pty.\n");
46 printf(" --color <color> - color to use for normal output.\n");
47 printf(" --timecolor <color> - color to use for timestamp.\n");
48 printf(" --bytescolor <color> - color to use for number of bytes transmitted.\n\n");
49 printf("Following names are valid colors:\n");
50 printf(" \tblack, red, green, yellow, blue, magenta, cyan, white,\n");
51 printf("\tbrightblack,brightred, brightgreen, brightyellow,\n");
52 printf("\tbrightblue, brightmagenta, brightcyan, brightwhite\n\n");
53 printf("Example: slsnif -l log.txt -s 2400 /dev/ttyS1\n\n");
56 void fatalError(char *msg) {
61 int setRaw(int fd, struct termios *ttystate_orig) {
62 /* set tty into raw mode */
64 struct termios tty_state;
66 if (tcgetattr(fd, &tty_state) < 0) return 0;
67 /* save off original settings */
68 *ttystate_orig = tty_state;
70 tty_state.c_lflag &= ~(ICANON | IEXTEN | ISIG | ECHO);
71 tty_state.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON | BRKINT);
72 tty_state.c_oflag &= ~OPOST;
73 tty_state.c_cflag |= CS8;
74 tty_state.c_cc[VMIN] = 1;
75 tty_state.c_cc[VTIME] = 0;
76 cfsetispeed(&tty_state, tty_data.baudrate);
77 cfsetospeed(&tty_state, tty_data.baudrate);
78 if (tcsetattr(fd, TCSAFLUSH, &tty_state) < 0) return 0;
82 void fmtData(unsigned char *in, char *out, int in_size) {
88 /* flush output buffer */
90 for (i = 0; i < in_size; i++) {
92 /* it's a DEL character */
93 sprintf(charbuf, "%s (%03i) ", DEL, 127);
96 /* it's a control character or space */
97 sprintf(charbuf, "%s (%03i) ", ascii_chars[(int) in[i]], in[i]);
99 /* it's a printable character */
100 sprintf(charbuf, "%c (%03i) ", in[i], in[i]);
102 /* put formatted data into output buffer */
103 strcat(out, charbuf);
106 void setColor(int out, char *color) {
107 /* changes color on the terminal */
110 write(out, color, 7);
112 write(out, colors[WHITE].color, 7);
116 void writeData(int in, int out, int aux, int mode) {
117 /* reads data from `in`, formats it, writes it to `out` and `aux`.
118 * mode 0 - read from pipe
119 * mode 1 - read from port
120 * mode 2 - read from pty
123 unsigned char buffer[BUFFSIZE];
124 char outbuf[BUFFSIZE * 16 + 1];
126 #ifdef HAVE_SYS_TIMEB_H
136 if ((n = read(in, buffer, BUFFSIZE)) < 0) {
141 perror(mode == 1 ? RPORTFAIL : RPTYFAIL);
147 write(out, buffer, n);
148 write(aux, buffer, n);
150 /* print timestamp if necessary */
151 if (tty_data.tstamp) {
152 if (out == STDOUT_FILENO) setColor(out, tty_data.tclr);
153 write(out, "\n\n", 2);
154 #ifdef HAVE_SYS_TIMEB_H
156 tmp[0] = tmp1[0] = tbuf[0] = 0;
157 strncat(tmp, ctime(&(tstamp.time)), 24);
158 strncat(tbuf, tmp, 19);
159 sprintf(tmp1, ".%2ui", tstamp.millitm);
160 strncat(tbuf, tmp1, 3);
161 strcat(tbuf, tmp + 19);
162 write(out, tbuf, 28);
166 write(out, ctime(&tstamp), 24);
172 if (out == STDOUT_FILENO) setColor(out, tty_data.clr);
174 write(out, aux ? PORT_IN : PORT_OUT, PRFXSIZE);
176 fmtData(buffer, outbuf, n);
178 write(out, outbuf, strlen(outbuf));
179 /* print total number of bytes if necessary */
180 if (tty_data.dspbytes) {
182 sprintf(buffer, "\n%s %i", TOTALBYTES, n);
183 if (out == STDOUT_FILENO) setColor(out, tty_data.bclr);
184 write (out, buffer, strlen(buffer));
192 /* get data drom pipes */
197 maxfd = max(tty_data.ptypipefd[0], tty_data.portpipefd[0]);
200 FD_SET(tty_data.ptypipefd[0], &read_set);
201 FD_SET(tty_data.portpipefd[0], &read_set);
202 if (select(maxfd + 1, &read_set, NULL, NULL, NULL) < 0) {
206 if (FD_ISSET(tty_data.ptypipefd[0], &read_set))
207 writeData(tty_data.ptypipefd[0], tty_data.logfd, 0, 0);
209 if (FD_ISSET(tty_data.portpipefd[0], &read_set))
210 writeData(tty_data.portpipefd[0], tty_data.logfd, 1, 0);
215 /* close all opened file descriptors */
216 /* unlock the port(s) if necessary */
217 if (!tty_data.nolock) {
218 if (tty_data.portName && tty_data.portName[0])
219 dev_unlock(tty_data.portName);
220 /* this pointer should be NULL if pty is used */
221 if (tty_data.ptyName) dev_unlock(tty_data.ptyName);
224 if (tty_data.logfd == STDOUT_FILENO)
225 setColor(tty_data.logfd, colors[WHITE].color);
226 /* restore settings on pty */
228 tcsetattr(tty_data.ptyfd, TCSAFLUSH, &tty_data.ptystate_orig);
230 if (tty_data.ptyfd >= 0) close(tty_data.ptyfd);
231 /* restore settings on port */
232 if (tty_data.portraw)
233 tcsetattr(tty_data.portfd, TCSAFLUSH, &tty_data.portstate_orig);
235 if (tty_data.portfd >= 0) close(tty_data.portfd);
237 write(tty_data.logfd, "\n", 1);
238 if (tty_data.logfd != STDOUT_FILENO && tty_data.logfd >= 0)
239 if ((close(tty_data.logfd)) < 0) perror(CLOSEFAIL);
240 /* close write pipes */
241 if (tty_data.ptypipefd[1] >= 0) close(tty_data.ptypipefd[1]);
242 if (tty_data.portpipefd[1] >= 0) close(tty_data.portpipefd[1]);
243 /* free allocated memory for portName */
244 if (tty_data.portName) free(tty_data.portName);
245 if (pid >= 0) kill(pid, SIGINT);
248 RETSIGTYPE sigintP(int sig) {
249 /*parent signal handler for SIGINT */
254 RETSIGTYPE sigintC(int sig) {
255 /* child signal handler for SIGINT */
256 /* close read pipes */
257 if (tty_data.ptypipefd[0] >= 0) close(tty_data.ptypipefd[0]);
258 if (tty_data.portpipefd[0] >= 0) close(tty_data.portpipefd[0]);
262 RETSIGTYPE sigchldP(int sig) {
263 /* signal handler for SIGCHLD */
270 char *getColor(char *name) {
271 /* returns escape sequence that corresponds to a given color name */
275 while (colors[i].name && strcmp(name, colors[i].name)) i++;
276 if (colors[i].name) return colors[i].color;
280 int main(int argc, char *argv[]) {
282 int i, j, maxfd, optret, needsync = 1;
283 char *logName = NULL, baudstr[7], *ptr1, *ptr2;
284 struct termios tty_state;
287 #ifdef HAVE_GETOPT_LONG
288 struct option longopts[] = {
289 {"help", 0, NULL, 'h'},
290 {"log", 1, NULL, 'l'},
291 {"nolock", 0, NULL, 'n'},
292 {"port2", 1, NULL, 'p'},
293 {"speed", 1, NULL, 's'},
294 {"bytes", 0, NULL, 'b'},
295 {"timestamp", 0, NULL, 't'},
296 {"color", 1, NULL, 'x'},
297 {"timecolor", 1, NULL, 'y'},
298 {"bytescolor", 1, NULL, 'z'},
302 /* initialize variables */
304 tty_data.portName = tty_data.ptyName = NULL;
305 tty_data.ptyfd = tty_data.portfd = tty_data.logfd = -1;
306 tty_data.ptyraw = tty_data.portraw = tty_data.nolock = FALSE;
307 tty_data.dspbytes = tty_data.tstamp = FALSE;
308 tty_data.ptypipefd[0] = tty_data.ptypipefd[1] = -1;
309 tty_data.portpipefd[0] = tty_data.portpipefd[1] = -1;
310 tty_data.baudrate = DEFBAUDRATE;
311 tty_data.clr[0] = tty_data.bclr[0] = tty_data.tclr[0] = 0;
314 /* activate signal handlers */
315 signal(SIGINT, sigintP);
316 signal(SIGCHLD, sigchldP);
317 /* register closeAll() function to be called on normal termination */
319 /* process command line arguments */
320 #ifdef HAVE_GETOPT_LONG
321 while ((optret = getopt_long(argc, argv, OPTSTR, longopts, NULL)) != -1) {
323 while ((optret = getopt(argc, argv, OPTSTR)) != -1) {
327 tty_data.dspbytes = TRUE;
330 tty_data.tstamp = TRUE;
333 tty_data.nolock = TRUE;
336 logName = (optarg[0] == '=' ? optarg + 1 : optarg);
340 while (baudrates[i].spdstr &&
341 strcmp(optarg, baudrates[i].spdstr)) i++;
342 if (baudrates[i].spdstr) {
343 tty_data.baudrate = baudrates[i].speed;
344 strcat(baudstr, baudrates[i].spdstr);
348 tty_data.ptyName = (optarg[0] == '=' ? optarg + 1 : optarg);
351 ptr1 = getColor(optarg);
354 strcat(tty_data.clr, ptr1);
358 ptr1 = getColor(optarg);
360 tty_data.tclr[0] = 0;
361 strcat(tty_data.tclr, ptr1);
365 ptr1 = getColor(optarg);
367 tty_data.bclr[0] = 0;
368 strcat(tty_data.bclr, ptr1);
375 tty_data.ptyName = NULL;
380 if (!(tty_data.portName = malloc(PORTNAMELEN))) fatalError(MEMFAIL);
382 ptr2 = tty_data.portName;
383 while((*ptr1 == '/' || isalnum(*ptr1))
384 && ptr2 - tty_data.portName < PORTNAMELEN - 1) *ptr2++ = *ptr1++;
387 if (!tty_data.portName || !tty_data.portName[0]) {
389 tty_data.ptyName = NULL;
395 tty_data.logfd = STDOUT_FILENO;
397 if((tty_data.logfd = open(logName,
398 O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) < 0) {
400 tty_data.logfd = STDOUT_FILENO;
402 printf("Started logging data into file '%s'.\n", logName);
406 if (pipe(tty_data.ptypipefd) < 0 || pipe(tty_data.portpipefd) < 0) {
410 /* fork child process */
411 switch (pid = fork()) {
414 /* close write pipe */
415 close(tty_data.ptypipefd[1]);
416 close(tty_data.portpipefd[1]);
417 signal(SIGINT, sigintC);
418 pipeReader(&tty_data);
426 /* close read pipe */
427 close(tty_data.ptypipefd[0]);
428 close(tty_data.portpipefd[0]);
431 if (!tty_data.ptyName) {
432 /* open master side of the pty */
433 /* Search for a free pty */
434 if (!(tty_data.ptyName = strdup(DEFPTRNAME))) fatalError(MEMFAIL);
435 for(i = 0; i < 16 && tty_data.ptyfd < 0; i++) {
436 tty_data.ptyName[8] = "pqrstuvwxyzPQRST"[i];
437 for(j = 0; j < 16 && tty_data.ptyfd < 0; j++) {
438 tty_data.ptyName[9] = "0123456789abcdef"[j];
439 /* try to open master side */
440 tty_data.ptyfd = open(tty_data.ptyName, O_RDWR|O_NONBLOCK|O_NOCTTY);
443 if (tty_data.ptyfd < 0) {
444 /* failed to find an available pty */
445 free(tty_data.ptyName);
446 /*set pointer to NULL as it will be checked in closeAll() */
447 tty_data.ptyName = NULL;
451 /* create the name of the slave pty */
452 tty_data.ptyName[5] = 't';
453 printf("Opened pty: %s\n", tty_data.ptyName);
454 free(tty_data.ptyName);
455 /*set pointer to NULL as it will be checked in closeAll() */
456 tty_data.ptyName = NULL;
458 /* open port2 instead of pty */
460 if (!tty_data.nolock && dev_setlock(tty_data.ptyName) == -1) {
461 /* couldn't lock the device */
464 /* try to open port2 */
465 if ((tty_data.ptyfd = open(tty_data.ptyName, O_RDWR|O_NONBLOCK)) < 0) {
469 printf("Opened port: %s\n", tty_data.ptyName);
471 /* set raw mode on pty */
472 if(!setRaw(tty_data.ptyfd, &tty_data.ptystate_orig)) {
476 tty_data.ptyraw = TRUE;
478 if (!tty_data.nolock && dev_setlock(tty_data.portName) == -1) {
479 /* couldn't lock the device */
482 /* try to open port */
483 if ((tty_data.portfd = open(tty_data.portName, O_RDWR|O_NONBLOCK)) < 0) {
487 printf("Opened port: %s\n", tty_data.portName);
488 /* set raw mode on port */
489 if (!setRaw(tty_data.portfd, &tty_data.portstate_orig)) {
493 tty_data.portraw = TRUE;
494 printf("Baudrate is set to %s baud%s.\n",
495 baudstr[0] ? baudstr : "9600",
496 baudstr[0] ? "" : " (default)");
497 /* start listening to the slave and port */
498 maxfd = max(tty_data.ptyfd, tty_data.portfd);
501 FD_SET(tty_data.ptyfd, &rset);
502 FD_SET(tty_data.portfd, &rset);
503 if (select(maxfd + 1, &rset, NULL, NULL, NULL) < 0) {
507 if (FD_ISSET(tty_data.portfd, &rset)) {
508 /* data coming from device */
509 writeData(tty_data.portfd, tty_data.ptyfd, tty_data.portpipefd[1], 1);
511 if (FD_ISSET(tty_data.ptyfd, &rset)) {
512 /* data coming from host */
514 printf("Syncronizing ports...");
515 if (tcgetattr(tty_data.ptyfd, &tty_state) ||
516 tcsetattr(tty_data.portfd, TCSANOW, &tty_state)) {
523 writeData(tty_data.ptyfd, tty_data.portfd, tty_data.ptypipefd[1], 2);