/* -------------------------------------------------------------------- */ /* SMS Client, send messages to mobile phones and pagers */ /* */ /* terminal_io.c */ /* */ /* Copyright (C) 1999 Angelo Masci */ /* */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public */ /* License as published by the Free Software Foundation; either */ /* version 2 of the License, or (at your option) any later version. */ /* */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* Library General Public License for more details. */ /* */ /* You should have received a copy of the GNU Library General Public */ /* License along with this library; if not, write to the Free */ /* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* */ /* You can contact the author at this e-mail address: */ /* */ /* angelo@styx.demon.co.uk */ /* */ /* -------------------------------------------------------------------- */ /* $Id$ -------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "terminal_io.h" #include "logfile_io.h" /* -------------------------------------------------------------------- */ #define FD_MAX(a,b) (((a)>(b))?(a):(b)) #define FD_MIN(a,b) (((a)<(b))?(a):(b)) struct termios current_term, original_term; /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ void resetterm(void) { /* Restore our old terminal settings */ tcsetattr(STDIN_FILENO, TCSANOW, &original_term); } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ void setterm(void) { if (isatty(STDIN_FILENO)) { /* If we're using a terminal for input */ /* we want it to be unbuffered. */ tcgetattr(STDIN_FILENO, ¤t_term); tcgetattr(STDIN_FILENO, &original_term); #if 0 current_term.c_lflag &= ~(ICANON | ECHO); #else current_term.c_lflag &= ~(ICANON); #endif current_term.c_cc[VMIN] = 1; current_term.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, ¤t_term); atexit(resetterm); } } /* -------------------------------------------------------------------- */ /* */ /* Terminal Terminal_io() Socket /dev/pts/? */ /* Client Server */ /* ----- 1. --------------- 3. ----- */ /* | |<---------------| <------+<-- |<------------| | */ /* | | | | | | | */ /* | | 2. | | | 4. | | */ /* | |--------------->| -->+---)--> |------------>| | */ /* ----- -----|---|----- ----- */ /* 6. | | */ /* Terminal <---------------+ | */ /* Client | | */ /* V V */ /* ----- */ /* | */ /* | */ /* V 5. */ /* -------------- */ /* | | Logfile */ /* -------------- */ /* */ /* 1. Client_fdout */ /* 2. Client_fdin */ /* 3. Server_fdout */ /* 4. Server_fdin */ /* 5. Flog */ /* 5. Terminal_fdout */ /* */ /* -------------------------------------------------------------------- */ void terminal_io(int client_fdin, int client_fdout, int server_fdin, int server_fdout, int terminal_fdout) { int select_res, read_res, write_res, client_fdout_data, server_fdout_data, terminal_fdout_data, read_data; int nfds; fd_set readfds, writefds; char c; /* ------------------------------------------------------------ */ if (terminal_fdout == client_fdout) { /* The terminal is set to echo data, if we */ /* allow data to be sent to the terminal also */ /* we'll end up with data being echoed twice */ terminal_fdout = -1; } /* ------------------------------------------------------------ */ FD_ZERO(&readfds); FD_ZERO(&writefds); nfds = FD_MAX(terminal_fdout, FD_MAX(FD_MAX(client_fdin, client_fdout), FD_MAX(server_fdin, server_fdout))) +1; client_fdout_data = 0; server_fdout_data = 0; terminal_fdout_data = 0; read_data = 1; do { /* -------------------------------------------- */ /* Set up FS_SET's we're interested in */ /* reading data from and writing data to. */ FD_SET(client_fdin, &readfds); FD_SET(server_fdin, &readfds); if (client_fdout_data) { /* We have data we want to output */ FD_SET(client_fdout, &writefds); } else { /* No data to output */ FD_CLR(client_fdout, &writefds); } if (terminal_fdout != -1) { if (terminal_fdout_data) { /* We have data we want to output */ FD_SET(terminal_fdout, &writefds); } else { /* No data to output */ FD_CLR(terminal_fdout, &writefds); } } if (server_fdout_data) { /* We have data we want to output */ FD_SET(server_fdout, &writefds); } else { /* No data to output */ FD_CLR(server_fdout, &writefds); } /* ---------------------------------------- */ do { select_res = select(nfds, &readfds, &writefds, NULL, NULL); } while ((select_res == -1) && (errno == EINTR)); if (select_res == -1) { exit(-1); } else if (select_res > 0) { if ((FD_ISSET(client_fdin, &readfds)) || (FD_ISSET(client_fdout, &writefds)) || ((terminal_fdout != -1) && (FD_ISSET(terminal_fdout, &writefds)))|| (FD_ISSET(server_fdin, &readfds)) || (FD_ISSET(server_fdout, &writefds))) { /* We've been alerted of some data */ /* being present on the lines we're */ /* interested in. */ if (FD_ISSET(client_fdin, &readfds)) { /* Something for us to READ from 'client_fdin' */ if ((client_fdout_data == 0) && (terminal_fdout_data == 0) && (server_fdout_data == 0)) { /* There aren't any characters left unsent */ /* Read a character */ do { read_res = read(client_fdin, &c, 1); } while ((read_res == -1) && (errno == EINTR)); if (read_res == -1) { exit(-1); } else if (read_res == 0) { /* EOF at our end - client */ #if 0 /* look at this */ fprintf(stderr, "Client sent EOF.\n"); read_data = 0; #endif } else { /* Character read */ #if 0 if ((int)c == '\004') /* CTR-D for EOF */ { fprintf(stderr, "Client sent EOF.\n"); read_data = 0; } #endif } /* Send data to 'client_fdout' and 'server_fdout' */ log_client_send(); log_character(c); if (terminal_fdout != -1) { terminal_fdout_data = 1; } server_fdout_data = 1; } } if (FD_ISSET(client_fdout, &writefds)) { /* Something for us to WRITE to 'client_fdout' */ assert(client_fdout_data); do { write_res = write(client_fdout, &c, 1); } while ((write_res == -1) && (errno == EINTR)); if (write_res == -1) { exit(-1); } else if (write_res == 0) { exit(-1); } else { /* Character written */ } client_fdout_data = 0; } if ((terminal_fdout != -1) && (FD_ISSET(terminal_fdout, &writefds))) { /* Something for us to WRITE to 'terminal_fdout' */ assert(terminal_fdout_data); do { write_res = write(terminal_fdout, &c, 1); } while ((write_res == -1) && (errno == EINTR)); if (write_res == -1) { exit(-1); } else if (write_res == 0) { exit(-1); } else { /* Character written */ } terminal_fdout_data = 0; } if (FD_ISSET(server_fdin, &readfds)) { /* Something for us to READ from 'server_fdin' */ if ((client_fdout_data == 0) && (terminal_fdout_data == 0) && (server_fdout_data == 0)) { /* There aren't any characters left unsent */ /* Read a character */ do { read_res = read(server_fdin, &c, 1); } while ((read_res == -1) && (errno == EINTR)); if (read_res == -1) { exit(-1); } else if (read_res == 0) { /* EOF at their end - server */ fprintf(stderr, "Server sent EOF.\n"); read_data = 0; } else { /* Character read */ } /* Send data to 'client_fdout' */ log_server_send(); log_character(c); if (terminal_fdout != -1) { terminal_fdout_data = 1; } client_fdout_data = 1; } } if (FD_ISSET(server_fdout, &writefds)) { /* Something for us to WRITE to 'server_fdout' */ assert(server_fdout_data); do { write_res = write(server_fdout, &c, 1); } while ((write_res == -1) && (errno == EINTR)); if (write_res == -1) { exit(-1); } else if (write_res == 0) { exit(-1); } else { /* Character written */ } server_fdout_data = 0; } } else { /* Something woke us and it wasn't */ /* 'client_fdin', 'client_fdout', */ /* 'server_fdin' or 'server_fdout' */ exit(-1); } } else { /* Timeout expired - we didn't set a timeout */ exit(-1); } } while (read_data); fprintf(stderr, "Connection terminated.\n"); }