home *** CD-ROM | disk | FTP | other *** search
- /* $Id: tcpip.c,v 1.13 1994/08/05 21:50:22 mjl Exp $
- * $Log: tcpip.c,v $
- * Revision 1.13 1994/08/05 21:50:22 mjl
- * Fixed bug responsible for DP driver not working on a Cray. Works great
- * now, including distributed rendering. Added much debug code.
- *
- * Revision 1.12 1994/07/28 07:42:39 mjl
- * Fix for side-effect of defining caddr_t in plConfig.h.
- *
- * Revision 1.11 1994/07/26 21:08:47 mjl
- * Put in a user-suggested portability fix (lost track of who sent it).
- *
- * Revision 1.10 1994/07/25 06:04:21 mjl
- * Desuckified header inclusions, and added test for unistd.h before
- * including it.
- *
- * Revision 1.9 1994/07/22 22:20:59 mjl
- * Eliminated a gcc -Wall warning.
- *
- * Revision 1.8 1994/07/21 08:41:56 mjl
- * Introduced some casts to satisfy the IRIX compiler.
- *
- * Revision 1.7 1994/07/19 22:31:46 mjl
- * All device drivers: enabling macro renamed to PLD_<driver>, where <driver>
- * is xwin, ps, etc. See plDevs.h for more detail. All internal header file
- * inclusion changed to /not/ use a search path so that it will work better
- * with makedepend.
- *
- * Revision 1.6 1994/06/30 18:45:04 mjl
- * Minor changes to pass gcc -Wall without warnings and other cleaning up.
- *
- * Revision 1.5 1994/05/14 05:42:52 mjl
- * Fixed the call to pl_UnRead the header -- a bogus argument was causing
- * spurious packet transmission failures.
- *
- * Revision 1.4 1994/04/08 12:00:05 mjl
- * Function name changes to reduce namespace pollution. Robustness improved
- * in packet reader (changes stolen from Tcl-DP3.1 update).
- *
- * Revision 1.3 1994/02/07 23:00:43 mjl
- * Simplified and genericized pl_PacketReceive and pl_PacketSend functions so
- * that they work with FIFO's as well as sockets: data stream is now fully
- * packetized. Functions require a PLiodev struct to be passed in,
- * containing lots of useful information about the connection.
- *
- * Revision 1.2 1994/02/01 22:44:35 mjl
- * Changes to support distributed operation between machines with different
- * int/long sizes.
- *
- * Revision 1.1 1994/01/15 17:50:09 mjl
- * Added to hold primarily socket related code. The latter were taken from
- * the Tcl-DP distribution and modified to use binary i/o to/from the memory
- * buffer pointed to by pdfs->buffer (pdfs a PDFstrm *).
- */
-
- /*
- * tcpip.c
- * Maurice LeBrun
- * 6-Jan-94
- *
- * Functions to handle a variety of TPC-IP related chores, in particular
- * socket i/o for data transfer to the Tcl-DP driver. For the latter, the
- * Tcl-DP routines were modified to use binary records; copyright follows:
- *
- * Copyright 1992 Telecom Finland
- *
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that this copyright
- * notice appears in all copies. Telecom Finland
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- *
- * Copyright (c) 1993 The Regents of the University of California.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose, without fee, and without written agreement is
- * hereby granted, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
- * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
-
- /*
- #define DEBUG
- */
-
- #include "plDevs.h"
-
- #if defined(PLD_dp) || defined (PLD_tk)
-
- /* This file is meant to be compiled with non-ANSI compilers ("cc").
- * The reason for doing it this way is to ensure that the full C
- * environment of the target machine is visible (at the time of writing
- * this, parts of this code are not covered by any international
- * standard). ANSI compilers are required to omit these extra symbols,
- * and at the moment there is no way to get them back except for by
- * vendor-specific defines (HP: _HPUX_SOURCE, AIX: _ALL_SOURCE, DGUX:
- * _DGUX_SOURCE). This is an omission in the POSIX standard more than
- * anything else, and will probably be rectified at some point. So for
- * now, instead of relying on a hodgepodge of vendor specific symbols I
- * forego the ANSI compiler here and go with good (bad) old "cc".
- */
-
- #include "plConfig.h"
- #ifdef _POSIX_SOURCE
- #undef _POSIX_SOURCE
- #endif
- #ifdef caddr_t
- #undef caddr_t
- #endif
- #define PLARGS(a) ()
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <string.h>
- #if defined(__sgi) && !defined(SVR3)
- #include <sys/select.h>
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
-
- #include "pdf.h"
- #include <tcl.h>
- #include <tk.h>
-
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <sys/uio.h>
- #include <errno.h>
-
- extern int errno;
-
- #ifndef MIN
- #define MIN(a,b) (((a) < (b)) ? (a) : (b))
- #endif
-
- /*
- * This is a "magic number" prepended to the beginning of the packet
- * Used to help resync the packet machanism in the event of errors.
- */
- #define PACKET_MAGIC 0x6feeddcc
-
- /*
- * For TCP, it's possible to get a line in pieces. In case everything we
- * want isn't there, we need a place to store partial results when we're
- * in non-blocking mode. The partial buffers below are created
- * dynamically to store incomplete data in these cases.
- */
-
- typedef struct PartialRead {
- char *buffer; /* Buffer of characters */
- int bufSize; /* Size of buffer */
- int offset; /* Offset of current character within buffer */
- struct PartialRead *next; /* Next buffer in chain */
- } PartialRead;
-
- #define MAX_OPEN_FILES 128
-
- static PartialRead *partial[MAX_OPEN_FILES];
-
- static void pl_FreeReadBuffer PLARGS((int fd));
- static void pl_Unread PLARGS((int fd, char *buffer,
- int numBytes, int copy));
- static int pl_Read PLARGS((int fd, char *buffer, int numReq));
-
- /*
- *--------------------------------------------------------------
- *
- * pl_FreeReadBuffer --
- *
- * This function is called to free up all the memory associated
- * with a file once the file is closed.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Any data buffered locally will be lost.
- *
- *--------------------------------------------------------------
- */
-
- static void
- pl_FreeReadBuffer(fd)
- int fd;
- {
- PartialRead *readList;
-
- while (partial[fd] != NULL) {
- readList = partial[fd];
- partial[fd] = readList->next;
- free (readList->buffer);
- free (readList);
- }
- }
-
- /*
- *--------------------------------------------------------------
- *
- * pl_Unread --
- *
- * This function puts data back into the read chain on a
- * file descriptor. It's basically an extended "ungetc".
- *
- * Results:
- * None.
- *
- * Side effects:
- * Subsequent calls to pl_Read on the fd will get this data.
- *
- *--------------------------------------------------------------
- */
-
- static void
- pl_Unread (fd, buffer, numBytes, copy)
- int fd; /* File descriptor */
- char *buffer; /* Data to unget */
- int numBytes; /* Number of bytes to unget */
- int copy; /* Should we copy the data, or use this */
- /* buffer? */
- {
- PartialRead *new;
-
- new = (PartialRead *) malloc (sizeof(PartialRead));
- if (copy) {
- new->buffer = (char *) malloc (numBytes);
- memcpy (new->buffer, buffer, numBytes);
- } else {
- new->buffer = buffer;
- }
- new->bufSize = numBytes;
- new->offset = 0;
- new->next = partial[fd];
- partial[fd] = new;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * pl_Read --
- *
- * This function implements a "read"-like command, but
- * buffers partial reads. The semantics are the same as
- * with read.
- *
- * Results:
- * Number of bytes read, or -1 on error (with errno set).
- *
- * Side effects:
- * All available data is read from the file descriptor.
- *
- *--------------------------------------------------------------
- */
-
- static int
- pl_Read (fd, buffer, numReq)
- int fd; /* File descriptor to read from */
- char *buffer; /* Place to put the data */
- int numReq; /* Number of bytes to get */
- {
- PartialRead *readList;
- PartialRead *tmp;
- int numRead;
- int numToCopy;
-
- readList = partial[fd];
-
- /*
- * If there's no data left over from a previous read, then just do a read
- * This is the common case.
- */
- if (readList == NULL) {
- numRead = read(fd, buffer, numReq);
- #ifdef DEBUG
- {
- int j;
- fprintf(stderr, "received %d bytes starting with:", numRead);
- for (j = 0; j < MIN(8,numRead); j++)
- fprintf(stderr, " %x", 0x000000FF & (unsigned long) buffer[j]);
- fprintf(stderr, "\n");
- }
- #endif
- return numRead;
- }
-
- /*
- * There's data left over from a previous read. Yank it in and
- * only call read() if we didn't get enough data (this keeps the fd
- * readable if they only request as much data as is in the buffers).
- */
- numRead = 0;
- while ((readList != NULL) && (numRead < numReq)) {
- numToCopy = readList->bufSize - readList->offset;
- if (numToCopy + numRead > numReq) {
- numToCopy = numReq - numRead;
- }
- memcpy (buffer+numRead, readList->buffer+readList->offset, numToCopy);
-
- /*
- * Consume the data
- */
- tmp = readList;
- readList = readList->next;
- tmp->offset += numToCopy;
- if (tmp->offset == tmp->bufSize) {
- free (tmp->buffer);
- free (tmp);
- partial[fd] = readList;
- }
- numRead += numToCopy;
- }
-
- /*
- * Only call read if at the end of a previously incomplete packet.
- */
- if ((numRead < numReq)) {
- numToCopy = numReq - numRead;
- numRead += read(fd, buffer+numRead, numToCopy);
- }
-
- return numRead;
- }
-
- /*----------------------------------------------------------------------*\
- * This part for Tcl-DP only
- \*----------------------------------------------------------------------*/
-
- #ifdef PLD_dp
-
- #include <dp.h>
-
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <sys/socket.h>
-
- /*----------------------------------------------------------------------*\
- * plHost_ID
- *
- * Tcl command -- return the IP address for the current host.
- *
- * Derived from source code in "UNIX Network Programming" by W. Richard
- * Stevens, Prentice Hall, 1990.
- \*----------------------------------------------------------------------*/
-
- static char *
- get_inet(listptr, length)
- char **listptr;
- int length;
- {
- struct in_addr *ptr;
-
- while ( (ptr = (struct in_addr *) *listptr++) == NULL)
- continue;
-
- return inet_ntoa(*ptr);
- }
-
- int
- plHost_ID(clientData, interp, argc, argv)
- ClientData clientData;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- register struct hostent *hostptr;
- char hostname[100];
-
- if (gethostname(hostname, 100)) {
- Tcl_AppendResult(interp, "Error -- cannot get host name",
- (char *) NULL);
- return TCL_ERROR;
- }
-
- if ( (hostptr = gethostbyname(hostname)) == NULL) {
- Tcl_AppendResult(interp, "Error -- cannot get host info for node ",
- hostname, (char *) NULL);
- return TCL_ERROR;
- }
-
- Tcl_SetResult(interp,
- get_inet(hostptr->h_addr_list, hostptr->h_length),
- TCL_VOLATILE);
-
- return TCL_OK;
- }
-
- #endif /* PLD_dp */
-
- /*
- *--------------------------------------------------------------
- *
- * pl_PacketReceive --
- *
- * This procedure is a modified version of Tdp_PacketReceive,
- * from the Tcl-DP distribution. It reads the socket,
- * returning a complete packet. If the entire packet cannot
- * be read, the partial packet is buffered until the rest is
- * available. Some capabilities have been removed from the
- * original (i.e. the check for a non-server TCP socket, since
- * there's no access to the optFlags array from here, and the
- * peek capability, since I don't need it).
- *
- * Results:
- * Packet contents stored in pdfs->buffer and pdfs->bp set
- * to the number of bytes read (zero if incomplete).
- *
- * Side effects:
- * The file descriptor passed in is read.
- *
- *--------------------------------------------------------------
- */
- int
- pl_PacketReceive(interp, iodev, pdfs)
- Tcl_Interp *interp;
- PLiodev *iodev;
- PDFstrm *pdfs;
- {
- int j, numRead;
- unsigned int packetLen, header[2];
- int headerSize;
- unsigned char hbuf[8];
- char *errMsg;
-
- pdfs->bp = 0;
-
- /*
- * Read in the header (8 bytes)
- */
- headerSize = 8;
- numRead = pl_Read (iodev->fd, (char *) hbuf, headerSize);
-
- if (numRead <= 0) {
- #ifdef DEBUG
- fprintf(stderr, "Incorrect header read, numRead = %d\n", numRead);
- #endif
- goto readError;
- }
-
- /*
- * Check for incomplete read. If so, put it back and return.
- */
- if (numRead < headerSize) {
- #ifdef DEBUG
- fprintf(stderr, "Incomplete header read, numRead = %d\n", numRead);
- #endif
- pl_Unread (iodev->fd, (char *) hbuf, numRead, 1);
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
-
- /*
- * Convert header character stream into ints. This works when the
- * connecting machine has a different size int (the old way didn't),
- * and takes care of the endian problem to boot. It is also mostly
- * backward compatible since network byte ordering (big endian) is
- * used.
- */
-
- j = 0;
-
- header[0] = 0;
- header[0] |= hbuf[j++] << 24;
- header[0] |= hbuf[j++] << 16;
- header[0] |= hbuf[j++] << 8;
- header[0] |= hbuf[j++];
-
- header[1] = 0;
- header[1] |= hbuf[j++] << 24;
- header[1] |= hbuf[j++] << 16;
- header[1] |= hbuf[j++] << 8;
- header[1] |= hbuf[j++];
-
- /*
- * Format of each packet:
- *
- * First 4 bytes are PACKET_MAGIC.
- * Next 4 bytes are packetLen.
- * Next packetLen-headerSize is zero terminated string
- */
- if (header[0] != PACKET_MAGIC) {
- fprintf(stderr, "Badly formatted packet, numRead = %d\n", numRead);
- Tcl_AppendResult(interp, "Error reading from ", iodev->typename,
- ": badly formatted packet", (char *) NULL);
- return TCL_ERROR;
- }
- packetLen = header[1] - headerSize;
-
- /*
- * Expand the size of the buffer, as needed.
- */
-
- if (header[1] > pdfs->bufmax) {
- free((void *) pdfs->buffer);
- pdfs->bufmax = header[1] + 32;
- pdfs->buffer = (unsigned char *) malloc(pdfs->bufmax);
- }
-
- /*
- * Read in the packet, and if it's not all there, put it back.
- *
- * We have to be careful here, because we could block when if just
- * the header came in (making the file readable at the beginning of this
- * function) but the rest of the packet is still out on the network.
- */
-
- if (iodev->type == 0)
- numRead = pl_Read (iodev->fd, (char *) pdfs->buffer, packetLen);
- else {
- #ifdef PLD_dp
- if (Tdp_FDIsReady(iodev->fd) & TCL_FILE_READABLE) {
- numRead = pl_Read (iodev->fd, (char *) pdfs->buffer, packetLen);
- } else {
- #ifdef DEBUG
- fprintf(stderr, "Packet not ready, putting back header\n");
- #endif
- pl_Unread (iodev->fd, (char *) hbuf, headerSize, 1);
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
- #endif
- }
-
- if (numRead <= 0) {
- goto readError;
- }
-
- if (numRead != packetLen) {
- #ifdef DEBUG
- fprintf(stderr, "Incomplete packet read, numRead = %d\n", numRead);
- #endif
- pl_Unread (iodev->fd, (char *) pdfs->buffer, numRead, 1);
- pl_Unread (iodev->fd, (char *) hbuf, headerSize, 1);
- return TCL_OK;
- }
-
- pdfs->bp = numRead;
- #ifdef DEBUG
- fprintf(stderr, "received %d byte packet starting with:", numRead);
- for (j = 0; j < 4; j++)
- fprintf(stderr, " %x", 0x000000FF & (unsigned long) pdfs->buffer[j]);
- fprintf(stderr, "\n");
- #endif
-
- return TCL_OK;
-
- readError:
- /*
- *
- * If we're in non-blocking mode, and this would block, return.
- * If the connection is closed (numRead == 0), don't return an
- * error message. Otherwise, return one.
- *
- * In either case, we close the file, delete the file handler, and
- * return a null string.
- */
-
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
-
- /* Record the error before closing the file */
- if (numRead != 0) {
- errMsg = Tcl_PosixError (interp);
- } else {
- errMsg = NULL; /* Suppresses spurious compiler warning */
- }
-
- /*
- * Remove the file handler and close the file.
- */
- if (iodev->type == 0) {
- Tk_DeleteFileHandler(iodev->fd);
- close(iodev->fd);
- }
- pl_FreeReadBuffer(iodev->fd);
-
- Tcl_ResetResult(interp);
- if (numRead == 0) {
- return TCL_OK;
- } else {
- Tcl_AppendResult (interp, "pl_PacketReceive -- error reading from ",
- iodev->typename, ": ", errMsg, (char *) NULL);
- return TCL_ERROR;
- }
- }
-
- /*
- *--------------------------------------------------------------
- *
- * pl_PacketSend --
- *
- * This procedure is a modified version of Tdp_PacketSend,
- * from the Tcl-DP distribution. It writes a complete packet
- * to a socket or file-oriented device.
- *
- * Results:
- * A standard tcl result.
- *
- * Side effects:
- * The specified buffer is written to the file descriptor passed
- * in.
- *
- *--------------------------------------------------------------
- */
-
- int
- pl_PacketSend(interp, iodev, pdfs)
- Tcl_Interp *interp;
- PLiodev *iodev;
- PDFstrm *pdfs;
- {
- int j, numSent;
- unsigned char hbuf[8];
- unsigned int packetLen, header[2];
- int len;
- char *buffer;
- char tmp[256];
-
- /*
- * Format up the packet:
- * First 4 bytes are PACKET_MAGIC.
- * Next 4 bytes are packetLen.
- * Next packetLen-8 bytes are buffer contents.
- */
-
- packetLen = pdfs->bp + 8;
-
- header[0] = PACKET_MAGIC;
- header[1] = packetLen;
-
- /*
- * Convert header ints to character stream.
- * Network byte ordering (big endian) is used.
- */
-
- j = 0;
-
- hbuf[j++] = (header[0] & (unsigned long) 0xFF000000) >> 24;
- hbuf[j++] = (header[0] & (unsigned long) 0x00FF0000) >> 16;
- hbuf[j++] = (header[0] & (unsigned long) 0x0000FF00) >> 8;
- hbuf[j++] = (header[0] & (unsigned long) 0x000000FF);
-
- hbuf[j++] = (header[1] & (unsigned long) 0xFF000000) >> 24;
- hbuf[j++] = (header[1] & (unsigned long) 0x00FF0000) >> 16;
- hbuf[j++] = (header[1] & (unsigned long) 0x0000FF00) >> 8;
- hbuf[j++] = (header[1] & (unsigned long) 0x000000FF);
-
- /*
- * Send it off, with error checking.
- * Simulate writev using memcpy to put together
- * the msg so it can go out in a single write() call.
- */
-
- len = pdfs->bp + 8;
- buffer = (char *) malloc(len);
-
- memcpy(buffer, (char *) hbuf, 8);
- memcpy(buffer + 8, (char *) pdfs->buffer, pdfs->bp);
-
- #ifdef DEBUG
- fprintf(stderr, "sending %d byte packet starting with:", len);
- for (j = 0; j < 12; j++)
- fprintf(stderr, " %x", 0x000000FF & (unsigned long) buffer[j]);
- fprintf(stderr, "\n");
- #endif
- numSent = write(iodev->fd, buffer, len);
-
- free(buffer);
-
- if (numSent != packetLen) {
-
- if ((errno == 0) || (errno == EWOULDBLOCK || errno == EAGAIN)) {
- /*
- * Non-blocking I/O: return number of bytes actually sent.
- */
- Tcl_ResetResult(interp);
- sprintf (tmp, "%d", numSent - 8);
- Tcl_SetResult(interp, tmp, TCL_VOLATILE);
- return TCL_OK;
- } else if (errno == EPIPE) {
- /*
- * Got a broken pipe signal, which means the far end closed the
- * connection. Close the file, delete the file handler, and
- * return 0 bytes sent.
- */
- if (iodev->type == 0) {
- Tk_DeleteFileHandler(iodev->fd);
- close(iodev->fd);
- }
- sprintf (tmp, "0");
- Tcl_SetResult(interp, tmp, TCL_VOLATILE);
- return TCL_OK;
- } else {
- Tcl_AppendResult (interp, "pl_PacketSend -- error writing to ",
- iodev->typename, ": ",
- Tcl_PosixError (interp), (char *) NULL);
- }
-
- return TCL_ERROR;
- }
-
- /*
- * Return the number of bytes sent (minus the header).
- */
- sprintf (tmp, "%d", numSent - 8);
- Tcl_SetResult(interp, tmp, TCL_VOLATILE);
- return TCL_OK;
- }
-
- #else
- int
- pldummy_tcpip()
- {
- return 0;
- }
-
- #endif /* defined(PLD_dp) || defined(PLD_tk) */
-