/*-------------------------------------------------------------------------*/ /** @file rtd_i.c @author N. Devillard @date Jul 2001 @version $Revision: 2.4 $ @brief Interfaces to RTD. This module was freely adapted from the rtdRemote.c module written by Allan Brighton. Most differences are related to error handling, to make it compliant with eclipse. */ /*--------------------------------------------------------------------------*/ /* $Id: rtd_i.c,v 2.4 2002/02/15 12:54:17 ndevilla Exp $ $Author: ndevilla $ $Date: 2002/02/15 12:54:17 $ $Revision: 2.4 $ */ /*--------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtd_i.h" #include "static_sz.h" #include "comm.h" /*--------------------------------------------------------------------------- Private data ---------------------------------------------------------------------------*/ /* Private struct allocated to manage the client's connection */ static struct { int socket; /* socket connection with display */ int pid; /* pid of display on host */ char host[64]; /* hostname where display is running */ int port; /* port number to use on host */ } rtdRemoteInfo ; /* * -- I/O routines for network I/O taken from the book -- * "UNIX Network Programming" by W. Richard Stevens, */ /* * Read "n" bytes from a descriptor. * Use in place of read() when fd is a stream socket. */ static int readn(int fd, char* ptr, int nbytes) { int nleft, nread; nleft = nbytes; while (nleft > 0) { nread = read(fd, ptr, nleft); if (nread < 0) return(nread); /* error, return < 0 */ else if (nread == 0) break; /* EOF */ nleft -= nread; ptr += nread; } return(nbytes - nleft); /* return >= 0 */ } /* * Write "n" bytes to a descriptor. * Use in place of write() when fd is a stream socket. */ static int writen(int fd, char* ptr, unsigned long nbytes) { int nleft, nwritten; nleft = nbytes; while (nleft > 0) { nwritten = write(fd, ptr, nleft); if (nwritten <= 0) return(nwritten); /* error */ nleft -= nwritten; ptr += nwritten; } return(nbytes - nleft); } /* * Read the line one byte at a time, looking for the newline. We store * the newline in the buffer, then follow it with a null (the same as * fgets(3)). Not very efficient but usefull for sockets. * * Returns the number of characters up to, but not including, the null * (the same as strlen(3)) or < 0 upon errors. */ static int readline(int fd, char* ptr, int maxlen) { int n, rc; char c; for (n = 1; n < maxlen; n++) { if ( (rc = read(fd, &c, 1)) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) return(0); /* EOF, no data read */ else break; /* EOF, some data was read */ } else return(-1); /* error */ } *ptr = 0; return(n); } /* * write the given buffer to the given file followed by a newline */ static int writeline(int fd, char* ptr) { return writen(fd, ptr, strlen(ptr)) + writen(fd, "\n", 1); } /* -- Other routines -- */ /* * Read the ~/.rtd-remote file to get the pid, hostname and port number * of the RTD, if it is running (check that it is running...) */ static int getRtdInfo(void) { char filename[FILENAMESZ]; char* home = getenv("HOME"); FILE* f; char hostname[64]; char ret ; /* Get values from rtd status file */ sprintf(filename, "%s/.rtd-remote", (home ? home : "/tmp")); if ((f=fopen(filename, "r"))==NULL) { e_error("cannot open status file: %s, is rtd running?", filename); return -1 ; } ret = fscanf(f, "%d %s %d", &rtdRemoteInfo.pid, rtdRemoteInfo.host, &rtdRemoteInfo.port) ; fclose(f); if (ret!=3) { e_error("in Rtd status file: %s", filename); return -1 ; } /* See if process is still alive */ if (kill(rtdRemoteInfo.pid, 0) != 0) { e_error("rtd not running on this host?"); return -1 ; } /* See if host name matches */ if (gethostname(hostname, sizeof(hostname))==-1) { perror("gethostname"); e_error("rtd not running on this host?"); return -1 ; } if (strcmp(hostname, rtdRemoteInfo.host)) { e_error("rtd not running on this host?"); return -1 ; } return 0; } /* -- public interface -- */ /*-------------------------------------------------------------------------*/ /** @brief Open a connection to a running RTD. @param pid PID of the RTD session to connect to. @param host Name of the host on which RTD is running. @param port Port number to talk to. @return int 0 for success, 1 for error. This function opens a connection to a currently running RTD display. The pid, hostname and port number, if not specified (set to 0) are read from the file $HOME/.rtd-remote, which is created by RTD on startup (see rtdimage/src/RtdRemote.C in the RTD sources). The returned value is 0 for success, 1 for error. */ /*--------------------------------------------------------------------------*/ int rtdRemoteConnect(int pid, char* host, int port) { struct hostent *hp; /* pointer to host info */ struct sockaddr_in addr; /* for peer socket address */ int ret ; if (pid && host && port) { rtdRemoteInfo.pid = pid; strncpy(rtdRemoteInfo.host, host, sizeof(rtdRemoteInfo.host)); rtdRemoteInfo.port = port; } else if (getRtdInfo() != 0) { /* get pid, hostname, port from ~/.rtd-remote file */ return 1; } /* clear out address */ memset ((char *)&addr, 0, sizeof(struct sockaddr_in)); /* Set up the peer address to which we will connect. */ addr.sin_family = AF_INET; /* Get the host information for the rtd display */ hp = gethostbyname (rtdRemoteInfo.host); if (hp == NULL) { if (debug_active()) { perror("gethostbyname"); e_error("cannot get hostname: aborting connection to rtd"); } return 1 ; } addr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr; addr.sin_port = htons(rtdRemoteInfo.port); /* Create the socket. */ rtdRemoteInfo.socket = socket(AF_INET, SOCK_STREAM, 0); if (rtdRemoteInfo.socket == -1) { if (debug_active()) { perror("socket"); e_error("creating socket: aborting connection to rtd"); } return 1 ; } /* Try to connect to the remote Rtd display */ ret = connect(rtdRemoteInfo.socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) ; if (ret==-1) { if (debug_active()) { perror("connect"); e_error("connecting to rtd"); return 1 ; } } return 0; } /*-------------------------------------------------------------------------*/ /** @brief Disconnect from remote RTD. @return void This function disconnects the current process from the running RTD session it has been connected to, using rtdRemoteConnect(). */ /*--------------------------------------------------------------------------*/ /* * disconnect from the Rtd display */ void rtdRemoteDisconnect(void) { if (rtdRemoteInfo.socket != -1) { close(rtdRemoteInfo.socket); rtdRemoteInfo.socket = -1; } } /*-------------------------------------------------------------------------*/ /** @brief Read answer from the last command sent to RTD. @param sock Current RTD socket. @param result Pointer to modify to store result string. @return int RTD status, -1 if error occurred. Read the socket with the answer from the last command sent to the remote RTD display and return the command's status. The command's result is returned in the 'result' argument, which points to local or static storage. The format of the message read from the socket is: @verbatim status length\n msg[length]\n @endverbatim where status is 0 (Ok) or 1 and length is the length of the result that follows. */ /*--------------------------------------------------------------------------*/ int rtdRemoteGetResult(int sock, char** result) { static char buf[ASCIILINESZ]; /* use to hold results */ static char* rbuf = buf; /* may be allocated with malloc if needed */ static int rbufsize = sizeof(buf) - 1; int status; /* return status of command */ int length; /* length of result */ if (result) *result = rbuf; buf[0] = '\0'; /* default to empty result */ if (readline(sock, buf, sizeof(buf)) <= 0) { e_error("reading result status from rtdimage"); return -1 ; } if (sscanf(buf, "%d %d", &status, &length) != 2) { e_error("unknown result from rtdimage"); return -1 ; } if (length == 0) { return status; /* empty result */ } if (length < 0) { e_error("bad length received from display application"); return -1 ; } if (length >= rbufsize) { /* use static storage or malloc ? */ if (rbufsize != sizeof(buf)) { free(rbuf); } rbuf = (char*)malloc(rbufsize=length+10); } if (result) { *result = rbuf; } if (readn(sock, rbuf, length) != length) { e_error("reading result from rtdimage"); return -1 ; } rbuf[length] = '\0'; return status; } /*-------------------------------------------------------------------------*/ /** @brief Send a command to a remote RTD display. @param cmd Command to send to RTD display. @return int 0 if Ok, 1 otherwise. Write the command to the RTD socket and return 0 if Ok, 1 otherwise. "cmd" should not contain a newline, it will be added here. */ /*--------------------------------------------------------------------------*/ int rtdRemoteSendOnly(char* cmd) { if (writeline(rtdRemoteInfo.socket, cmd) <= 0) { e_error("error sending command to RTD"); return -1 ; } return 0; } /*-------------------------------------------------------------------------*/ /** @brief Evaluate an RTD command and return the command status. @param cmd Command to send to the remote RTD session. @return int status of the command. Evaluate the given rtdimage subcommand in the remote rtd application and return the status of the command. The command syntax is the same as for the "rtdimage" widget (image type), except that the instance name is missing. Example: @code char* result; int status = rtdRemoteCmd("wcscenter", &result); if (status == 0) { if (sscanf(result, ...) ...) {...} ... } @endcode On success, "result" points to a char buffer containing the result of the command. The buffer is internal and should not be freed and will be overwritten in the next call to this routine. If the command could not be sent, result is set to a NULL pointer and an error status (1) is returned. */ /*--------------------------------------------------------------------------*/ int rtdRemoteSend(char* cmd, char** result) { if (rtdRemoteInfo.socket == -1) { e_error("no connection to the image display"); return 1 ; } if (rtdRemoteSendOnly(cmd) != 0) { return 1; } return rtdRemoteGetResult(rtdRemoteInfo.socket, result); } /* vim: set ts=4 et sw=4 tw=75 */