gems-1.1.1/0000755000175000017500000000000011306774376011201 5ustar maxymaxygems-1.1.1/AUTHORS0000644000175000017500000000077210123160002012222 0ustar maxymaxygems 1.1 Credits ---------------- Coding ------ Diego Essaya Emiliano Castagnari (aka Torian) Beta testing ------------ Enrique Calot Spanish <--> English translation -------------------------------- Melisa Halsband (aka Sarah) Enrique Calot Diego Essaya Margarita Manterola Debian package -------------- Maximiliano Curia gems-1.1.1/client/0000755000175000017500000000000011306774376012457 5ustar maxymaxygems-1.1.1/client/source/0000755000175000017500000000000011306774376013757 5ustar maxymaxygems-1.1.1/client/source/Changelog0000644000175000017500000001014010123177501015544 0ustar maxymaxy2004/08/28 - Diego Essaya * Bugfix: #19 wrong priority for input processing. 2004/08/28 - Diego Essaya * Messages: Supressed the initialization message. Simplified the message output by adding a print_msg() function. 2004/08/09 - Diego Essaya * English translation: Translated everything into English. Added NLS (gettext) support. 2004/07/23 - Diego Essaya * New option: '-i' to ignore server terminal size. 2004/03/05 - Diego Essaya * Release: 1.0 2004/02/26 - Diego Essaya * New options: '-h' to print help; '-v' to print version. 2004/02/25 - Diego Essaya * Connection: Now the client detects if the server is saturated when connecting. 2004/02/23 - Diego Essaya * Code cleanup: t_client_data structure implemented. This one contains all the information relative to the client, such as the socket, options, etc. Function prototypes are now much smaller. 2004/02/22 - Diego Essaya * Makefile: Everything is now nicer :) . gcc's -I parameter is now used, and also make's VPATH variable; so it is no longer necessary to specify include directory in the sources. 2004/02/17 - Diego Essaya * Code cleanup: I know... I just can't help it :P This time I splitted the huge main() function into various smaller functions: init_client(), do_server_input(), finish_client(), etc. - Now the client emits an alarm when any key is pressed. 2004/02/16 - Diego Essaya * General code cleanup: Every c/h file was adapted to the specs in CodingStandards. I used the indent(1) tool to acomplish this. The only problem were the comments at the right of code, because indent uses tabs instead of spaces. Indentation was set to 4 spaces. * Makefile: make does not work well with symlinks. Fixed Makefile. * Signal handling: Code rewritten: - It is now a separate module: common/signal.c/h. This module is common to both server and client. - It is not necessary to use too many global variables; only one for each signal to be caught. These are declared in the signal module. - The program is more stable upon arrival of signals in an asynchronous way, as the signal handlers take a minimum amount of time to modify the appropriate variable. * Global variables: Given the enhanced signal handling code, the prototype of almost all functions were changed, so the only global variables used are the ones relative to signals. The only problem with this approach is that each function receives about 5 parameters, and prototypes are now endless :P. 2004/01/13 - Emiliano Castagnari Makefile improved: "all" target added. It creates 'bin/' directory if it is not found. Added links in client/source to files in common directory, so to not use relative paths. 2004/01/09 - Diego Essaya Protocol modified (see common/Changelog). The modified function is start_comm(). 2004/01/06 - Diego Essaya - Code cleanup. - Client disables "echoing" in its terminal. This way, characters received from keyboard are not written to the terminal. - It is possible to quit by pressing 'q'. - New option: alarm filtering. When active, client does not write '\a' characters that are received from server. This option is enabled/disabled by pressing 'a' at run-time. 2004/01/02 - Diego Essaya - Data is no longer received directly. The new protocol is now used instead. - Client now handles changes in terminal size, and disconnects from the server if local size is smaller than the remote terminal. 2004/01/01 - Diego Essaya Now the client receives server's version after sending its own. 2003/12/31 - Diego Essaya Started to implement communication protocol. As for now, the client sends its version at the beginning of the connection. The rest works exactly as before. 2003/12/29 - Diego Essaya * Release: 0.2.0 Code cleanup - removed gems-client.h. 2003/10/10 - Diego Essaya * Release: 0.1.0 Client now accepts server's hostname as well as IP. Port is now optional. Code cleanup. 2003/09/28 - Diego Essaya First client version uploaded, called gems-client. It is minimally functional: it connects to the server (given IP & port), and writes into stdout everything it receives. gems-1.1.1/client/source/gems-client.c0000644000175000017500000003174210123177501016320 0ustar maxymaxy/* ============================================================================ * << File: gems_client.c >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * * Description: * Every client-specific functions are implemented here. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* socket(), connect() */ #include /* inet_ntoa() */ #include /* write(), close(), ... */ #include /* atoi() */ #include /* fprintf() */ #include /* strncmp() */ #include /* gethostbyname() */ #include /* tcsetattr(), tcgetattr() */ #include /* Errors - errno variable */ #include /* setlocale() */ #include /* variable parameter functions */ #include "protocol.h" #include "common.h" #include "sighandlers.h" #define APPNAME PROJECT_NAME"-client" /* Accepted arguments: */ #define OPT_HELP "-h" #define OPT_VERSION "-v" #define OPT_IGNORE_WS "-i" /* Structure with client-relative information (sockets, file descriptors, * options, etc), used in many functions */ typedef struct t_client_data { struct termios *old_tty_settings; /* Old terminal configuration */ int socket_fd; /* Connection socket FD */ fd_set open_fds; /* List of open FDs, for select() */ int server_cols; /* Server's terminal size */ int server_rows; char alarm_enabled; /* 1 = Play alarm when received ('\a') */ char ignore_winsize; /* 1 = Ignore server's terminal size */ } t_client_data; /* Print a message to stderr, prepending the "appname: " string */ void print_msg(char *msg, ...) { static char string[255]; /* Container for the formatted message */ va_list va; va_start(va, msg); vsprintf(string, msg, va); va_end(va); fprintf(stderr, "%s: %s\n", APPNAME, string); } /* Disables local echoing and canonical input for stdin (in order to be able * to read one character at a time, instead of one line). * Returns the old terminal configuration, or NULL in case of error. */ struct termios *set_tty_settings() { struct termios new_settings; static struct termios old_settings; if (tcgetattr(0, &old_settings) == -1) { perror(APPNAME); return NULL; } new_settings = old_settings; /* Disable echoing */ new_settings.c_lflag &= (~ECHO); /* Disable canonical input, and set input buffer to 1 byte (1 char) */ new_settings.c_lflag &= (~ICANON); new_settings.c_cc[VTIME] = 0; new_settings.c_cc[VMIN] = 1; if (tcsetattr(0, TCSANOW, &new_settings) == -1) { perror(APPNAME); return NULL; } return &old_settings; } /* Converts server's domain (obtained from program arguments) into its IP * address. Returns the structure with this info, ready to be used in * connect(), or NULL in case of error. */ struct sockaddr_in *get_server_address(char *host, int tcp_port) { struct hostent *server; static struct sockaddr_in address; /* Get IP: */ if (!(server = gethostbyname(host)) || !(server->h_addr)) { print_msg(_("Host %s not found."), host); return NULL; } /* Type of IP: IPv4 */ address.sin_family = AF_INET; /* Port: */ address.sin_port = htons(tcp_port); /* IP address: */ address.sin_addr.s_addr = ((struct in_addr *) (server->h_addr))->s_addr; return &address; } /* Establish connection with server. Returns the new socket, or -1 in case * of error. */ int connect_with_server(char *ip_str, int port) { struct sockaddr_in *address = NULL; /* server's address */ int socket_fd = -1; /* Configure the struct address, to be passed to connect() */ if ((address = get_server_address(ip_str, port)) == NULL) return -1; /* Create the socket: */ if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror(APPNAME); return -1; } /* Connect to server: */ if (connect(socket_fd, (struct sockaddr *) address, sizeof(*address))) { perror(APPNAME); return -1; } return socket_fd; } /* Once the connection has been established, this function starts * communication with the server, following the protocol specifications. * Saves the server's terminal size in *server_cols and *server_rows. */ fbool_t start_comm(t_client_data *data) { char *version; fbool_t r; /* Server sends its version: */ if ((r = get_peer_version(data->socket_fd, &version)) != TRUE) { /* If the server sends DISCONNECT instead of its version, it means * that the connection cannot be established, because the server is * saturated. */ if (r == FALSE) print_msg(_("Server does not accept any more connections.")); return r; } /* Compare versions: */ if (strncmp(version, PROTOCOL_VERSION, strlen(PROTOCOL_VERSION))) { print_msg(_("Incompatible server version.")); if ((r = send_disconnect(data->socket_fd) != TRUE)) return r; return FALSE; } if ((r = send_ack(data->socket_fd) != TRUE)) return r; /* Now the client must send its version: */ if ((r = send_version(data->socket_fd)) != TRUE) return r; if ((r = get_ack(data->socket_fd)) != TRUE) { if (r == FALSE) print_msg( _("Connection refused by server - Incompatible versions.")); return r; } /* Server sends terminal size: */ if (get_winsize(data->socket_fd, &(data->server_cols), &(data->server_rows)) != TRUE) { /* Local size too small */ if (! data->ignore_winsize) { print_msg(_("Terminal size too small (minimum required: %dx%d)."), data->server_cols, data->server_rows); if ((r = send_disconnect(data->socket_fd) != TRUE)) return r; return FALSE; } } if ((r = send_ack(data->socket_fd) != TRUE)) return r; return TRUE; } /* Writes data received from server into stdout. * If data->alarm_enabled == 0, characteres '\a' are also filtered. */ void do_output(char *buffer, int len, t_client_data *data) { int i; if (data->alarm_enabled) write(STDOUT_FILENO, buffer, len); else { for (i = 0; i < len; i++) if (buffer[i] != '\a') write(STDOUT_FILENO, buffer + i, 1); } } /* Gets a complete message from the server, and processes it. * Returns EXIT_OK if the server closed the connection, and CONTINUE if no * errors occured. */ status_t do_server_input(t_client_data *data) { t_gems_msg msg; fbool_t r; r = get_msg(data->socket_fd, &msg); if (r == FALSE) { fputc('\n', stderr); print_msg(_("End of transmission.")); /* putchar('\r'); */ /* Just in case */ return EXIT_OK; } else if (r == FAIL) return EXIT_FAIL; switch (msg.type) { case GEMS_DATA: /* Data to be written to stdout */ do_output(msg.data, msg.len, data); break; case GEMS_WINSIZE: /* remote terminal size has changed */ get_cols_rows(&msg, &(data->server_cols), &(data->server_rows)); if (! data->ignore_winsize) { if (compare_winsize(data->server_cols, data->server_rows, APPNAME) != TRUE) return EXIT_FAIL; } break; default: /* unknown message type */ return EXIT_FAIL; } return CONTINUE; } /* Prints a short help message */ void print_help() { printf(_("Usage: %s [%s] HOST [PORT]\n"), APPNAME, OPT_IGNORE_WS); printf(" "APPNAME" "OPT_HELP" | "OPT_VERSION"\n"); } /* Prints complete help message. */ void long_help() { printf(_("Usage: %s [%s] HOST [PORT]\n"), APPNAME, OPT_IGNORE_WS); printf(" "APPNAME" "OPT_HELP" | "OPT_VERSION"\n"); putchar('\n'); printf(_("Arguments:\n")); printf(_(" HOST\t\tServer's IP address or domain.\n")); printf(_(" PORT\t\tServer's TCP port. Default: %d.\n"), DEFAULT_TCP_PORT); printf(_(" %s\t\tIgnore server's terminal size.\n"), OPT_IGNORE_WS); printf(_(" %s\t\tShow this help message.\n"), OPT_HELP); printf(_(" %s\t\tShow version information.\n"), OPT_VERSION); } /* Parses arguments received from command line. Saves server's domain and * port in *addr and *port. */ status_t parse_args(int argc, char *argv[], char **addr, int *port, t_client_data *data) { int i; char addr_received = FALSE; char port_received = FALSE; for (i = 1; i < argc; i++) { /* ¿Option '-h' received? */ if (!strcmp(argv[i], OPT_HELP)) { long_help(); return EXIT_OK; } /* ¿Option '-v' received? */ if (!strcmp(argv[i], OPT_VERSION)) { PRINT_VERSION(APPNAME) return EXIT_OK; } /* ¿Option '-i' received? */ if (!strcmp(argv[i], OPT_IGNORE_WS)) { data->ignore_winsize = TRUE; continue; } if (! addr_received) { *addr = argv[i]; addr_received = TRUE; continue; } if (! port_received) { *port = atoi(argv[2]); port_received = TRUE; continue; } /* Syntax error: */ print_help(); return EXIT_FAIL; } if (! addr_received) { /* Syntax error: */ print_help(); return EXIT_FAIL; } return CONTINUE; } /* Reads one char from stdin and makes the appropriate action. Returns EXIT_OK * if 'q' is received; CONTINUE otherwise. Beeps if the pressed key has no * action. */ status_t parse_key(t_client_data *data) { char c; const char alarm_char = '\a'; c = getchar(); if (c == 'q') { fprintf(stderr, _("\n%s terminated.\n"), APPNAME); return EXIT_OK; } if (c == 'a') data->alarm_enabled = !(data->alarm_enabled); else write(STDOUT_FILENO, &alarm_char, 1); return CONTINUE; } /* Initializes the client, including connection with server. */ status_t init_client(int argc, char **argv, t_client_data *data) { char *server_addr; /* Server's domain */ int server_port = DEFAULT_TCP_PORT; /* Server's port */ status_t r; /* Initialize client information that has not yet been specified, * in case of error (see finish_client() ) */ data->socket_fd = -1; data->old_tty_settings = NULL; data->alarm_enabled = TRUE; data->ignore_winsize = FALSE; /* Configure locale: */ setlocale(LC_ALL, ""); bindtextdomain("gems", LOCALEDIR); textdomain("gems"); /* Process received arguments: */ if ((r = parse_args(argc, argv, &server_addr, &server_port, data)) != CONTINUE) return r; /* Configure terminal: */ if (!(data->old_tty_settings = set_tty_settings())) return EXIT_FAIL; /* Initialize connection: */ if ((data->socket_fd = connect_with_server(server_addr, server_port)) == -1) return EXIT_FAIL; /* Start communication: */ if (start_comm(data) != TRUE) return EXIT_FAIL; /* Configure open_fds for calling select(). The fds to be checked are * stdin and the socket. */ FD_ZERO(&(data->open_fds)); FD_SET(STDIN_FILENO, &(data->open_fds)); FD_SET(data->socket_fd, &(data->open_fds)); /* Configure signal handlers: */ install_sighandlers(); /* Connection succeded: */ print_msg(_("Connection established -- Press 'q' to exit.")); return CONTINUE; } /* Makes all necessary steps to finish the program in a clean way. */ void finish_client(t_client_data *data) { /* Close socket: */ if (data->socket_fd > 0) close(data->socket_fd); /* Restore old terminal configuration: */ if (data->old_tty_settings) tcsetattr(0, TCSANOW, data->old_tty_settings); } int main(int argc, char *argv[]) { t_client_data data; /* Client data to be used in all functions */ fd_set rfds; /* temporary fd_set for select() */ status_t status; /* Client status */ status = init_client(argc, argv, &data); /* Main loop: */ for ( ; status == CONTINUE ; ) { /* Wait until data received, or signal raised: */ rfds = data.open_fds; if (select(data.socket_fd + 1, &rfds, NULL, NULL, NULL) == -1) if (errno != EINTR) status = EXIT_FAIL; /* error */ if (sigint_raised) status = EXIT_OK; /* CTRL-C pressed */ else if (winsize_changed) { /* Local terminal size changed: */ if (compare_winsize(data.server_cols, data.server_rows, APPNAME) != TRUE) status = EXIT_FAIL; winsize_changed = 0; } else if (FD_ISSET(STDIN_FILENO, &rfds)) /* Data received from stdin (keyboard) */ status = parse_key(&data); else if (FD_ISSET(data.socket_fd, &rfds)) /* Data received from server: */ status = do_server_input(&data); } finish_client(&data); return status; } /* vim: set ts=4 sw=4: */ gems-1.1.1/client/Makefile0000644000175000017500000000177611306773631014123 0ustar maxymaxySRCDIR = source/ BINDIR = bin/ COMDIR = ../common/ VPATH = $(COMDIR) $(SRCDIR) INCS = -I$(COMDIR) -I$(SRCDIR) PACKDEP = $(BINDIR)gems-client.o COMMONPACKDEP = $(COMDIR)protocol.o $(COMDIR)sighandlers.o CLIENTDEP = gems-client.c common.h version.h protocol.h sighandlers.h ################################################## .PHONY: all bindir remove clean install uninstall all: bindir $(BINDIR)gems-client bindir: @if [ ! -d "$(BINDIR)" ] ; then mkdir "$(BINDIR)"; fi $(BINDIR)gems-client : $(PACKDEP) $(COMMONPACKDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) -o $@ $^ $(BINDIR)gems-client.o : $(CLIENTDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) -c $< -o $@ $(COMDIR)protocol.o: protocol.c common.h version.h protocol.h cd $(COMDIR) && $(MAKE) $(COMDIR)sighandlers.o: ../common/sighandlers.c ../common/sighandlers.h cd $(COMDIR) && $(MAKE) remove clean: rm -f $(PACKDEP) $(BINDIR)gems-client install: $(BINDIR)gems-client install -m755 -D $< $(BINPATH)/gems-client uninstall: rm -fv $(BINPATH)/gems-client gems-1.1.1/server/0000755000175000017500000000000011306774376012507 5ustar maxymaxygems-1.1.1/server/source/0000755000175000017500000000000011306774376014007 5ustar maxymaxygems-1.1.1/server/source/gems-server.h0000644000175000017500000000714310123160003016370 0ustar maxymaxy/* ============================================================================ * << File: gems_server.h >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _GEMS_SERVER_H_ #define _GEMS_SERVER_H_ #include /* struct sockaddr_in */ #include "common.h" /* fbool_t */ #include "protocol.h" /* gems_msg_type_t */ #include "sock_info.h" /* t_clsock */ /* Server options: */ typedef struct t_options { t_ip ip; int port; char *flog; /* Archivo de log - file | Syslog */ int script; char *script_bin; int maxconn; int expected_connections; /* Cantidad de clientes a esperar */ } t_options; /* Server-related information: */ typedef struct t_server_data { int sock_server; /* Socket for accept()ing connections */ int input_fd; /* Data input fd */ fd_set open_fds; /* Array of open fds */ int fdmax; /* Maximum open file descriptor */ int clients; /* Number of connected clients */ struct t_clsock *client_list; /* List of connected clients */ struct t_waiting_client *waiting_client_list; /* List of clients awaiting connection */ } t_server_data; status_t init_sock_server(t_server_data *data, t_options *options); void close_sockets(t_server_data *data); fbool_t validate_client(int fd, int max_conn, t_server_data *data); fbool_t accept_new_client(t_server_data *data, int max_conn); struct t_clsock *add_client(int socket, struct sockaddr_in *client_data, t_server_data *data); void init_clsock(struct t_clsock *client_sock); void del_client(t_clsock *client, t_server_data *data); fbool_t send_to_all(gems_msg_type_t type, char *buffer, int n, t_server_data *data); fbool_t send_input_to_clients(t_server_data *data); void attend_client(t_clsock *client, t_server_data *data); void send_winsize_to_clients(t_server_data *data); void init_data(t_server_data *data, int input_fd, int expected_connections); status_t gems_server(int input_fd, t_options *options); fbool_t check_new_client(t_server_data *data, int expected_connections); void remove_waiting_client(t_server_data *data, t_waiting_client *wc); t_waiting_client *add_waiting_client(t_server_data *data, pid_t pid, int socket, struct sockaddr_in *addr_data); void kill_waiting_clients(t_server_data *data); char *create_lock(t_options *options); void release_lock(char *name); void check_winsize(); #endif /* vim: set ts=4 sw=4: */ gems-1.1.1/server/source/version_server.h0000644000175000017500000000313210114506232017206 0ustar maxymaxy/* ============================================================================ * << File: server_version.h >> * ---------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 23/01/04 * * Description: * Server name and version. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _SERVER_VERSION_H_ #define _SERVER_VERSION_H_ #include "version.h" #define APPNAME PROJECT_NAME"-server" #endif gems-1.1.1/server/source/defaults.h0000644000175000017500000000507010123177501015750 0ustar maxymaxy/* ============================================================================ * << File: defaults.h >> * ---------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * * Description: * Declarations of the default options for the server. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _DEFAULTS_H_ #define _DEFAULTS_H_ #include /* INADDR_ANY */ #define SERVER_DEF_IP INADDR_ANY /* Listen all the IPs */ /* Destination of logs. Possible values: * NULL -> don't log * "filename" -> append log to "filename" * "stderr" -> log to stderr * "syslog" -> log through syslogd */ #define SERVER_DEF_FLOG NULL #define SERVER_DEF_SCRIPT 1 /* Call script by default */ #define SERVER_DEF_SCRIPT_BIN "/usr/bin/script" #define SCRIPT_OPTIONS "-fq" /* See man script */ /* Lock filename prefix (followed by TCP port) */ #define LOCK_PREFIX "/var/lock/gems-server." /* Suggested maximum terminal size */ #define SUGGESTED_MAX_COLS 80 #define SUGGESTED_MAX_ROWS 25 /* Maximum simmultaneous client connections: */ #define SERVER_DEF_MAX_CONN 25 /* Number of client connections to wait before starting the transmission: */ #define SERVER_DEF_WAIT 0 /* FIXME: This info should be obtained from /etc/protocols; see * getprotoent(3) */ #define PROTO_TCP 6 /* TCP - No modificar */ #define PROTO_UDP 17 /* UDP - No modificar */ #endif gems-1.1.1/server/source/Changelog0000644000175000017500000001766710123177501015621 0ustar maxymaxy2004/08/29 - Diego Essaya * Terminal size: Server now warns when the terminal size is greater than 80x25. * Bugfixes: (Found with valgrind) - Wrong call to memcpy() in add_client() (Bug #15). - Invalid read after client has been deleted in tcp_server() (Bug #15). - Bug #16: Implemented lock creation/release. 2004/08/29 - Diego Essaya * Messages: Supressed the boring initialization messages. The only message that remained is "gems-server initialized.\n". 2004/07/27 - Diego Essaya * Bugfix: The server freezed if it received a connection with telnet. To solve this, now client connections are handled in child processes (one for each new connection), until they are accepted. When this child process terminates, the father adds the client to the connected clients list. 2004/07/23 - Diego Essaya * Protocol: The version used to compare at connection-time is PROTOCOL_VERSION instead of PROJECT_VERSION. This way, the connection will be refused only when protocol versions difer. 2004/02/29 - Diego Essaya * Logging: Server now does not emit logs by default. 2004/02/28 - Diego Essaya * main.c: Improved agrument parsing. * Logging: Improved log module. g_log() now takes one mandatory argument: errno. Also, now the messages are categorized by their priority (a la syslog). 2004/02/27 - Diego Essaya * tcp_server(): Now takes only two parameters: status_t tcp_server(int *input_fd, t_options *options); * FD_ADD() macro: Simplifies the process of adding a fd to data->open_fds. * New option: '-wait M'. Enables the server to wait for the connection of M clients before starting data transmission. 2004/02/26 - Diego Essaya * New options: '-h' to show a help message; '-v' to show version. 2004/02/25 - Diego Essaya * Connections: New option: '-maxconn N' to set the maximum number of simultaneous client connections. Default: 25. * script: New option: '-script_bin' to set the location of the script(1) binary. Default: "/usr/bin/script". * Log: '-log' option is now fully functional; it allows to specify the log destination. * Syslog: If '-log syslog' is used, the server logs through syslogd. * log.c: The log destination is now stored in a global variable. Thus, the g_log() function now takes one less parameter. g_set_logfile() allows setting the log destination. 2004/02/23 - Diego Essaya * gems-server.c/h / code cleanup: Implemented the t_server_data structure containing all the important server data. This way, the function prototypes are much shorter. 2004/02/22 - Diego Essaya * Makefile: Everything is now nicer :) . gcc's -I parameter is now used, and also make's VPATH variable; so it is no longer necessary to specify include directory in the sources. 2004/02/16 - Diego Essaya * General code cleanup: Every c/h file was adapted to the specs in CodingStandards. I used the indent(1) tool to acomplish this. The only problem were the comments at the right of code, because indent uses tabs instead of spaces. Indentation was set to 4 spaces. * Makefile: make does not work well with symlinks. Fixed Makefile. * Signal handling: Code rewritten: - It is now a separate module: common/signal.c/h. This module is common to both server and client. - It is not necessary to use too many global variables; only one for each signal to be caught. These are declared in the signal module. - The program is more stable upon arrival of signals in an asynchronous way, as the signal handlers take a minimum amount of time to modify the appropriate variable. * Global variables: Given the enhanced signal handling code, the prototype of almost all functions were changed, so the only global variables used are the ones relative to signals. The only problem with this approach is that each function receives about 5 parameters, and prototypes are now endless :P. * call_script.c/h: Functions for running script(1) as a child. * sock_info.h: Data types related to the client information (IP, port, etc). * log.c / g_log(): A '\n' is appended to the log, so it is not necessary to include it in the message. * add_client(): Bugfix in list handling. 2004/01/15 - Emiliano Castagnari * Logging: The logging function changes to g_log(). Logging can now be done to a file (the command-line argument is not implemented yet). Also, the logs are now written in a syslog fashion, with the date and stuff ;) . 2004/01/15 - Emiliano Castagnari * log.c / g_time(): Function that returns actual time. * gems-server.c / init_sock_server(): Starts listening in a specified port & IP. 2004/01/15 - Emiliano Castagnari * Change in ADT for clientes: Now the clients are stored in a doubly-linked list, along with their IP address, port, etc. 2004/01/13 - Emiliano Castagnari * Makefile: Makefile improved: "all" target added. It creates 'bin/' directory if it is not found. Added links in client/source to files in common directory, so to not use relative paths. 2004/01/09 - Diego Essaya * Protocol: With the changes in the protocol (see common/Changelog), the server now can produce more precise logs, when client connections are refused. These logs are not yet implemented. 2004/01/06 - Diego Essaya * Bugfix: Terminal size was not correctly obtained when running script. * Code cleanup. 2004/01/04 - Diego Essaya * script: Unless the -noscript parameter is used, the server runs "script -fq" automatically. This change is not permanent; we plan to add script's functionality to gems-server. 2004/01/02 - Diego Essaya * Protocol: Data is no longer received directly. The new protocol is now used instead. * Winsize: Implemented terminal size detection. 2004/01/01 - Diego Essaya * Server: Server code rewritten, now using select(2). Now the server "sleeps" until an event is detected (data input, client connection/disconnection, etc). * Version: Server now compares its version with the client's. This is to be improved yet, because the connection is refused if the versions are _different_. 2003/12/31 - Diego Essaya * Protocol: Started to implement a new communication protocol. As for now, the client sends its version at the beginning of the connection. The rest works exactly as before. 2003/12/29 - Diego Essaya * Release: 0.2.0 * Code cleanup: Created common.c/h, with declarations common to server and client. * Bugfix: "address already in use". Now the server can be run immediately after closing it. 2003/10/12 - Diego essaya * Release: 0.1.0 * Bugfix: If stdin received EOF, or if server was interrupted by CTRL-C, the sockets were not closed. Both cases are now handled properly. To this avail, the server_sock and remote_sock[] variables are now global, with names server_sock and client_sock[]. 2003/10/10 - Emiliano castagnari y Diego essaya * Bugfix: Multiple connections problem fixed. * Logueo: Logging function enhanced: it now supports variable arguments. 2003/10/09 - Diego Essaya Server now accepts multiple client connections, in a somewhat rudimentary fashion... but it works :) . There is one little problem, though: if one of the clients disconnects, the server dies. 2003/10/03 - Emiliano Castagnari Added the capability of running the program with no command-line arguments (default options are defined in defaults.h). Implemented the set_options() function. The optios structure was subdivided in two new structures containing IP and port info. 2003/10/01 - Emiliano Castagnari Finished command-line argument parsing functions. 2003/09/30 - Emiliano Castagnari Implemented a function for logging to a file descriptor (this is yet to be defined). 2003/09/29 - Emiliano Castagnari Server sources renamed (tcp_server -> gems-server). Cerated main.c/h, containing the initialization functions. Created types.h, with definition of various data types. 2003/09/26 - Emiliano Castagnari Project "gemsd" started. Server and client sources are under development. gems-1.1.1/server/source/sock_info.h0000644000175000017500000000473410114506232016116 0ustar maxymaxy/* ============================================================================ * << File: sock_info.h >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 03/01/04 * * Description: * Data types related to the connection information (IP, port, etc) * and the client lists. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _SOCK_INFO_H_ #define _SOCK_INFO_H_ /* IP address information: */ typedef struct t_ip { unsigned long s_addr; /* IP address in binary notation */ char *ip_str; /* IP address string */ } t_ip; /* TCP port information: */ typedef struct t_port { unsigned short port; /* Port number */ char *port_str; /* Port string */ } t_port; /* Client information: */ typedef struct t_clsock { int socket; /* Connection socket */ t_ip ip; /* IP address */ struct t_clsock *next; /* Double-linked list... you guessed it */ struct t_clsock *prev; } t_clsock; /* Connection-waiting client information: */ typedef struct t_waiting_client { int socket; /* Connection socket */ struct sockaddr_in addr_data; /* Address information */ pid_t pid; /* PID of the proccess that accepts the connection */ struct t_waiting_client *next; /* Another list... */ struct t_waiting_client *prev; } t_waiting_client; #endif /* vim: set ts=4 sw=4: */ gems-1.1.1/server/source/gems-server.c0000644000175000017500000004237411306773631016415 0ustar maxymaxy/* ============================================================================ * << File: gems_server.c >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * * Description: * Main server functions. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* malloc() */ #include /* bzero(), strncmp() */ #include /* close(), read() */ #include /* accept(), socket(), ... */ #include /* inet_addr() */ #include /* Errores - variable errno */ #include /* struct winsize */ #include /* wait() */ #include "version_server.h" #include "defaults.h" #include "sock_info.h" #include "log.h" #include "call_script.h" #include "sighandlers.h" #include "gems-server.h" /* Macro to add a FD to the 'open_fds' fd_set, which is in the data structure, * updating data->maxfd at the same time. */ #define FD_ADD(fd, data) \ { \ FD_SET((fd), &((data)->open_fds)); \ (data)->fdmax = MAX((data)->fdmax, (fd)); \ } /***************************************************************************** * init_sock_server() * * Opens and initializes a socket for incoming connections. Returns the * socket file descriptor, or -1 in case of error. */ status_t init_sock_server(t_server_data *data, t_options *options) { struct sockaddr_in server; /* socket data */ int sockopt = 1; /* for setsockopt() -- see below */ /* Create the new socket: */ if ((data->sock_server = socket(AF_INET, SOCK_STREAM, PROTO_TCP)) == -1) { perror(APPNAME); return EXIT_FAIL; } /* Set the SO_REUSEADDR flag. Necessary to be able to reconnect whithout * having to wait for the OS to close the port (?) */ setsockopt(data->sock_server, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)); /* If an IP address was not specified, listen any: */ server.sin_addr.s_addr = (options->ip.ip_str == NULL) ? SERVER_DEF_IP : inet_addr(options->ip.ip_str); server.sin_family = AF_INET; /* Protocol family: IPv4 */ server.sin_port = htons(options->port); /* Listening port */ bzero(&(server.sin_zero), 8); /* Initialize structure */ /* Bind the socket to the TCP port: */ if (bind(data->sock_server, (struct sockaddr *)&server, sizeof(server)) == -1) { perror(APPNAME); return EXIT_FAIL; } /* Start listening: */ listen(data->sock_server, options->maxconn); /* Add sock_server to open_fds: */ FD_ADD(data->sock_server, data); g_log(LOG_SOCK_C, options->port); return CONTINUE; } /***************************************************************************** * close_sockets() * * Close all open fds, including sockets. */ void close_sockets(t_server_data *data) { g_log(LOG_CLOSING_ALL); for ( ; data->client_list != NULL; ) del_client(data->client_list, data); data->clients = 0; if (data->sock_server > 0) close(data->sock_server); if (data->input_fd > 0) close(data->input_fd); } /***************************************************************************** * validate_client() * * Once the connection has been accept()ed, this function follows the gems * protocol, making the connection initialization. Returns TRUE if the * client has been accepted, according to the gems protocol. */ fbool_t validate_client(int fd, int max_conn, t_server_data *data) { char *client_version; fbool_t r; /* Check for maximum allowed connections */ if (data->clients >= max_conn) { g_log(LOG_MAXCONN); if (send_disconnect(fd) != TRUE) return FAIL; return FALSE; } /* Send server version */ if (send_version(fd) != TRUE) return FAIL; if ((r = get_ack(fd)) != TRUE) { if (r == FALSE) g_log(LOG_SERVER_VERSION); return r; } /* Get client version and compare */ if ((r = get_peer_version(fd, &client_version)) != TRUE) return r; if (strncmp(client_version, PROTOCOL_VERSION, strlen(PROTOCOL_VERSION))) { /* Different versions. */ /* If client version is "1.0b", accept. (That version uses the same * protocol): */ if (strcmp(client_version, "1.0b")) { g_log(LOG_CLIENT_VERSION); if (send_disconnect(fd) != TRUE) return FAIL; return FALSE; } } if (send_ack(fd) != TRUE) return FAIL; /* Send terminal size: */ if (send_winsize(fd) != TRUE) return FAIL; if ((r = get_ack(fd)) != TRUE) { if (r == FALSE) g_log(LOG_WINSIZE); return r; } return TRUE; } /***************************************************************************** * add_waiting_client() * * Add a client to the waiting queue. */ t_waiting_client *add_waiting_client(t_server_data *data, pid_t pid, int socket, struct sockaddr_in *addr_data) { t_waiting_client *new_wc; /* New waiting client client */ if (!(new_wc = (t_waiting_client *) malloc(sizeof(t_waiting_client)))) { g_log(ERR_MEMORY); return NULL; } new_wc->pid = pid; new_wc->socket = socket; new_wc->addr_data = *addr_data; /* Add the node to the start of the list (this results in a LIFO queue, * but who cares): */ new_wc->next = data->waiting_client_list; new_wc->prev = NULL; if (data->waiting_client_list != NULL) data->waiting_client_list->prev = new_wc; data->waiting_client_list = new_wc; return new_wc; } /***************************************************************************** * accept_new_client() * * Make all necessary operations to accept a client connection. */ fbool_t accept_new_client(t_server_data *data, int max_conn) { int fd; struct sockaddr_in client_data; unsigned int addr_size = sizeof(client_data); /* for accept() */ pid_t pid; if ((fd = accept(data->sock_server, (struct sockaddr *)&client_data, &addr_size)) == -1) { perror(APPNAME); return FAIL; } if (!(pid = fork())) { /* Child must validate the client and exit: */ if (validate_client(fd, max_conn, data) != TRUE) _exit(1); _exit(0); /* Client accepted */ } else if (pid > 0) { /* Father: must add the client to the waiting queue: */ add_waiting_client(data, pid, fd, &client_data); return TRUE; } /* WTF? Error in fork()! */ perror(APPNAME); close(fd); return FAIL; } /***************************************************************************** * add_client() * * Adds a client to the list of connected clients */ struct t_clsock *add_client(int socket, struct sockaddr_in *client_data, t_server_data *data) { struct t_clsock *new_client; char *ip_str; int ip_len; /* New connected client */ if (!(new_client = (struct t_clsock *) malloc(sizeof(struct t_clsock)))) { g_log(ERR_MEMORY); return NULL; } init_clsock(new_client); /* Get IP address: */ ip_str = inet_ntoa(client_data->sin_addr); ip_len = strlen(ip_str) + 1; /* + 1 for the '\0' character */ /* Store client info: */ if (!(new_client->ip.ip_str = malloc(sizeof(char) * ip_len))) { g_log(ERR_MEMORY); return NULL; } memcpy(new_client->ip.ip_str, ip_str, ip_len); new_client->socket = socket; /* Add the node to the start of the list: */ new_client->next = data->client_list; new_client->prev = NULL; if (data->client_list != NULL) data->client_list->prev = new_client; data->client_list = new_client; data->clients++; #ifdef DEBUG g_log(DEBUG_ADD_C, new_client->prev, new_client, new_client->next); #endif g_log(LOG_NEW_C, data->clients, new_client->ip.ip_str); return new_client; } /***************************************************************************** * remove_waiting_client() * * Removes a client from the waiting queue */ void remove_waiting_client(t_server_data *data, t_waiting_client *wc) { t_waiting_client *prev; t_waiting_client *next; if (wc == NULL) return; /* Store prev & next pointers: */ if (wc->prev == NULL) data->waiting_client_list = wc->next; else { prev = wc->prev; prev->next = wc->next; } if (wc->next != NULL) { next = wc->next; next->prev = wc->prev; } free(wc); } /***************************************************************************** * check_new_client(t_server_data *data) * * This function is called after receiving SIGCHLD. * It wait()s, retrieves the child PID and removes the client from the * waiting queue. * A child exit status of 0 means that the associated client has been accepted. * In this case, this function must add the client to the connected clients * list. */ fbool_t check_new_client(t_server_data *data, int expected_connections) { pid_t pid; int status; t_waiting_client *wc; pid = wait(&status); /* Search PID in the waiting queue: */ for (wc = data->waiting_client_list; wc != NULL; wc = wc->next) if (wc->pid == pid) break; if (!wc) return FALSE; /* PID not found */ /* Add the client to the connected clients list: */ if (add_client(wc->socket, &(wc->addr_data), data) == NULL) { close(wc->socket); return FAIL; } FD_ADD(wc->socket, data); /* Remove client from the waiting queue: */ remove_waiting_client(data, wc); /* If we must wait for a specified number of connections (-wait N * option), check if this condition is met. * To implement this, we simply add the input_fd file descriptor to * open_fds when we want to start transmission. */ if ((expected_connections > 0) && !(FD_ISSET(data->input_fd, &(data->open_fds)))) { if (data->clients == expected_connections) FD_ADD(data->input_fd, data); /* Start transmission. */ } return TRUE; } /***************************************************************************** * kill_waiting_clients() * * Kills all children processes that are waiting for client response. */ void kill_waiting_clients(t_server_data *data) { t_waiting_client *wc; pid_t pid; /* Send SIGTERM to all children: */ for (wc = data->waiting_client_list; wc != NULL; wc = wc->next) kill(data->waiting_client_list->pid, SIGTERM); /* wait() until the queue is empty: */ while (data->waiting_client_list != NULL) { pid = wait(NULL); for (wc = data->waiting_client_list; wc != NULL; wc = wc->next) { if (wc->pid == pid) { remove_waiting_client(data, wc); } } } } /***************************************************************************** * init_clsock() * * Initialize a client node */ void init_clsock(struct t_clsock *client_sock) { if (client_sock != NULL) { client_sock->socket = -1; client_sock->ip.ip_str = NULL; client_sock->ip.s_addr = 0; client_sock->prev = NULL; client_sock->next = NULL; } } /***************************************************************************** * del_client() * * Removes a client from the list of connected clients, closing its associated * socket */ void del_client(t_clsock *client, t_server_data *data) { struct t_clsock *prev; struct t_clsock *next; if (client == NULL) return; close(client->socket); FD_CLR(client->socket, &(data->open_fds)); #ifdef DEBUG g_log(DEBUG_DEL_C, client->prev, client, client->next); #endif g_log(LOG_CLOSE_C, client->ip.ip_str); /* Store prev & next pointers: */ if (client->prev == NULL) data->client_list = client->next; else { prev = client->prev; prev->next = client->next; } if (client->next != NULL) { next = client->next; next->prev = client->prev; } free(client->ip.ip_str); free(client); data->clients--; } /***************************************************************************** * send_to_all() * * Send a message to all clients */ fbool_t send_to_all(gems_msg_type_t type, char *buffer, int n, t_server_data *data) { t_gems_msg msg; t_clsock *client, *next; msg.type = type; msg.len = n; msg.data = buffer; for (client = data->client_list; client; client = next) { /* Save pointer to next client, in case this one is deleted: */ next = client->next; if (send_msg(client->socket, &msg) != TRUE) /* Must remove client from list: */ del_client(client, data); } return TRUE; } /***************************************************************************** * send_input_to_clients() * * Reads data from input stream and transmits to all clients. */ fbool_t send_input_to_clients(t_server_data *data) { int n; n = read(data->input_fd, gems_data_buffer, MSG_MAX_DATA_SIZE); if (n == 0) return FALSE; /* EOF */ else if (n == -1) { perror(APPNAME); return FAIL; } send_to_all(GEMS_DATA, gems_data_buffer, n, data); return TRUE; } /***************************************************************************** * attend_client() * * Recieves data from a client, and in case of EOF or error, closes the * connection. */ void attend_client(t_clsock *client, t_server_data *data) { if (read(client->socket, gems_msg_buffer, MSG_MAX_DATA_SIZE) <= 0) /* Error or EOF */ del_client(client, data); } /***************************************************************************** * send_winsize_to_clients() * * Sends the terminal size to all clients. */ void send_winsize_to_clients(t_server_data *data) { struct winsize win; int size[2]; ioctl(0, TIOCGWINSZ, (char *) &win); size[0] = (int) win.ws_col; size[1] = (int) win.ws_row; send_to_all(GEMS_WINSIZE, (char *)size, 2 * sizeof(int), data); } /***************************************************************************** * init_data() * * Initialize the server_data structure. */ void init_data(t_server_data *data, int input_fd, int expected_connections) { data->clients = 0; data->client_list = NULL; data->waiting_client_list = NULL; data->input_fd = input_fd; data->sock_server = -1; FD_ZERO(&(data->open_fds)); data->fdmax = 0; /* Is the -wait option enabled? If so, we must not add input_fd to * open_fds, so that select() does not check this fd. */ if (expected_connections == 0) /* Must not wait => start transmission immediately. */ FD_ADD(data->input_fd, data); } /***************************************************************************** * check_winsize() * * Checks local terminal size and outputs a warning if greater than 80x25 */ void check_winsize() { struct winsize win; /* Get terminal size: */ ioctl(0, TIOCGWINSZ, (char *) &win); if ((win.ws_col > SUGGESTED_MAX_COLS) || (win.ws_row > SUGGESTED_MAX_ROWS)) { fprintf(stderr, _("%s: warning: terminal size is greater " "than %dx%d.\n"), APPNAME, SUGGESTED_MAX_COLS, SUGGESTED_MAX_ROWS); } } /***************************************************************************** * gems_server() * * Server main function. */ status_t gems_server(int input_fd, t_options *options) { t_server_data data; fd_set rfds; /* Temporary fd list for select() */ struct t_clsock *client = NULL; /* List iterator */ struct t_clsock *next_client = NULL; /* List iterator */ status_t status = CONTINUE; /* Program status and return value. */ fbool_t r; /**************** Server initialization: ********************/ /* Initialize structure with server info: */ init_data(&data, input_fd, options->expected_connections); check_winsize(); /* Check terminal size: */ status = init_sock_server(&data, options); /* Start listening */ install_sighandlers(); /* Install signal handlers */ /********************** Main loop: **************************/ for ( ; status == CONTINUE ; ) { /* Wait until one of the fds has something to read: */ rfds = data.open_fds; if (select(data.fdmax + 1, &rfds, NULL, NULL, NULL) == -1) { if (errno != EINTR) status = EXIT_FAIL; /* error */ } if (sigint_raised) status = EXIT_OK; else if (winsize_changed) { send_winsize_to_clients(&data); winsize_changed = 0; } else if (sigchld_raised) { /* A child process has finished. This means that a new client * has (or not) been accepted. */ check_new_client(&data, options->expected_connections); sigchld_raised--; } else if (FD_ISSET(data.input_fd, &rfds)) { /* Must send data to all clients: */ if ((r = send_input_to_clients(&data)) == FAIL) status = EXIT_FAIL; else if (r == FALSE) /* EOF */ status = EXIT_OK; } else if (FD_ISSET(data.sock_server, &rfds)) /* A new connection must be accept()ed: */ accept_new_client(&data, options->maxconn); else /* Check clients: */ for (client = data.client_list; client; client = next_client) { /* Save pointer to next client in case this client is deleted */ next_client = client->next; if (FD_ISSET(client->socket, &rfds)) attend_client(client, &data); } } kill_waiting_clients(&data); close_sockets(&data); return status; } /* vim: set ts=4 sw=4: */ gems-1.1.1/server/source/main.c0000644000175000017500000002351411306760727015077 0ustar maxymaxy/* ============================================================================ * << File: main.c >> * ------------------ * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * * Description: * Server initialization functions, including main(). * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* isdigit() */ #include /* atoi() */ #include /* setlocale() */ #include /* strlen() */ #include /* open() */ #include /* open() */ #include /* close(), unlink() */ #include "defaults.h" #include "gems-server.h" #include "log.h" #include "call_script.h" #include "version_server.h" #include "main.h" /* Command line arguments: */ #define OPT_TOKEN '-' #define OPT_IP "-ip" #define OPT_PORT "-port" #define OPT_FLOG "-log" #define OPT_NOSCRIPT "-noscript" #define OPT_MAXCONN "-maxconn" #define OPT_SCRIPT_BIN "-script_bin" #define OPT_HELP "-h" #define OPT_VERSION "-v" #define OPT_WAIT "-wait" /*****************************************************************************/ int main(int argc, char *argv[]) { t_options options; /* Server options */ status_t status = CONTINUE; /* Program status, and return value */ /* Locale configuration: */ setlocale(LC_ALL, ""); bindtextdomain("gems", LOCALEDIR); textdomain("gems"); /* Set default options: */ init_options(&options); /* Parse arguments: */ status = parse_args(argc, argv, &options); if (status == CONTINUE) { /* Validate arguments: */ if (check_options(&options) == FAIL) return EXIT_FAIL; status = init_server(&options); } return status; } status_t parse_args(int argc, char *argv[], t_options *options) { status_t status = CONTINUE; /* state variable, and return value */ int i; int missing_arg = 0; /* ¿arguments missing? */ for (i = 1; (i < argc) && (status == CONTINUE); i++) { #ifdef DEBUG (i % 2) ? printf(" argv[%i]: %5s: ", i, argv[i]) : printf("%s%c", argv[i], '\n'); ((argc - i - 1)) ? 0 : printf("\n"); #endif /* IP address */ if (cmpstr(argv[i], OPT_IP) == TRUE) { if (i + 1 < argc) options->ip.ip_str = argv[++i]; else missing_arg = 1; } /* TCP port */ else if (cmpstr(argv[i], OPT_PORT) == TRUE) { if (i + 1 < argc) options->port = atoi(argv[++i]); else missing_arg = 1; } /* 'wait' option */ else if (cmpstr(argv[i], OPT_WAIT) == TRUE) { if (i + 1 < argc) options->expected_connections = atoi(argv[++i]); else missing_arg = 1; } /* Log filename */ else if (cmpstr(argv[i], OPT_FLOG) == TRUE) { if ((i + 1) < argc) options->flog = argv[++i]; else missing_arg = 1; } /* 'noscript' option */ else if (cmpstr(argv[i], OPT_NOSCRIPT) == TRUE) options->script = 0; /* Script location */ else if (cmpstr(argv[i], OPT_SCRIPT_BIN) == TRUE) { if (i + 1 < argc) options->script_bin = argv[++i]; else missing_arg = 1; } /* Max number of connections: */ else if (cmpstr(argv[i], OPT_MAXCONN) == TRUE) { if (i + 1 < argc) options->maxconn = atoi(argv[++i]); else missing_arg = 1; } /* Show version : */ else if (cmpstr(argv[i], OPT_VERSION) == TRUE) { PRINT_VERSION(APPNAME) status = EXIT_OK; } /* Help: */ else if (cmpstr(argv[i], OPT_HELP) == TRUE) { long_help(); status = EXIT_OK; } else /* Invalid option */ { g_log(ERR_NOT_OPT, argv[i]); short_help(); status = EXIT_FAIL; } /* Arguments missing? */ if (missing_arg) { g_log(ERR_OPT_WO_ARG, argv[i]); status = EXIT_FAIL; } } #ifdef DEBUG print_options(options); #endif return status; } /*****************************************************************************/ char *create_lock(t_options *options) { int fd; static char name[64]; char pid_str[16]; int pid_strlen; snprintf(name, 64, "%s%d", LOCK_PREFIX, options->port); if ((fd = open(name, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1) { g_log(ERR_LOCK, name, APPNAME); return NULL; } pid_strlen = snprintf(pid_str, 16, "%d\n", getpid()); write(fd, pid_str, pid_strlen); close(fd); return name; } /*****************************************************************************/ void release_lock(char *name) { if (unlink(name) == -1) perror(APPNAME); } /*****************************************************************************/ void short_help() { printf(_("Usage: %s [%s IP] [%s PORT] [%s N] [%s M] [%s LOGFILE]\n" "\t\t [%s] [%s SCRIPT] [%s] [%s]\n"), APPNAME, OPT_IP, OPT_PORT, OPT_MAXCONN, OPT_WAIT, OPT_FLOG, OPT_NOSCRIPT, OPT_SCRIPT_BIN, OPT_HELP, OPT_VERSION); printf(_("Try `%s %s' for more information.\n"), APPNAME, OPT_HELP); } /*****************************************************************************/ void long_help() { printf(_("Usage: %s [options]\n"), APPNAME); putchar('\n'); printf(_("Options:\n")); printf(_(" %s IP\t\tIP where connections will be accepted.\n"), OPT_IP); printf(_(" %s PORT\t\tTCP port. Default: %d.\n"), OPT_PORT, DEFAULT_TCP_PORT); printf(_(" %s N\t\tAccept N simultaneous clients. Default: %d.\n"), OPT_MAXCONN, SERVER_DEF_MAX_CONN); printf(_(" %s M\t\tWait for the connection of M clients before starting\n" "\t\t\ttransmission. Default: %d.\n"), OPT_WAIT, SERVER_DEF_WAIT); printf(_(" %s LOGFILE\t\tSpecify the log destination. " "The possible values are:\n" "\t\t\tthe name of a file, `syslog', or `stderr'.\n"), OPT_FLOG); printf(_(" %s\t\tDo not run script(1).\n"), OPT_NOSCRIPT); printf(_(" %s SCRIPT\tSpecify the location of script.\n" "\t\t\tDefault: %s.\n"), OPT_SCRIPT_BIN, SERVER_DEF_SCRIPT_BIN); printf(_(" %s\t\t\tShow this help message.\n"), OPT_HELP); printf(_(" %s\t\t\tShow version information.\n"), OPT_VERSION); } /*****************************************************************************/ void init_options(t_options * opcion) { opcion->ip.s_addr = SERVER_DEF_IP; /* Equivalent to INADDR_ANY */ opcion->ip.ip_str = NULL; opcion->port = DEFAULT_TCP_PORT; opcion->flog = SERVER_DEF_FLOG; opcion->script = SERVER_DEF_SCRIPT; opcion->script_bin = SERVER_DEF_SCRIPT_BIN; opcion->maxconn = SERVER_DEF_MAX_CONN; opcion->expected_connections = SERVER_DEF_WAIT; } /*****************************************************************************/ fbool_t cmpstr(const char *str_1, const char *str_2) { int size_str1 = strlen(str_1); int size_str2 = strlen(str_2); int i; if (size_str1 != size_str2) return FALSE; for (i = 0; (i < size_str1) && (str_1[i] == str_2[i]); i++) ; return (i == size_str1) ? TRUE : FALSE; } /*****************************************************************************/ fbool_t check_options(t_options * opcion) { /* Validate TCP port: */ if (!port_range(opcion->port)) { g_log(ERR_PORT); return FAIL; } /* Validate 'maxconn' option: */ if (opcion->maxconn <= 0) { g_log(ERR_MAXCONN); return FAIL; } /* Validate 'wait' option: */ if ((opcion->expected_connections < 0) || (opcion->expected_connections > opcion->maxconn)) { g_log(ERR_WAIT); return FAIL; } return TRUE; } /*****************************************************************************/ #ifdef DEBUG void print_options(const t_options * option) { printf("%c", '\n'); printf(" IP: ........ %s\n", (option->ip.ip_str == NULL) ? "*" : option->ip.ip_str); printf(" Port: ... %i\n", option->port); printf(" LogFile: ... %s\n", option->flog); printf(" Script: ... %d\n", option->script); printf(" Script_bin: ... %s\n", option->script_bin); printf(" MaxConn: ... %d\n", option->maxconn); printf(" Wait: ... %d\n", option->expected_connections); printf("\n"); } #endif /* DEBUG */ /*****************************************************************************/ status_t init_server(t_options *options) { fbool_t r; int input_fd; /* data input file descriptor */ char *lockname; if (options == NULL) return EXIT_FAIL; if ((lockname = create_lock(options)) == NULL) return EXIT_FAIL; fprintf(stderr, _("%s initialized.\n"), APPNAME); /* Must call script? */ if (options->script) { if ((r = start_script(options->script_bin, &input_fd)) != CONTINUE) { kill_script(); release_lock(lockname); return r; } } else input_fd = 0; /* stdin */ /* Configure logging: */ if (options->flog) { if (cmpstr(options->flog, "syslog") == TRUE) g_set_logdest(G_LOG_SYSLOG); else if (cmpstr(options->flog, "stderr") == TRUE) g_set_logdest(G_LOG_STDERR); else g_set_logdest(G_LOG_FILE, options->flog); } else g_set_logdest(G_LOG_NOLOG); /* Main server function: */ r = gems_server(input_fd, options); kill_script(); if (r == EXIT_OK) fprintf(stderr, _("\n%s terminated.\n"), APPNAME); release_lock(lockname); return r; } /* vim: set ts=4 sw=4: */ gems-1.1.1/server/source/main.h0000644000175000017500000000343110114506232015061 0ustar maxymaxy/* ============================================================================ * << File: main.h >> * ------------------ * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _MAIN_H_ #define _MAIN_H_ #include "common.h" status_t parse_args(int argc, char *argv[], t_options *options); void short_help(); void long_help(); void init_options(t_options *); fbool_t cmpstr(const char *, const char *); fbool_t check_options(t_options * option); status_t init_server(t_options *); #ifdef DEBUG void print_options(const t_options *); #endif /* DEBUG */ #endif gems-1.1.1/server/source/call_script.h0000644000175000017500000000310710114506231016433 0ustar maxymaxy/* ============================================================================ * << File: call_script.h >> * ------------------------- * Author: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 14/01/04 * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _CALL_SCRIPT_H_ #define _CALL_SCRIPT_H_ #include "common.h" /* status_t */ status_t start_script(char *script_bin, int *input_fd); void kill_script(); #endif gems-1.1.1/server/source/log.c0000644000175000017500000001332410123160003014703 0ustar maxymaxy/* ============================================================================ * << File: log.c >> * ----------------- * Author: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * * Description: * Functions for server logging. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* variable parameter functions */ #include /* time_t */ #include /* syslog() */ #include /* strlen() */ #include "version_server.h" #include "log.h" /* Global variables defining where to log: */ log_opt_t logdest = G_LOG_NOLOG; char *logfile = NULL; t_log mesg_log[] = { {ERR_NOT_OPT, LOG_ERR, N_("Invalid option: %s.")}, {ERR_OPT_WO_ARG, LOG_ERR, N_("Option %s requires an argument.")}, {ERR_PORT, LOG_ERR, N_("Invalid TCP port.")}, {ERR_MAXCONN, LOG_ERR, N_("Invalid value for maximum number of" " connections.")}, {ERR_WAIT, LOG_ERR, N_("Invalid value for 'wait' option.")}, {ERR_MEMORY, LOG_ERR, N_("Not enough memory.")}, {ERR_LOCK, LOG_ERR, N_("Cannot create lock file: %s\n" " Maybe another instance of %s is running?")}, {ERR_INTERNAL, LOG_ERR, N_("Aaargh! Internal error -- sorry.")}, {LOG_SOCK_C, LOG_INFO, N_("Accepting connections on port %d.")}, {LOG_NEW_C, LOG_INFO, N_("New connection -- Client %i -- " "IP: %s.")}, {LOG_CLOSE_C, LOG_INFO, N_("Client disconnection -- IP: %s.")}, {LOG_CLOSING_ALL, LOG_INFO, N_("Closing all connections...")}, {LOG_MAXCONN, LOG_INFO, N_("Connection refused: maximum number " "of connections reached.")}, {LOG_SERVER_VERSION,LOG_INFO, N_("Connection refused: obsolete server " "version.")}, {LOG_CLIENT_VERSION,LOG_INFO, N_("Connection refused: obsolete client " "version.")}, {LOG_WINSIZE, LOG_INFO, N_("Client disconnected: terminal size " "too small.")}, {LOG_RUNSCRIPT, LOG_INFO, N_("Running %s...")}, {LOG_ENDSCRIPT, LOG_INFO, N_("script terminated.")}, {DEBUG_ADD_C, LOG_DEBUG, " # DEBUG | add_client(): client->prev[%p] | client[%p] | client->next[%p]"}, {DEBUG_DEL_C, LOG_DEBUG, " # DEBUG | del_client(): client->prev[%p] | client[%p] | client->next[%p]"}, {DEBUG_SEND_C, LOG_DEBUG, " # DEBUG | send_2client(): "} }; /***************************************************************************** * g_set_logfile() * * Specifies where the logs wil be written to. * If _logdest == G_LOG_FILE, another argument is expected (filename). */ void g_set_logdest(log_opt_t _logdest, ...) { va_list va; logdest = _logdest; va_start(va, _logdest); if (logdest == G_LOG_FILE) logfile = va_arg(va, char *); else logfile = NULL; va_end(va); if (logdest == G_LOG_SYSLOG) openlog(APPNAME, LOG_PID, LOG_USER); } /***************************************************************************** * g_log() * * Logging function. * In case of not writing to syslog, the format of the produced log is: * * APPNAME [DATE]: mesg_log[errno] * * where 'APPNAME' is the name of the program, 'DATE' is the value returned * by the 'g_time()' function, and 'mesg_log[errno]' is the message string. * * The extra arguments are passed to vsprintf(). */ void g_log(short int errno, ...) { char string[255]; /* Container for the formatted message */ va_list va; FILE *fd_log = NULL; /* Validation of the errno argument: */ if (errno >= sizeof(mesg_log) || errno < 0) errno = ERR_INTERNAL; /* Format message string + variable arguments into the "string" array */ va_start(va, errno); vsprintf(string, _(mesg_log[errno].log_str), va); va_end(va); if (logdest == G_LOG_SYSLOG) /* use syslog */ { syslog(mesg_log[errno].priority, string); } else if (logdest != G_LOG_NOLOG) /* log to stderr or a file: */ { if ((logdest == G_LOG_FILE) && (logfile)) { fd_log = fopen(logfile, "a"); } fprintf((fd_log == NULL) ? stderr : fd_log, APPNAME" [%s]: %s\n", g_time(), string); if (fd_log != NULL) fclose(fd_log); } /* If we have an error message, write it to stderr (if not done yet): */ if ((mesg_log[errno].priority == LOG_ERR) && (logdest != G_LOG_STDERR)) { fprintf(stderr, APPNAME": %s\n", string); } } /*****************************************************************************/ /* Return the actual time & date, in string format. */ char *g_time(void) { time_t time_utc; /* UTC formatted date */ struct tm time_fmt; /* Time data structure */ char *time_str; time_utc = time(NULL); time_fmt = (*localtime(&time_utc)); time_str = asctime(&time_fmt); time_str[strlen(time_str) - 1] = '\0'; return time_str; } /* vim: set ts=4 sw=4: */ gems-1.1.1/server/source/call_script.c0000644000175000017500000000747010114506231016435 0ustar maxymaxy/* ============================================================================ * << File: call_script.c >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 14/01/04 * * Description: * Functions for calling script(1) as a child process and take its * output using a pipe. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* fork(), pipe() */ #include /* kill() */ #include /* wait() */ #include /* strcat() */ #include /* exit() */ #include /* error values - variable errno */ #include "defaults.h" #include "version_server.h" #include "log.h" #include "call_script.h" #ifndef DEV_FD_DIR #define DEV_FD_DIR "/dev/fd/" #endif /* PID of script. If zero, it means that script is not running. */ static pid_t script_pid = 0; /* start_script() * * Runs script(1) as a child process and returns in *input_fd the file * descriptor from which script's output data can be read. */ status_t start_script(char *script_bin, int *input_fd) { /* Steps: * - Create a pipe * - fork() * * Child -> exec(script -fq /dev/fd/nn) * where nn is pipefd[1]. * * Father -> read data from pipefd[0] * * FIXME: This function supposes the existence of the /dev/fd/ directory. * Is this portable?... */ int pipefd[2]; char fname[20] = DEV_FD_DIR; char num_str[5]; /* We'll pass 3 arguments to script, so we need a 4 element array of * strings, the last one being NULL */ char *script_argv[] = {NULL, SCRIPT_OPTIONS, NULL, NULL}; script_argv[0] = script_bin; g_log(LOG_RUNSCRIPT, script_bin); /* Create the pipe: */ if (pipe(pipefd) == -1) { perror(APPNAME); return EXIT_FAIL; } switch ((script_pid = fork())) { case -1: perror(APPNAME); return EXIT_FAIL; case 0: /* CHILD - run script */ /* Close the side of the pipe that is not used: */ close(pipefd[0]); /* Get the name of the /dev/fd/xx file associated with the pipe */ sprintf(num_str, "%d", pipefd[1]); strcat(fname, num_str); script_argv[2] = fname; /* Execute script: */ execv(script_argv[0], script_argv); perror(APPNAME); exit(-1); } /* FATHER */ /* Close the side of the pipe that is not used: */ close(pipefd[1]); *input_fd = pipefd[0]; return CONTINUE; } /* kill_script() * * If script is running, this function sends the SIGTERM signal to it, and * wait()s. */ void kill_script() { if (script_pid > 0) { if (kill(script_pid, SIGTERM) == -1) { if (errno != ESRCH) perror(APPNAME); } else if (wait(NULL) == -1) perror(APPNAME); script_pid = 0; g_log(LOG_ENDSCRIPT); } } /* vim: set ts=4 sw=4: */ gems-1.1.1/server/source/log.h0000644000175000017500000000443210122713423014721 0ustar maxymaxy/* ============================================================================ * << File: log.h >> * ----------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _LOG_H_ #define _LOG_H_ typedef enum { ERR_NOT_OPT, ERR_OPT_WO_ARG, ERR_PORT, ERR_MAXCONN, ERR_WAIT, ERR_MEMORY, ERR_LOCK, ERR_INTERNAL, LOG_SOCK_C, LOG_NEW_C, LOG_CLOSE_C, LOG_CLOSING_ALL, LOG_MAXCONN, LOG_SERVER_VERSION, LOG_CLIENT_VERSION, LOG_WINSIZE, LOG_RUNSCRIPT, LOG_ENDSCRIPT, DEBUG_ADD_C, DEBUG_DEL_C, DEBUG_SEND_C } log_t; /* Log message: */ typedef struct t_log { log_t log_id; /* ID number */ int priority; /* Type of message, same as syslog. Possible values: * LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR */ char *log_str; /* Message string */ } t_log; /* Log destinations: */ typedef enum { G_LOG_NOLOG = 0, G_LOG_SYSLOG, G_LOG_FILE, G_LOG_STDERR } log_opt_t; /************************************************/ void g_set_logdest(log_opt_t _logdest, ...); void g_log(short int, ...); char *g_time(void); #endif /* vim: set ts=4 sw=4: */ gems-1.1.1/server/Makefile0000644000175000017500000000321111306773631014135 0ustar maxymaxySRCDIR = source/ BINDIR = bin/ COMDIR = ../common/ VPATH = $(COMDIR) $(SRCDIR) INCS = -I$(COMDIR) -I$(SRCDIR) PACKDEP = $(BINDIR)main.o $(BINDIR)log.o $(BINDIR)gems-server.o \ $(BINDIR)call_script.o COMMONPACKDEP = $(COMDIR)protocol.o $(COMDIR)sighandlers.o MAINDEP = main.c defaults.h sock_info.h gems-server.h log.h version_server.h \ common.h version.h protocol.h call_script.h main.h LOGDEP = log.c log.h version_server.h version.h SERVERDEP = gems-server.c common.h version.h version_server.h protocol.h \ defaults.h sock_info.h log.h call_script.h gems-server.h sighandlers.h SCRIPTDEP = call_script.c defaults.h call_script.h common.h \ version_server.h version.h ################################################## .PHONY: all bindir remove clean install uninstall all: bindir $(BINDIR)gems-server bindir: @if [ ! -d "$(BINDIR)" ] ; then mkdir "$(BINDIR)"; fi $(BINDIR)gems-server : $(PACKDEP) $(COMMONPACKDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) $^ -o $@ $(BINDIR)main.o : $(MAINDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) -c $< -o $@ $(BINDIR)log.o : $(LOGDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) -c $< -o $@ $(BINDIR)call_script.o : $(SCRIPTDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) -c $< -o $@ $(BINDIR)gems-server.o : $(SERVERDEP) $(CC) $(CFLAGS) $(CDEFINES) $(INCS) -c $< -o $@ $(COMDIR)protocol.o: protocol.c common.h version.h protocol.h cd $(COMDIR) && $(MAKE) $(COMDIR)sighandlers.o: ../common/sighandlers.c ../common/sighandlers.h cd $(COMDIR) && $(MAKE) clean remove: rm -f $(PACKDEP) $(BINDIR)gems-server install: $(BINDIR)gems-server install -m755 -D $< $(BINPATH)/gems-server uninstall: rm -fv $(BINPATH)/gems-server gems-1.1.1/doc/0000755000175000017500000000000011306774376011746 5ustar maxymaxygems-1.1.1/doc/Protocol0000644000175000017500000000455710113534137013465 0ustar maxymaxy gems communication protocol --------------------------- Data streams sent in either way (server - >client or client -> server) have the following structure: | msg type (1 byte) | Size n (4 bytes) | Message (n bytes) | Integer numbers are sent with the least significant byte first (this is how integers are stored in i386). Types of messages: | DATA | Size: n | Data (n bytes) | Code: DATA = 0x00 With this message the server sends all the data which is to be shown on screen by the clients. n is at most 1024 bytes. | WINSIZE | Size: 8 | Cols (4 bytes) | Rows (4 bytes) | Code: WINSIZE = 0x01 By sending this message, the server informs the client about its terminal size. | VERSION | SIZE: n | Version string (n bytes) | Code: VERSION = 0x02 Informs about the protocol version used by the program. | ACK | SIZE: 0 | Code: ACK = 0x03 Acknowledge. | DISCONNECT | SIZE: 0 | Code: DISCONNECT = 0x04 After any of both parts send this message, the connection is closed. When a client starts the connection with the server, the following messages are interchanged in order to decide if the connection is accepted or not. CLIENT SERVER VERSION <---- Client can close connection if its protocol version is incompatible. ----> ACK ----> VERSION Server can refuse connection if client's protocol version is incompatibe. ACK <---- WINSIZE <---- Client can close connection if its terminal size is small. ----> ACK Connection accepted. Server starts sending data. (...) DATA <---- DATA <---- DATA <---- (...) During the initial communication (before any DATA messages are sent), if the server refuses the client, or the client decides to close connection, the DISCONNECT message must be sent instead of ACK, before physically closing the connection. If the first message sent to the client is DISCONNECT (instead of VERSION), this means that the server refuses the connection because it has reached the maximum number of allowed connections. It is not necessary to send DISCONNECT to close the connection after it has been accepted (i.e., if a user closes manually the server or the client). If a change in the terminal size occurs, the server can send another WINSIZE message, where clients can disconnect or not (client must not answer with ACK in this case). gems-1.1.1/doc/gems-server.10000644000175000017500000000514510123177501014253 0ustar maxymaxy.\"Command for previewing: man -l gems-server.1 .TH GEMS-SERVER 1 "AUGUST 2004" gems "gems documentation" .SH NAME gems-server \- Transmit a shell session in real time. .SH SYNOPSIS .B gems-server .RB [ -ip .IR ip ] .RB [ -port .IR p ] .RB [ -maxconn .IR n ] .RB [ -wait .IR m ] .RB [ -log .IR dest ] .RB [ -noscript ] .RB [ -script_bin .IR script ] .P .B gems-server .BR -h\ |\ -v .SH DESCRIPTION Transmits a shell session to be shown in real time in various different computers or terminals. .P .B gems-server transmits data in real time to clients via a network connection. Each one of these clients must run the .BR gems-client (1) program to establish connection and receive the transmitted data. Clients can connect and disconnect at any given time, but of course they will only receive the data that is transmitted while they are connected (just like a radio transmission/reception system). .P The gems system is normally used to transmit a console session and show it in various computers at the same time, but it is also possible to transmit any other kind of data. .SH OPTIONS .B .IP -ip IP address where connections will be accepted. .B .IP -port TCP port. Default: 6666. .B .IP -maxconn Do not accept more than .I n simultaneous clients. Default: 25. .B .IP -wait Wait until .I m clients are connected before starting the transmission. This is useful to ensure that the first .I m clients will receive all data transmitted. The default behavior is to start transmission immediately, without waiting for client connections. .B .IP -log By default, the server does not write log messages. This option configures the destination of logs. The .I dest argument can be `syslog' (to log through .BR syslogd (8)), `stderr' (to log through standard error output), or it can be an arbitrary filename (log messages will be appended to the file, if it exists). .B .IP -noscript By default, the server executes the .BR script (1) program to take terminal data and transmit it. When this option is used the server will not run script, and data will be read form standard input. .B .IP -script_bin Specifies the location of the .BR script (1). program. Default: .IR /usr/bin/script . .B .IP -h Show a short help message. .B .IP -v Show version information. .SH FILES .B .IP /var/lock/gems-server.PORT Lock file. .B gems-server creates this file on initialization and deletes it on exit. PORT is replaced by the TCP port used. .B gems-server will not run if this file is already present in the system. .SH AUTHORS Diego Essaya .P Emiliano Castagnari .SH SEE ALSO .BR gems-client (1), .BR script (1), .BR syslogd (8) gems-1.1.1/doc/gems-client.10000644000175000017500000000242410114471641014222 0ustar maxymaxy.\"Command for previewing: man -l gems-client.1 .TH GEMS-CLIENT 1 "AUGUST 2004" gems "gems documentation" .SH NAME gems-client \- Show data transmitted by gems-server (1) .SH SYNOPSIS .B gems-client .RB [ -i ] .I host .RI [ port ] .P .B gems-client .BR -h\ |\ -v .SH DESCRIPTION Connects to a computer running .BR gems-server (1) to show in the local terminal the data transmitted, in real time. .SH OPTIONS .I .IP host Hostname or IP address where the server is running. .I .IP port TCP port used by the server. Default: 6666. .B .IP -i Ignore server terminal size. If this option is not supplied, .B gems-client disconnects itself whenever the local terminal is smaller than the terminal associated to the server. .B .IP -h Show a short help message. .B .IP -v Show version information. .SH FUNCTION KEYS While .B gems-client is running, some keys have special functions: .B .IP q Quit. .B .IP a Enable/disable alarm blocking. When active, if the server transmits an alarm character ('\\a'), the client ignores it, so the alarm does not play. This is specially useful when many clients are present in the same room, to avoid the annoying simultaneous beeps. .SH AUTHORS Diego Essaya .P Emiliano Castagnari .SH SEE ALSO .BR gems-server (1), .BR script (1) gems-1.1.1/doc/es/0000755000175000017500000000000011306774376012355 5ustar maxymaxygems-1.1.1/doc/es/gems-server.10000644000175000017500000000601210123177501014654 0ustar maxymaxy.\"Comando para previsualizar: man -l gems-server.1 .TH GEMS-SERVER 1 "AGOSTO 2004" gems "Documentación de gems" .SH NOMBRE gems-server \- Transmitir una sesión de consola en tiempo real. .SH SINOPSIS .B gems-server .RB [ -ip .IR ip ] .RB [ -port .IR p ] .RB [ -maxconn .IR n ] .RB [ -wait .IR m ] .RB [ -log .IR dest ] .RB [ -noscript ] .RB [ -script_bin .IR script ] .P .B gems-server .BR -h\ |\ -v .SH DESCRIPCION Permite transmitir una sesión de consola para mostrarla en tiempo real en varias computadoras o terminales. .P .B gems-server transmite datos en tiempo real a clientes mediante una conexión de red. Cada uno de estos clientes debe ejecutar el programa .BR gems-client (1) para establecer la conexión y recibir los datos transmitidos. Los clientes pueden conectarse y desconectarse en cualquier momento dado, pero por supuesto, solamente recibirán los datos que fueron transmitidos mientras estaban conectados (como un sistema de transmisión/recepción de radio). .P Normalmente el sistema gems se utiliza para transmitir una sesión de consola y así mostrarla en varias computadoras al mismo tiempo, pero también es posible transmitir cualquier otro tipo de datos. .SH OPCIONES .B .IP -ip Dirección IP donde se aceptarán conexiones. .B .IP -port Puerto TCP. Por omisión: 6666. .B .IP -maxconn Aceptar a lo sumo .I n clientes simultáneos. Por omisión: 25. .B .IP -wait Esperar la conexión de .I m clientes antes de comenzar a transmitir. Esta opción es útil para asegurarse de que los primeros .I m clientes reciban los primeros datos transmitidos, aunque tarden un poco en conectarse. El comportamiento por omisión es no esperar a ningún cliente, y comenzar la transmisión inmediatamente. .B .IP -log Por omisión, el servidor no escribe logs (mensajes de diagnóstico). Esta opción permite especificar que el servidor emita logs, y el destino de los mismos. El argumento .I dest puede ser `syslog' para indicar que los logs deben ser transmitidos mediante .BR syslogd (8), `stderr' para que los logs sean escritos en la salida de error, o bien puede indicarse el nombre de un archivo, para que los logs sean escritos en el mismo. .B .IP -noscript Por omisión, el servidor ejecuta el programa .BR script (1) para tomar los datos de la terminal y así poder transmitirlos. Si se utiliza esta opción, el servidor no ejecutará este programa, y los datos transmitidos serán leídos de la entrada estándar. .B .IP -script_bin Permite especificar la ubicación del programa .BR script (1). Por omisión: .IR /usr/bin/script . .B .IP -h Mostrar un breve texto de ayuda. .B .IP -v Mostrar información acerca de la versión. .SH ARCHIVOS .B .IP /var/lock/gems-server.PUERTO Archivo de lock. .B gems-server crea este archivo durante la inicialización y lo borra al finalizar la ejecución. PUERTO es reemplazado por el puerto TCP utilizado. .B gems-server no se ejecutará si este archivo ya está presente en el sistema. .SH AUTORES Diego Essaya .P Emiliano Castagnari .SH VEA TAMBIEN .BR gems-client (1), .BR script (1), .BR syslogd (8) gems-1.1.1/doc/es/gems-client.10000644000175000017500000000245510114471641014635 0ustar maxymaxy.\"Comando para previsualizar: man -l gems-client.1 .TH GEMS-CLIENT 1 "AGOSTO 2004" gems "Documentación de gems" .SH NOMBRE gems-client \- Mostrar datos transmitidos por el servidor gems-server (1) .SH SINOPSIS .B gems-client .RB [ -i ] .I host .RI [ puerto ] .P .B gems-client .BR -h\ |\ -v .SH DESCRIPCION Permite conectarse a una computadora donde se está ejecutando .BR gems-server (1) para mostrar en pantalla los datos transmitidos, en tiempo real. .SH OPCIONES .I .IP host Dominio o IP en donde se está ejecutando el servidor. .I .IP puerto Puerto TCP usado por el servidor. Por omisión: 6666. .B .IP -h Mostrar un breve texto de ayuda. .B .IP -v Mostrar información acerca de la versión. .SH TECLAS Mientras .B gems-client está en funcionamiento, las siguientes teclas ingresadas mediante el teclado tienen funciones asignadas: .B .IP q Salir. .B .IP a Activar/desactivar el bloqueo de la alarma. Cuando la opción está activa, si el servidor transmite una alarma (caracter '\\a'), el cliente la ignora, por lo que la alarma no suena. Esto es especialmente útil cuando varios clientes se están ejecutando en una misma sala, para evitar que suenen varios 'beeps' simultáneos. .SH AUTORES Diego Essaya .P Emiliano Castagnari .SH VEA TAMBIEN .BR gems-server (1), .BR script (1) gems-1.1.1/doc/Makefile0000644000175000017500000000151210114264006013361 0ustar maxymaxyCLIENT_MAN = gems-client.1 SERVER_MAN = gems-server.1 #extra translations for man pages: LANGUAGES = es TRANSLATED_MAN_PAGES = $(LANGUAGES:%=%/$(CLIENT_MAN)) TRANSLATED_MAN_PAGES += $(LANGUAGES:%=%/$(SERVER_MAN)) .PHONY: all remove clean install uninstall all remove clean: install: $(CLIENT_MAN) $(SERVER_MAN) $(TRANSLATED_MAN_PAGES) install -m644 -D $(CLIENT_MAN) $(MANPATH)/man1/$(CLIENT_MAN) install -m644 -D $(SERVER_MAN) $(MANPATH)/man1/$(SERVER_MAN) for i in $(LANGUAGES); do \ install -m644 -D $$i/$(SERVER_MAN) $(MANPATH)/$$i/man1/$(SERVER_MAN); \ install -m644 -D $$i/$(CLIENT_MAN) $(MANPATH)/$$i/man1/$(CLIENT_MAN); \ done uninstall: rm -fv $(MANPATH)/man1/$(CLIENT_MAN) $(MANPATH)/man1/$(SERVER_MAN) for i in $(LANGUAGES); do \ rm -fv $(MANPATH)/$$i/man1/$(CLIENT_MAN) $(MANPATH)/$$i/man1/$(SERVER_MAN); \ done gems-1.1.1/doc/CodingStandars.tex0000644000175000017500000000646310113534137015364 0ustar maxymaxy\documentclass[11pt]{article} %\usepackage [spanish]{babel} %\usepackage[latin1] {inputenc} \title{Coding Standards} \author{Emiliano Castagnari } \begin{document} \tableofcontents \newpage \section{Files} Every source code file must start with the filename, author(s), a short description of the file contents, copyright notice and the abbreviated GPL notice. \section{Modules} The inclusion of header files (\#include) and declarations of macros and constants (\#define) must be made at the beginning of the .c source file, unless it is necessary to make it in the module header file (.h) (eg, when other modules reference the same definition). \section{Tabs} Indentation is made with tab characters, except for comments used at the right of the code, where spaces should be used instead. \section{Screen width} When possible, the 80 character width must be respected. \section{Data types} Data types should be defined following this scheme:\\ \begin{center} \begin{tabular}{l|l|l|l} Data type & Prefix & Suffix & Example \\ \hline Structures & t\_ & - & t\_node \\ Other & - & \_t & node\_t \\ \hline \end{tabular} \end{center} \section{Functions and prototypes} Functions should not be declared inside other functions, and their prototypes must be declared in the header file. The syntax must be as follows: \begin{verbatim} /* Function prototype (in header file) */ /* Parameter names are optional. */ return_type function(type1, type2 *, type3); /* Function implementation */ /* Short description of the function (recommended). */ return_type function(type1 FirstVariable, type2 *SecondVariable, type3 ThirdVariable) { ... statements; ... } \end{verbatim} \section{Blocks} The {\bf conditional blocks}, {\bf control structures} and {\bf iterative cycles} must be indented as follows: If the block has only one statement: \begin{verbatim} /*...*/ if() statement; else statement; /* alternatively */ if() statement; else statement; for(statement1; ; statement3) statement; /* alternatively */ for(statement1; ; statement3) statement; while() statement; /* alternatively */ while() statement; /*...*/ \end{verbatim} General case (more than one statement in the block): \begin{verbatim} /*...*/ if() { statement1; statement2; /* ... */ } else { statement1; statement2; /* ... */ } for(statement1; ; statement3) { statement1; statement2; /* ... */ } while() { statement1; statement2; /* ... */ } \end{verbatim} If a control structure is nested inside another one, the use of \{\} is mandatory, especially with 'if' structures. \begin{verbatim} if(...) { if(...) { ... } else do_something(); } \end{verbatim} \section{indent(1)} It is possible to use the \verb|indent(1)| tool to apply the proposed indentation to any source file. To do this, the command line should be: \begin{verbatim} $ indent -bli0 -nsob -di1 -npsl -npcs -ss -bls -i4 -ts4 -lc80 -l80 \end{verbatim} The only problem with this approach are the comments at the right of the code, because \verb|indent| uses tabs for them, instead of spaces. (Note that tab size has been set to 4 spaces). \end{document} gems-1.1.1/Changelog0000644000175000017500000000745611306773631013020 0ustar maxymaxygems - Changelog 2009/12/06 - Version 1.1.1 * main.c small fix: open() needs mode if O_CREAT in main.c (LP: #492996) * client/Makefile and server/Makefile: -s option argument of install has been removed. * server/source/gems-server.c: Avoid sending an extra char to the terminal when the terminal size is larger than suggested. 2004/09/19 - Version 1.1 * Internationalization: Everything was translated into English, including code comments. Both server and client now support locales via gettext. Spanish translation is included. * Server - Terminal size: gems-server now prints a warning message if the terminal size is greater than 80x25. * Server - Lockfile: gems-server now uses a lock file to prevent nested execution. * Client - New option: '-i' to ignore server terminal size. * Bugfixes: gems-server freezed if a connection was attempted using telnet. This is now fixed. Several other minor bugs were also fixed. 2004/03/05 - Version 1.0 * Code: The main code was rewritten, for both client and server, so that they work more efficiently now, and also the server stores some important information about the connected clients (e.g.: their IP address). * Protocol: server and client now use our own communication protocol. As a consequence, there is no compatibility with previous versions. Bad luck :P * Terminal size: In order to solve the problems caused by the different sizes (rows and columns) of server and client terminals, the client will now close the connection if its terminal size is smaller than the server's. Therefore, the server should be run in a terminal not larger than 80x25, to be sure that clients will have no problems. * Server - script: Now the server runs "script -fq" automatically. This allows simply running "gems-server" and getting a shell session opened to be transmitted to all the clients. This behavior can be avoided with the parameter '-noscript', that will make the server take the data to be transmitted directly from standard input, as it used to be before. This behavior is not permanent; in the future gems-server is planned to emulate script's functionality, without actually executing it. * Server - logging: The option '-log' allows log messages to be written either to an arbitrary file, stderr, or to the syslog daemon. * Server - Connections: The maximum number of allowed connections can be specified with the option '-maxconn'. The option '-wait M' makes the server wait for the connection of M clients before starting the data transmission. * Client - Keys: Echoing is now disabled in the terminal, which means that anything typed in the keyboard is not shown on screen. Furthermore some features were assigned to certain keys; for instance 'q' = quit. If a key with no assigned function is typed, a warning beep is emmited. * Client - beep filtering: The 'a' key allows to enable/disable "beep filtering" at run-time. When this feature is active, the client ignores any alarm character ('\a') sent by the server so that it does not play. This is especially useful when many clients are running in the same physical room, to avoid the annoying simultaneous beeps. * Help: In both client and server, the option '-h' shows a brief help message. Also, the option '-v' shows the program version. * Manuals: The manual pages gems-server(1) and gems-client(1) were written. 2003/12/29 - Version 0.2.0 * Bugfix: "address already in use". Now the server can be run immediately after being closed, without having to wait some minutes. 2003/10/10 - Version 0.1.0 First functional version. The server takes the data to transmit directly from the standard input, therefore, in order to start a script-like shell session, it is necessary to run something like: $ script -fq >(gems-server) gems-1.1.1/TODO0000644000175000017500000000031610123160002011634 0ustar maxymaxy* Server: Implement the script functionality (instead of calling it as a sub-process). * Implement some kind of security. * Change the GForge project name from gemsd to gems. * Use autoconf + automake. gems-1.1.1/po/0000755000175000017500000000000011306774376011617 5ustar maxymaxygems-1.1.1/po/gems.pot0000644000175000017500000001313410123177501013257 0ustar maxymaxy# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2004-09-18 20:36-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../client/source/gems-client.c:386 ../server/source/main.c:368 #, c-format msgid "" "\n" "%s terminated.\n" msgstr "" #: ../server/source/main.c:249 #, c-format msgid " %s\t\t\tShow this help message.\n" msgstr "" #: ../server/source/main.c:250 #, c-format msgid " %s\t\t\tShow version information.\n" msgstr "" #: ../server/source/main.c:246 #, c-format msgid " %s\t\tDo not run script(1).\n" msgstr "" #: ../client/source/gems-client.c:306 #, c-format msgid " %s\t\tIgnore server's terminal size.\n" msgstr "" #: ../client/source/gems-client.c:307 #, c-format msgid " %s\t\tShow this help message.\n" msgstr "" #: ../client/source/gems-client.c:308 #, c-format msgid " %s\t\tShow version information.\n" msgstr "" #: ../server/source/main.c:236 #, c-format msgid " %s IP\t\tIP where connections will be accepted.\n" msgstr "" #: ../server/source/main.c:243 #, c-format msgid "" " %s LOGFILE\t\tSpecify the log destination. The possible values are:\n" "\t\t\tthe name of a file, `syslog', or `stderr'.\n" msgstr "" #: ../server/source/main.c:241 #, c-format msgid "" " %s M\t\tWait for the connection of M clients before starting\n" "\t\t\ttransmission. Default: %d.\n" msgstr "" #: ../server/source/main.c:239 #, c-format msgid " %s N\t\tAccept N simultaneous clients. Default: %d.\n" msgstr "" #: ../server/source/main.c:237 #, c-format msgid " %s PORT\t\tTCP port. Default: %d.\n" msgstr "" #: ../server/source/main.c:247 #, c-format msgid "" " %s SCRIPT\tSpecify the location of script.\n" "\t\t\tDefault: %s.\n" msgstr "" #: ../client/source/gems-client.c:303 #, c-format msgid " HOST\t\tServer's IP address or domain.\n" msgstr "" #: ../client/source/gems-client.c:304 #, c-format msgid " PORT\t\tServer's TCP port. Default: %d.\n" msgstr "" #: ../common/version.h:49 #, c-format msgid "" "%s %s\n" "Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari. Under GPL " "license.\n" msgstr "" #: ../server/source/main.c:336 #, c-format msgid "%s initialized.\n" msgstr "" #: ../common/protocol.c:296 #, c-format msgid "%s: Terminal size too small (minimum required: %dx%d).\n" msgstr "" #: ../server/source/gems-server.c:588 #, c-format msgid "%s: warning: terminal size is greater than %dx%d.\n" msgstr "" #: ../server/source/log.c:60 msgid "Aaargh! Internal error -- sorry." msgstr "" #: ../server/source/log.c:61 #, c-format msgid "Accepting connections on port %d." msgstr "" #: ../client/source/gems-client.c:302 #, c-format msgid "Arguments:\n" msgstr "" #: ../server/source/log.c:58 #, c-format msgid "" "Cannot create lock file: %s\n" " Maybe another instance of %s is running?" msgstr "" #: ../server/source/log.c:72 msgid "Client disconnected: terminal size too small." msgstr "" #: ../server/source/log.c:64 #, c-format msgid "Client disconnection -- IP: %s." msgstr "" #: ../server/source/log.c:65 msgid "Closing all connections..." msgstr "" #: ../client/source/gems-client.c:442 msgid "Connection established -- Press 'q' to exit." msgstr "" #: ../client/source/gems-client.c:208 msgid "Connection refused by server - Incompatible versions." msgstr "" #: ../server/source/log.c:66 msgid "Connection refused: maximum number of connections reached." msgstr "" #: ../server/source/log.c:70 msgid "Connection refused: obsolete client version." msgstr "" #: ../server/source/log.c:68 msgid "Connection refused: obsolete server version." msgstr "" #: ../client/source/gems-client.c:260 msgid "End of transmission." msgstr "" #: ../client/source/gems-client.c:133 #, c-format msgid "Host %s not found." msgstr "" #: ../client/source/gems-client.c:197 msgid "Incompatible server version." msgstr "" #: ../server/source/log.c:53 msgid "Invalid TCP port." msgstr "" #: ../server/source/log.c:51 #, c-format msgid "Invalid option: %s." msgstr "" #: ../server/source/log.c:56 msgid "Invalid value for 'wait' option." msgstr "" #: ../server/source/log.c:54 msgid "Invalid value for maximum number of connections." msgstr "" #: ../server/source/log.c:62 #, c-format msgid "New connection -- Client %i -- IP: %s." msgstr "" #: ../server/source/log.c:57 msgid "Not enough memory." msgstr "" #: ../server/source/log.c:52 #, c-format msgid "Option %s requires an argument." msgstr "" #: ../server/source/main.c:235 #, c-format msgid "Options:\n" msgstr "" #: ../server/source/log.c:74 #, c-format msgid "Running %s..." msgstr "" #: ../client/source/gems-client.c:191 msgid "Server does not accept any more connections." msgstr "" #: ../client/source/gems-client.c:218 #, c-format msgid "Terminal size too small (minimum required: %dx%d)." msgstr "" #: ../server/source/main.c:227 #, c-format msgid "Try `%s %s' for more information.\n" msgstr "" #: ../server/source/main.c:223 #, c-format msgid "" "Usage: %s [%s IP] [%s PORT] [%s N] [%s M] [%s LOGFILE]\n" "\t\t [%s] [%s SCRIPT] [%s] [%s]\n" msgstr "" #: ../client/source/gems-client.c:291 ../client/source/gems-client.c:299 #, c-format msgid "Usage: %s [%s] HOST [PORT]\n" msgstr "" #: ../server/source/main.c:233 #, c-format msgid "Usage: %s [options]\n" msgstr "" #: ../server/source/log.c:75 msgid "script terminated." msgstr "" gems-1.1.1/po/Makefile0000644000175000017500000000072010114471501013232 0ustar maxymaxyLANGUAGES=es POT_FILE=gems.pot MO_FILES=$(LANGUAGES:%=%.mo) all: $(MO_FILES) $(POT_FILE): $(shell find .. -name '*.[ch]') xgettext -k_ -kN_ -o $@ $^ -d gems -s $(MO_FILES): %.mo: %.po msgfmt -c -o $@ $< remove clean: rm -f $(MO_FILES) install: $(MO_FILES) for i in $(LANGUAGES); do \ install -m644 -D $$i.mo $(LOCALEPATH)/$$i/LC_MESSAGES/gems.mo; \ done uninstall: for i in $(LANGUAGES); do \ rm -fv $(LOCALEPATH)/$$i/LC_MESSAGES/gems.mo; \ done gems-1.1.1/po/es.po0000644000175000017500000001763010123177501012554 0ustar maxymaxy# Spanish translation for gems. # Copyright (C) Diego Essaya, Emiliano Castagnari # This file is distributed under the same license as the gems package. # msgid "" msgstr "" "Project-Id-Version: gems 1.1\n" "Report-Msgid-Bugs-To: dessaya@fi.uba.ar\n" "POT-Creation-Date: 2004-09-18 20:36-0400\n" "PO-Revision-Date: 2004-09-18 20:42-0400\n" "Last-Translator: Enrique Calot \n" "Language-Team: Lugfi \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: ../client/source/gems-client.c:386 ../server/source/main.c:368 #, c-format msgid "" "\n" "%s terminated.\n" msgstr "" "\n" "%s terminado.\n" #: ../server/source/main.c:249 #, c-format msgid " %s\t\t\tShow this help message.\n" msgstr " %s\t\t\tMostrar este mensaje de ayuda.\n" #: ../server/source/main.c:250 #, c-format msgid " %s\t\t\tShow version information.\n" msgstr " %s\t\t\tMostrar información acerca de la versión.\n" #: ../server/source/main.c:246 #, c-format msgid " %s\t\tDo not run script(1).\n" msgstr " %s\t\tNo ejecutar script(1).\n" #: ../client/source/gems-client.c:306 #, c-format msgid " %s\t\tIgnore server's terminal size.\n" msgstr " %s\t\tIgnorar tamaño de terminal del servidor.\n" #: ../client/source/gems-client.c:307 #, c-format msgid " %s\t\tShow this help message.\n" msgstr " %s\t\tMostrar este mensaje de ayuda.\n" #: ../client/source/gems-client.c:308 #, c-format msgid " %s\t\tShow version information.\n" msgstr " %s\t\tMostrar información acerca de la versión.\n" #: ../server/source/main.c:236 #, c-format msgid " %s IP\t\tIP where connections will be accepted.\n" msgstr " %s IP\t\tIP donde las conexiones serán aceptadas.\n" #: ../server/source/main.c:243 #, c-format msgid "" " %s LOGFILE\t\tSpecify the log destination. The possible values are:\n" "\t\t\tthe name of a file, `syslog', or `stderr'.\n" msgstr "" " %s LOGFILE\t\tEspecificar el destino de los mensajes de diagnóstico.\n" "\t\t\tLos posibles valores son: el nombre de un archivo,\n" "\t\t\t`syslog' o `stderr'.\n" #: ../server/source/main.c:241 #, c-format msgid "" " %s M\t\tWait for the connection of M clients before starting\n" "\t\t\ttransmission. Default: %d.\n" msgstr "" " %s M\t\tEsperar la conexión de M clientes antes de comenzar\n" "\t\t\tla transmisión. Por omisión: %d.\n" #: ../server/source/main.c:239 #, c-format msgid " %s N\t\tAccept N simultaneous clients. Default: %d.\n" msgstr " %s N\t\tAceptar N clientes simultáneos. Por omisión: %d\n" #: ../server/source/main.c:237 #, c-format msgid " %s PORT\t\tTCP port. Default: %d.\n" msgstr " %s PUERTO\t\tPuerto TCP. Por omisión: %d.\n" #: ../server/source/main.c:247 #, c-format msgid "" " %s SCRIPT\tSpecify the location of script.\n" "\t\t\tDefault: %s.\n" msgstr "" " %s SCRIPT\tEspecificar la ubicación de script.\n" "\t\t\tPor omisión: %s.\n" #: ../client/source/gems-client.c:303 #, c-format msgid " HOST\t\tServer's IP address or domain.\n" msgstr " HOST\t\tDirección IP o dominio del servidor.\n" #: ../client/source/gems-client.c:304 #, c-format msgid " PORT\t\tServer's TCP port. Default: %d.\n" msgstr " PORT\t\tPuerto TCP del servidor. Por omisión: %d.\n" #: ../common/version.h:49 #, c-format msgid "" "%s %s\n" "Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari. Under GPL " "license.\n" msgstr "" "%s %s\n" "Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari. Bajo licencia " "GPL.\n" #: ../server/source/main.c:336 #, c-format msgid "%s initialized.\n" msgstr "%s inicializado.\n" #: ../common/protocol.c:296 #, c-format msgid "%s: Terminal size too small (minimum required: %dx%d).\n" msgstr "%s: Tamaño de la terminal insuficiente (mínimo requerido: %dx%d).\n" #: ../server/source/gems-server.c:588 #, c-format msgid "%s: warning: terminal size is greater than %dx%d.\n" msgstr "%s: aviso: el tamaño de la terminal es mayor a %dx%d.\n" #: ../server/source/log.c:60 msgid "Aaargh! Internal error -- sorry." msgstr "Ayayay! Error interno -- perdón." #: ../server/source/log.c:61 #, c-format msgid "Accepting connections on port %d." msgstr "Aceptando conexiones en el puerto %d." #: ../client/source/gems-client.c:302 #, c-format msgid "Arguments:\n" msgstr "Argumentos:\n" #: ../server/source/log.c:58 #, c-format msgid "" "Cannot create lock file: %s\n" " Maybe another instance of %s is running?" msgstr "" "Imposible crear el archivo de lock: %s\n" " ¿Tal vez hay otra instancia de %s corriendo?" #: ../server/source/log.c:72 msgid "Client disconnected: terminal size too small." msgstr "Cliente desconectado: tamaño de terminal insuficiente." #: ../server/source/log.c:64 #, c-format msgid "Client disconnection -- IP: %s." msgstr "Cliente desconectado -- IP: %s." #: ../server/source/log.c:65 msgid "Closing all connections..." msgstr "Cerrando todas las conexiones..." #: ../client/source/gems-client.c:442 msgid "Connection established -- Press 'q' to exit." msgstr "Conexión establecida. -- Presione 'q' para terminar." #: ../client/source/gems-client.c:208 msgid "Connection refused by server - Incompatible versions." msgstr "Conexión rechazada por el servidor - Versiones incompatibles." #: ../server/source/log.c:66 msgid "Connection refused: maximum number of connections reached." msgstr "Conexión rechazada: máxima cantidad de conexiones alcanzada." #: ../server/source/log.c:70 msgid "Connection refused: obsolete client version." msgstr "Conexión rechazada por el servidor - Versiones incompatibles." #: ../server/source/log.c:68 msgid "Connection refused: obsolete server version." msgstr "Conexión rechazada por el servidor - Versiones incompatibles." #: ../client/source/gems-client.c:260 msgid "End of transmission." msgstr "Fin de la transmisión." #: ../client/source/gems-client.c:133 #, c-format msgid "Host %s not found." msgstr "El host %s no fue encontrado." #: ../client/source/gems-client.c:197 msgid "Incompatible server version." msgstr "Versión de servidor incompatible." #: ../server/source/log.c:53 msgid "Invalid TCP port." msgstr "Puerto TCP inválido." #: ../server/source/log.c:51 #, c-format msgid "Invalid option: %s." msgstr "Opción inválida: %s." #: ../server/source/log.c:56 msgid "Invalid value for 'wait' option." msgstr "Valor inválido para la opción 'wait'." #: ../server/source/log.c:54 msgid "Invalid value for maximum number of connections." msgstr "Valor inválido para la cantidad máxima de conexiones." #: ../server/source/log.c:62 #, c-format msgid "New connection -- Client %i -- IP: %s." msgstr "Nueva conexión -- Cliente %i -- IP: %s." #: ../server/source/log.c:57 msgid "Not enough memory." msgstr "Memoria insuficiente." #: ../server/source/log.c:52 #, c-format msgid "Option %s requires an argument." msgstr "La opción %s requiere un argumento." #: ../server/source/main.c:235 #, c-format msgid "Options:\n" msgstr "Opciones:\n" #: ../server/source/log.c:74 #, c-format msgid "Running %s..." msgstr "Ejecutando %s..." #: ../client/source/gems-client.c:191 msgid "Server does not accept any more connections." msgstr "El servidor ya no acepta más conexiones." #: ../client/source/gems-client.c:218 #, c-format msgid "Terminal size too small (minimum required: %dx%d)." msgstr "Tamaño de la terminal insuficiente (mínimo requerido: %dx%d)." #: ../server/source/main.c:227 #, c-format msgid "Try `%s %s' for more information.\n" msgstr "Pruebe `%s %s' para más información.\n" #: ../server/source/main.c:223 #, c-format msgid "" "Usage: %s [%s IP] [%s PORT] [%s N] [%s M] [%s LOGFILE]\n" "\t\t [%s] [%s SCRIPT] [%s] [%s]\n" msgstr "" "Modo de uso: %s [%s IP] [%s PORT] [%s N] [%s M]\n" "\t\t[%s LOGFILE] [%s] [%s SCRIPT] [%s] [%s]\n" #: ../client/source/gems-client.c:291 ../client/source/gems-client.c:299 #, c-format msgid "Usage: %s [%s] HOST [PORT]\n" msgstr "Modo de uso: %s [%s] HOST [PUERTO]\n" #: ../server/source/main.c:233 #, c-format msgid "Usage: %s [options]\n" msgstr "Modo de uso: %s [opciones]\n" #: ../server/source/log.c:75 msgid "script terminated." msgstr "script terminado." gems-1.1.1/common/0000755000175000017500000000000011306774376012471 5ustar maxymaxygems-1.1.1/common/protocol.c0000644000175000017500000002014710114506231014455 0ustar maxymaxy/* ============================================================================ * << File: protocol.c >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 31/12/03 * * Description: * Functions related to message I/O, using the gems protocol specification * (see doc/Protocol). * * Also, some global buffers are declared here, which can be used in other * modules to save I/O data. This is to improve general efficiency, because * it is not necessary to declare a different buffer for each function, and * to copy data between buffers. All functions use the same I/O buffer. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* perror */ #include /* recv() y send() */ #include /* recv() y send() */ #include /* struct winsize */ #include /* int errno */ #include /* memcpy() */ #include "protocol.h" #include "common.h" /* I/O buffers: One buffer to hold an entire message (msg_buffer), and pointers * to the header and message data. */ char gems_msg_buffer[MSG_HEADER_SIZE + MSG_MAX_DATA_SIZE]; char *gems_header_buffer = gems_msg_buffer; char *gems_data_buffer = gems_msg_buffer + MSG_HEADER_SIZE; /* Pointers to the header fields: */ gems_msg_type_t *gems_type_buffer = (gems_msg_type_t *) gems_msg_buffer; gems_msg_len_t *gems_len_buffer = (gems_msg_len_t *) (gems_msg_buffer + MSG_TYPE_SIZE); /* Buffer to hold version string: Let's suppose 20 characters is enough. */ #define VERSION_BUFFER_LEN 20 char gems_version_buffer[VERSION_BUFFER_LEN]; /* get_stream() is a wrapper for recv(). * * Reads data from the specified fd until all expected bytes have arrived, * writing them into the buffer. * Returns FALSE if a disconnection occurs. */ fbool_t get_stream(int fd, char *buffer, int bytes) { int n; do { n = recv(fd, buffer, bytes, MSG_WAITALL); } while ((n == -1) && (errno == EINTR)); /* EINTR means that a signal has been caught and handled. We don't mind * about this, so continue reading. */ if (n == bytes) return TRUE; if (n == 0) return FALSE; /* Disconnection. */ /* Error: */ perror("recv"); return FAIL; } /* get_msg() * * Reads data from the specified fd until an entire message has arrived. * Returns FALSE if a disconnection occurs, or if the message received is * DISCONNECT. * NOTE: The data (msg->data) is not copied into a new array; therefore * succesive calls to this function will overwrite that section of memory. */ fbool_t get_msg(int fd, t_gems_msg * msg) { fbool_t r; /* Receive message header: */ if ((r = get_stream(fd, gems_header_buffer, MSG_HEADER_SIZE)) != TRUE) return r; /* Get message type: */ msg->type = *gems_type_buffer; /* Get data size: */ msg->len = *gems_len_buffer; /* Get data: */ if (msg->len > 0) { if ((r = get_stream(fd, gems_data_buffer, msg->len)) != TRUE) return r; msg->data = gems_data_buffer; } if (msg->type == GEMS_DISCONNECT) return FALSE; return TRUE; } /* send_msg() * * Sends the message, following the protocol specifications. The global buffer * gems_data_buffer can be used for msg->data. This is encouraged, because * otherwise an extra memcpy() has to be done. */ fbool_t send_msg(int fd, t_gems_msg * msg) { /* Armo el paquete: */ *gems_type_buffer = msg->type; *gems_len_buffer = msg->len; if (msg->len > 0) if (msg->data != gems_data_buffer) memcpy(gems_data_buffer, msg->data, msg->len); /* Envio el mensaje: */ if (send(fd, gems_msg_buffer, MSG_HEADER_SIZE + msg->len, 0) == -1) { perror("send"); return FAIL; } return TRUE; } /* send_version() * * Sends the protocol version. */ fbool_t send_version(int fd) { t_gems_msg msg; msg.type = GEMS_VERSION; msg.len = strlen(PROTOCOL_VERSION); msg.data = PROTOCOL_VERSION; return send_msg(fd, &msg); } /* get_peer_version() * * Receives the remote protocol version. Returns a zero-terminated string * in *version. * If DISCONNECT is received instead of VERSION, or if the connection is * closed, this function returns FALSE. * NOTE: Succesive calls to this function will overwrite the string buffer. */ fbool_t get_peer_version(int fd, char **version) { t_gems_msg msg; fbool_t r = TRUE; if ((r = get_msg(fd, &msg)) != TRUE) return r; if (msg.type != GEMS_VERSION) return FAIL; if (msg.len > VERSION_BUFFER_LEN - 1) return FAIL; memcpy(gems_version_buffer, msg.data, msg.len); gems_version_buffer[msg.len] = '\0'; *version = gems_version_buffer; return TRUE; } /* send_ack() * * Sends an ACK message. */ fbool_t send_ack(int fd) { t_gems_msg msg; msg.type = GEMS_ACK; msg.len = 0; return send_msg(fd, &msg); } /* get_ack() * * Waits until an ACK message is received. Returns FALSE if a disconnection * occurs. */ fbool_t get_ack(int fd) { t_gems_msg msg; fbool_t r; if ((r = get_msg(fd, &msg)) != TRUE) return r; if (msg.type != GEMS_ACK) return FAIL; return TRUE; } /* send_disconnect() * * Sends a DISCONNECT message. */ fbool_t send_disconnect(int fd) { t_gems_msg msg; msg.type = GEMS_DISCONNECT; msg.len = 0; return send_msg(fd, &msg); } /* send_winsize() * * Sends the local terminal size. */ fbool_t send_winsize(int fd) { struct winsize win; t_gems_msg msg; int data[2]; /* Obtengo el tamaño de la ventana: */ ioctl(0, TIOCGWINSZ, (char *) &win); data[0] = (int) win.ws_col; data[1] = (int) win.ws_row; msg.type = GEMS_WINSIZE; msg.len = 2 * sizeof(int); msg.data = (char *) data; return send_msg(fd, &msg); } /* compare_winsize() * * Compares the remote and local terminal sizes. Returns TRUE if local size * is greater. Otherwise prints appropriate error message and returns FALSE. */ fbool_t compare_winsize(int remote_cols, int remote_rows, char *appname) { struct winsize win; /* Obtengo el tamaño de la ventana local: */ ioctl(0, TIOCGWINSZ, (char *) &win); if ((win.ws_col < remote_cols) || (win.ws_row < remote_rows)) { if (appname) { fputc('\n', stderr); fprintf(stderr, _("%s: Terminal size too small " "(minimum required: %dx%d).\n"), appname, remote_cols, remote_rows); } return FALSE; } return TRUE; } /* get_cols_rows() * * Extracts the columns and rows values from a GEMS_WINSIZE message. */ fbool_t get_cols_rows(const t_gems_msg * msg, int *cols, int *rows) { if ((msg->type != GEMS_WINSIZE) || (msg->len != 2 * sizeof(int))) return FAIL; *cols = ((int *) msg->data)[0]; *rows = ((int *) msg->data)[1]; return TRUE; } /* get_winsize() * * Receives the remote terminal size, compares it with the local size and * returns TRUE if local size is greater. Returns FALSE if a disconnection * occurs. */ fbool_t get_winsize(int fd, int *cols, int *rows) { t_gems_msg msg; fbool_t r; if ((r = get_msg(fd, &msg)) != TRUE) return r; if (msg.type != GEMS_WINSIZE) return FAIL; if ((r = get_cols_rows(&msg, cols, rows)) != TRUE) return r; return compare_winsize(*cols, *rows, NULL); } /* vim: set ts=4 sw=4: */ gems-1.1.1/common/version.h0000644000175000017500000000363010114506231014304 0ustar maxymaxy/* ============================================================================ * << File: version.h >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Date: 23/01/04 * * Description: * Project name and version related declarations. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _VERSION_H_ #define _VERSION_H_ #include /* printf() */ #include "common.h" #define PROJECT_NAME "gems" #define PROJECT_VERSION "1.1" #define PROTOCOL_VERSION "1.0" /* Communication protocol version */ /* Version message for both server and client: */ #define PRINT_VERSION(appname) \ { \ fprintf(stdout, _("%s %s\n" \ "Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari. " \ "Under GPL license.\n"), appname, PROJECT_VERSION); \ } #endif gems-1.1.1/common/Changelog0000644000175000017500000000512110107032174014257 0ustar maxymaxy2004/04/10 - Diego Essaya: * NLS (gettext) support and English translation. 2004/02/29 - Diego Essaya * Documentation: Wrote man pages for gems-server(1) and gems-client(1) in doc/. 2004/02/26 - Diego Essaya * status_t: Changed the osret_t definition to status_t: enum {EXIT_OK = 0, EXIT_FAIL, CONTINUE} status_t; Changed some functions in server and client to use variables of this type for flow control. 2004/02/25 - Diego Essaya * Protocol: Change in the order in which the versions are interchanged. Now the server sends its version first. This allows the client to know that the connection was refused if the server does not send its version. See doc/Protocol. 2004/02/23 - Diego Essaya Changed the name gems_msg_t to t_gems_msg. 2004/02/22 - Diego Essaya Changed the module name 'signal' to 'sighandlers', because there is a 'signal.h' in the standard C include directory. 2004/02/16 - Diego Essaya * General code cleanup: Every c/h file was adapted to the specs in CodingStandards. I used the indent(1) tool to acomplish this. The only problem were the comments at the right of code, because indent uses tabs instead of spaces. Indentation was set to 4 spaces. * signal.c / signal.h: Functions for signal handling, common to the server and client. 2004/02/14 - Emiliano Castagnari * version.h: Declarations and macros related to the project version. 2004/01/09 - Diego Essaya Protocol modified: if the server denies connection for X cause, the client now has a way to know this. See doc/Protocol. 2004/01/03 - Diego Essaya Code cleanup and optimization. To save memory and execution time, I use one single I/O buffer for the whole program. This buffer is declared globally as msg_buffer[BUFSIZE] in protocol.c. 2004/01/02 - Diego Essaya - Code cleanup. The function parse_msg() was a pain in the ass (I don't know what I was thinking of when I wrote it :P ), so I changed it to get_msg(), which is much more simple and useful. - Implemented functions for sending & receiving the terminal size: get_winsize(), send_winsize() and compare_winsize(). 2003/12/31 - Diego Essaya Started to implement the communication protocol. Files added: * protocol.h and protocol.c: Functions and declarations for message I/O following the protocol defined in doc/Protocol. o y seguramente defectuoso, pero es algo como para empezar... 2003/12/29 - Diego Essaya Release: 0.2.0 Files in this directory contain declarations and code common to both server and client. By now, the only file created is: * common.h: common declarations, as the default TCP port and project version. gems-1.1.1/common/sighandlers.h0000644000175000017500000000327410114506231015126 0ustar maxymaxy/* ============================================================================ * << File: sighandlers.h >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 16/02/04 * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ /* Global variables, which notify the arrival of signals: */ extern volatile int winsize_changed; extern volatile int sigint_raised; extern volatile int sigchld_raised; void install_sighandlers(); void sigint_handler(int n); void sigwinch_handler(int n); void sigchld_handler(int n); gems-1.1.1/common/protocol.h0000644000175000017500000000633110114506231014461 0ustar maxymaxy/* ============================================================================ * << File: protocol.h >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 31/12/03 * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _PROTOCOL_H_ #define _PROTOCOL_H_ #include "common.h" /* Socket input/output buffer size: */ #define BUFSIZE 1024 /* TCP ports: */ #define DEFAULT_TCP_PORT 6666 #define PORT_MIN 1024 #define PORT_MAX 65535 /* Port range verification * Ports lower than PORT_MIN are reserved by IANA */ #define port_range(p) ((p >= PORT_MIN) && (p <= PORT_MAX)) /* Message field sizes, in bytes: */ #define MSG_TYPE_SIZE 1 #define MSG_DATA_LEN_SIZE sizeof(int) /* should be 4 */ #define MSG_MAX_DATA_SIZE 1024 /* Message header total size: */ #define MSG_HEADER_SIZE ((MSG_TYPE_SIZE) + (MSG_DATA_LEN_SIZE)) /* Types of messages, and their respective codes: */ typedef char gems_msg_type_t; #define GEMS_DATA ((gems_msg_type_t)0x00) #define GEMS_WINSIZE ((gems_msg_type_t)0x01) #define GEMS_VERSION ((gems_msg_type_t)0x02) #define GEMS_ACK ((gems_msg_type_t)0x03) #define GEMS_DISCONNECT ((gems_msg_type_t)0x04) /* The size of message data is stored in an int (4 bytes): */ typedef unsigned int gems_msg_len_t; /* Message structure: */ typedef struct { gems_msg_type_t type; gems_msg_len_t len; char *data; } t_gems_msg; /* I/O buffers, declared here to be used in other modules: */ extern char gems_msg_buffer[]; extern char *gems_header_buffer; extern char *gems_data_buffer; fbool_t get_stream(int fd, char *buffer, int bytes); fbool_t get_msg(int fd, t_gems_msg * msg); fbool_t send_msg(int fd, t_gems_msg * msg); fbool_t send_version(int fd); fbool_t get_peer_version(int fd, char **version); fbool_t send_ack(int fd); fbool_t get_ack(int fd); fbool_t send_disconnect(int fd); fbool_t send_winsize(int fd); fbool_t compare_winsize(int remote_cols, int remote_rows, char *appname); fbool_t get_cols_rows(const t_gems_msg * msg, int *cols, int *rows); fbool_t get_winsize(int fd, int *cols, int *rows); #endif gems-1.1.1/common/sighandlers.c0000644000175000017500000000422410114506231015115 0ustar maxymaxy/* ============================================================================ * << File: sighandlers.c >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 16/02/04 * * Description: * Signal handling functions. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #include /* signal() */ #include "sighandlers.h" /* Global variables which notify the arrival of signals. These are the only * variables modified by the signal handlers: */ volatile int winsize_changed = 0; volatile int sigint_raised = 0; volatile int sigchld_raised = 0; void install_sighandlers() { signal(SIGINT, sigint_handler); signal(SIGWINCH, sigwinch_handler); signal(SIGCHLD, sigchld_handler); } void sigint_handler(int n) { sigint_raised = 1; signal(n, sigint_handler); } void sigwinch_handler(int n) { winsize_changed = 1; signal(n, sigwinch_handler); } void sigchld_handler(int n) { sigchld_raised++; signal(n, sigchld_handler); } /* vim: set ts=4 sw=4: */ gems-1.1.1/common/Makefile0000644000175000017500000000050110123201424014074 0ustar maxymaxyTARGETS= protocol.o sighandlers.o .PHONY: all clean remove install uninstall all: $(TARGETS) protocol.o : protocol.c protocol.h common.h version.h $(CC) $(CFLAGS) $(CDEFINES) -c $< sighandlers.o : sighandlers.c sighandlers.h $(CC) $(CFLAGS) $(CDEFINES) -c $< clean remove: rm -f $(TARGETS) install: uninstall: gems-1.1.1/common/common.h0000644000175000017500000000407510114506231014113 0ustar maxymaxy/* ============================================================================ * << File: common.h >> * ------------------------- * Authors: Emiliano Castagnari (aka Torian) * Diego Essaya * Date: 29/12/03 * * Description: * Declarations common to all project modules. * ============================================================================ * ============================================================================ * * ChangeLog: View Changelog * * ============================================================================ * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ============================================================================ */ #ifndef _COMMON_H_ #define _COMMON_H_ /* gettext() support: */ #include #define _(String) gettext(String) #define gettext_noop(String) (String) #define N_(String) gettext_noop(String) #include "version.h" /* Enable debugging output? */ /*#define DEBUG #define DEBUG_VERBOSE */ /* Not much to say about these: */ #define MAX(a, b) ((a) >= (b) ? (a) : (b)) #define MIN(a, b) ((a) <= (b) ? (a) : (b)) /* Tristate logic - for functions */ typedef enum { FAIL = -1, FALSE, TRUE } fbool_t; /* OS return values: */ typedef enum { EXIT_OK = 0, EXIT_FAIL, CONTINUE } status_t; #endif gems-1.1.1/README0000644000175000017500000000207610123054602012041 0ustar maxymaxyThe gems system =============== The gems system allows you to show a console session in various different terminals, in real time. It something like VNC in observer mode, but for the console :) . One possible use would be in a classroom with many computers, where all students can observe in their screens what the professor is doing in his console. To begin "sharing" a console session, just run the server with the command: $ gems-server From then on, the clients can be used to "observe" the session from any terminal with network access: $ gems-client server-address where "server-address" is the domain or IP address of the computer where the gems-server was started. Clients can connect and disconnect at any time. The gems system can also be used to transmit any kind of data (instead of a console session) to more than one computer in real time. Just use the option '-noscript', and gems-server will read data from standard input. See INSTALL to obtain installation instructions. You can get the latest release from: http://gforge.lug.fi.uba.ar/projects/gemsd gems-1.1.1/Makefile0000644000175000017500000000241210134612676012630 0ustar maxymaxySUBDIRS = common client server doc po # Destination root directory for install: DESTDIR = / # prefix: install architecture independent files in PREFIX: PREFIX = usr/local # exec-prefix: install architecture dependent files in EPREFIX: EPREFIX = $(PREFIX) # Path where to Install binaries: BINPATH = $(DESTDIR)/$(EPREFIX)/bin # Path where to install man pages: MANPATH = $(DESTDIR)/$(PREFIX)/man # Path where to install locale files: LOCALEPATH = $(DESTDIR)/$(PREFIX)/share/locale CC = gcc CFLAGS = -O2 CDEFINES = -DLOCALEDIR=\"$(LOCALEPATH)\" export CC CFLAGS CDEFINES BINPATH MANPATH LOCALEPATH .PHONY: all $(SUBDIRS) $(SUBDIRS:%=%-clean) $(SUBDIRS:%=%-remove) \ $(SUBDIRS:%=%-install) $(SUBDIRS:%=%-uninstall) \ clean remove install uninstall all: $(SUBDIRS) $(SUBDIRS): %: $(MAKE) -C $* $(SUBDIRS:%=%-clean): %-clean: $(MAKE) -C $* clean $(SUBDIRS:%=%-remove): %-remove: $(MAKE) -C $* remove $(SUBDIRS:%=%-install): %-install: $(MAKE) -C $* install $(SUBDIRS:%=%-uninstall): %-uninstall: $(MAKE) -C $* uninstall binary: $(SUBDIRS:%=%-binary) clean: $(SUBDIRS:%=%-clean) remove: $(SUBDIRS:%=%-remove) install: $(SUBDIRS:%=%-install) uninstall: $(SUBDIRS:%=%-uninstall) debian-package: dist/debian ln -sf dist/debian debian fakeroot debian/rules binary rm debian gems-1.1.1/dist/0000755000175000017500000000000011306774376012144 5ustar maxymaxygems-1.1.1/dist/debian/0000755000175000017500000000000011306774376013366 5ustar maxymaxygems-1.1.1/dist/debian/rules0000755000175000017500000000050410134612134014422 0ustar maxymaxy#!/usr/bin/make -f include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/makefile.mk DEB_MAKE_INSTALL_TARGET = install DESTDIR=$(DEB_DESTDIR) PREFIX=/usr MANPATH=$(DEB_DESTDIR)/usr/share/man # Store build information common-binary-post-install-arch common-binary-post-install-indep:: dh_buildinfo gems-1.1.1/dist/debian/copyright0000644000175000017500000000222610134612134015300 0ustar maxymaxyThis is gems, packaged for Debian GNU systems. Upstream authors: Diego Essaya and Emiliano Castagnari . Upstream source: http://gforge.lug.fi.uba.ar/projects/gemsd/ Copyright and licensing info: * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari * * This file is part of gems. * * gems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gems 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. On Debian Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. gems-1.1.1/dist/debian/changelog0000644000175000017500000000162610134612134015222 0ustar maxymaxygems (1.1-2) unstable; urgency=low * Merging Jonas Smedegaard changes to the Debian packaging: * First official release for Debian. * Tightened debian/copyright: + Add intro with upstream name and "Debian GNU systems". + Add location of upstream source. + Copy copyright and licensing info verbatim (from common/common.h). * Corrected speling error in long description. -- Maximiliano Curia Sat, 25 Sep 2004 20:09:42 -0300 gems (1.1-1) unstable; urgency=low * New release. -- Maximiliano Curia Fri, 24 Sep 2004 20:37:19 -0300 gems (1.0.20040929-1) unstable; urgency=low * New CVS update. * Lintian cleanups. -- Maximiliano Curia Sun, 19 Sep 2004 00:59:50 -0300 gems (1.0-1) unstable; urgency=low * Initial Release. -- Diego Essaya Thu, 4 Mar 2004 19:57:33 -0300 gems-1.1.1/dist/debian/docs0000644000175000017500000000001510134612134014212 0ustar maxymaxydoc/Protocol gems-1.1.1/dist/debian/watch0000644000175000017500000000022010134612134014366 0ustar maxymaxy# watch control file for uscan version=2 http://gforge.lug.fi.uba.ar/frs/?group_id=10 \ /frs/download.php/.*/gems-(.*)\.tar\.gz debian uupdate gems-1.1.1/dist/debian/control0000644000175000017500000000151210134612134014745 0ustar maxymaxySource: gems Section: misc Priority: optional Maintainer: Maximiliano Curia Build-Depends: cdbs, debhelper (>= 4.1), dh-buildinfo Standards-Version: 3.6.1 Package: gems Architecture: any Depends: ${shlibs:Depends} Replaces: gemsd Conflicts: gemsd Description: Shows a console session in several terminals The gems system is a client/server application that allows to show a single console session in different computers or terminals in real time. It can also be used to transmit any other kind of data to more than one computer at the same time, via a network connection. . It was designed as an educational tool for teachers that have to show in a computer lab how to do certain things with the console. Using the gems system, each student can observe in his/her own terminal everything the teacher does. gems-1.1.1/INSTALL0000644000175000017500000000032310114506231012203 0ustar maxymaxyCommands to compile and install gems from sources ================================================= To compile: $ make To install (as root): # make install To uninstall (as root): # make uninstall gems-1.1.1/COPYING0000644000175000017500000004307607742337602012242 0ustar maxymaxy GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.