PLplot  5.9.9
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
tcpip.c
Go to the documentation of this file.
1 // $Id: tcpip.c 11975 2011-10-19 11:05:10Z andrewross $
2 //
3 // Maurice LeBrun
4 // 6-Jan-94
5 //
6 // Functions to handle a variety of TPC-IP related chores, in particular
7 // socket i/o for data transfer to the Tcl-DP driver. For the latter, the
8 // Tcl-DP routines were modified to use binary records; copyright follows:
9 //
10 // Copyright 1992 Telecom Finland
11 //
12 // Permission to use, copy, modify, and distribute this
13 // software and its documentation for any purpose and without
14 // fee is hereby granted, provided that this copyright
15 // notice appears in all copies. Telecom Finland
16 // makes no representations about the suitability of this
17 // software for any purpose. It is provided "as is" without
18 // express or implied warranty.
19 //
20 // Copyright (c) 1993 The Regents of the University of California.
21 // All rights reserved.
22 //
23 // Permission to use, copy, modify, and distribute this software and its
24 // documentation for any purpose, without fee, and without written agreement is
25 // hereby granted, provided that the above copyright notice and the following
26 // two paragraphs appear in all copies of this software.
27 //
28 // IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
29 // DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
30 // OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
31 // CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
34 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
35 // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
36 // ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
37 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
38 //
39 //
40 // Copyright (C) 2004 Joao Cardoso
41 //
42 // This file is part of PLplot.
43 //
44 // PLplot is free software; you can redistribute it and/or modify
45 // it under the terms of the GNU Library General Public License as published
46 // by the Free Software Foundation; either version 2 of the License, or
47 // (at your option) any later version.
48 //
49 // PLplot is distributed in the hope that it will be useful,
50 // but WITHOUT ANY WARRANTY; without even the implied warranty of
51 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 // GNU Library General Public License for more details.
53 //
54 // You should have received a copy of the GNU Library General Public License
55 // along with PLplot; if not, write to the Free Software
56 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
57 //
58 
59 //
60 // #define DEBUG
61 //
62 
63 #include "plDevs.h"
64 #include "plConfig.h"
65 
66 #if defined ( PLD_tk ) || defined ( ENABLE_tk )
67 
68 // This file is meant to be compiled with non-ANSI compilers ("cc").
69 // The reason for doing it this way is to ensure that the full C
70 // environment of the target machine is visible (at the time of writing
71 // this, parts of this code are not covered by any international
72 // standard). ANSI compilers are required to omit these extra symbols,
73 // and at the moment there is no way to get them back except for by
74 // vendor-specific defines, e.g. _HPUX_SOURCE (HP), _ALL_SOURCE (AIX),
75 // _DGUX_SOURCE (DGUX). This is an omission in the POSIX standard more
76 // than anything else, and will probably be rectified at some point. So
77 // for now, instead of relying on a hodgepodge of vendor specific symbols
78 // I forego the ANSI compiler here and go with good (bad) old "cc".
79 //
80 
81 #ifdef _POSIX_SOURCE
82 #undef _POSIX_SOURCE
83 #endif
84 #ifdef caddr_t
85 #undef caddr_t
86 #endif
87 
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <math.h>
91 #include <string.h>
92 #if defined ( __sgi ) && !defined ( SVR3 )
93 #include <sys/select.h>
94 #endif
95 #ifdef PL_HAVE_UNISTD_H
96 #include <unistd.h>
97 #endif
98 
99 #include "plplot.h"
100 #include "tcpip.h"
101 #include <tcl.h>
102 #include <tk.h>
103 
104 #include <sys/stat.h>
105 #include <sys/types.h>
106 #include <fcntl.h>
107 #include <ctype.h>
108 #include <sys/uio.h>
109 #include <errno.h>
110 
111 //extern int errno;
112 
113 #ifndef MIN
114 #define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
115 #endif
116 
117 //
118 // This is a "magic number" prepended to the beginning of the packet
119 // Used to help resync the packet machanism in the event of errors.
120 //
121 #define PACKET_MAGIC 0x6feeddcc
122 
123 //
124 // For TCP, it's possible to get a line in pieces. In case everything we
125 // want isn't there, we need a place to store partial results when we're
126 // in non-blocking mode. The partial buffers below are created
127 // dynamically to store incomplete data in these cases.
128 //
129 
130 typedef struct PartialRead
131 {
132  char *buffer; // Buffer of characters
133  int bufSize; // Size of buffer
134  int offset; // Offset of current character within buffer
135  struct PartialRead *next; // Next buffer in chain
136 } PartialRead;
137 
138 #define MAX_OPEN_FILES 128
139 
140 static PartialRead *partial[MAX_OPEN_FILES];
141 
142 static void pl_FreeReadBuffer( int fd );
143 static void pl_Unread( int fd, char *buffer, int numBytes, int copy );
144 static int pl_Read( int fd, char *buffer, int numReq );
145 
146 int pl_PacketReceive( Tcl_Interp * interp, PLiodev *iodev, PDFstrm *pdfs );
147 int pl_PacketSend( Tcl_Interp * interp, PLiodev *iodev, PDFstrm *pdfs );
148 
149 //
150 //--------------------------------------------------------------------------
151 //
152 // pl_FreeReadBuffer --
153 //
154 // This function is called to free up all the memory associated
155 // with a file once the file is closed.
156 //
157 // Results:
158 // None.
159 //
160 // Side effects:
161 // Any data buffered locally will be lost.
162 //
163 //--------------------------------------------------------------------------
164 //
165 
166 static void
167 pl_FreeReadBuffer( int fd )
168 {
169  PartialRead *readList;
170 
171  while ( partial[fd] != NULL )
172  {
173  readList = partial[fd];
174  partial[fd] = readList->next;
175  free( readList->buffer );
176  free( readList );
177  }
178 }
179 
180 //
181 //--------------------------------------------------------------------------
182 //
183 // pl_Unread --
184 //
185 // This function puts data back into the read chain on a
186 // file descriptor. It's basically an extended "ungetc".
187 //
188 // Results:
189 // None.
190 //
191 // Side effects:
192 // Subsequent calls to pl_Read on the fd will get this data.
193 //
194 //--------------------------------------------------------------------------
195 //
196 
197 static void
198 pl_Unread( int fd, char *buffer, int numBytes, int copy )
199 //int fd; // File descriptor
200 //char *buffer; // Data to unget
201 //int numBytes; // Number of bytes to unget
202 //int copy; // Should we copy the data, or use this
203 // buffer?
204 {
205  PartialRead *new;
206 
207  new = (PartialRead *) malloc( sizeof ( PartialRead ) );
208  if ( copy )
209  {
210  new->buffer = (char *) malloc( (size_t) numBytes );
211  memcpy( new->buffer, buffer, (size_t) numBytes );
212  }
213  else
214  {
215  new->buffer = buffer;
216  }
217  new->bufSize = numBytes;
218  new->offset = 0;
219  new->next = partial[fd];
220  partial[fd] = new;
221 }
222 
223 //
224 //--------------------------------------------------------------------------
225 //
226 // pl_Read --
227 //
228 // This function implements a "read"-like command, but
229 // buffers partial reads. The semantics are the same as
230 // with read.
231 //
232 // Results:
233 // Number of bytes read, or -1 on error (with errno set).
234 //
235 // Side effects:
236 // All available data is read from the file descriptor.
237 //
238 //--------------------------------------------------------------------------
239 //
240 
241 static int
242 pl_Read( int fd, char *buffer, int numReq )
243 //int fd; // File descriptor to read from
244 //char *buffer; // Place to put the data
245 //int numReq; // Number of bytes to get
246 {
247  PartialRead *readList;
248  PartialRead *tmp;
249  int numRead;
250  int numToCopy;
251 
252  readList = partial[fd];
253 
254  //
255  // If there's no data left over from a previous read, then just do a read
256  // This is the common case.
257  //
258  if ( readList == NULL )
259  {
260  numRead = (int) read( fd, buffer, (size_t) numReq );
261 #ifdef DEBUG
262  {
263  int j;
264  fprintf( stderr, "received %d bytes starting with:", numRead );
265  for ( j = 0; j < MIN( 8, numRead ); j++ )
266  fprintf( stderr, " %x", 0x000000FF & (unsigned long) buffer[j] );
267  fprintf( stderr, "\n" );
268  }
269 #endif
270  return numRead;
271  }
272 
273  //
274  // There's data left over from a previous read. Yank it in and
275  // only call read() if we didn't get enough data (this keeps the fd
276  // readable if they only request as much data as is in the buffers).
277  //
278  numRead = 0;
279  while ( ( readList != NULL ) && ( numRead < numReq ) )
280  {
281  numToCopy = readList->bufSize - readList->offset;
282  if ( numToCopy + numRead > numReq )
283  {
284  numToCopy = numReq - numRead;
285  }
286  memcpy( buffer + numRead, readList->buffer + readList->offset, (size_t) numToCopy );
287 
288  //
289  // Consume the data
290  //
291  tmp = readList;
292  readList = readList->next;
293  tmp->offset += numToCopy;
294  if ( tmp->offset == tmp->bufSize )
295  {
296  free( tmp->buffer );
297  free( tmp );
298  partial[fd] = readList;
299  }
300  numRead += numToCopy;
301  }
302 
303  //
304  // Only call read if at the end of a previously incomplete packet.
305  //
306  if ( ( numRead < numReq ) )
307  {
308  numToCopy = numReq - numRead;
309  numRead += (int) read( fd, buffer + numRead, (size_t) numToCopy );
310  }
311 
312  return numRead;
313 }
314 
315 //--------------------------------------------------------------------------
316 // This part for Tcl-DP only
317 //--------------------------------------------------------------------------
318 
319 #ifdef PLD_dp
320 
321 #include <dp.h>
322 
323 #include <arpa/inet.h>
324 #include <netdb.h>
325 #include <netinet/in.h>
326 #include <sys/socket.h>
327 
328 //--------------------------------------------------------------------------
329 // plHost_ID
330 //
331 // Tcl command -- return the IP address for the current host.
332 //
333 // Derived from source code in "UNIX Network Programming" by W. Richard
334 // Stevens, Prentice Hall, 1990.
335 //--------------------------------------------------------------------------
336 
337 static char *
338 get_inet( char ** listptr, int length )
339 {
340  struct in_addr *ptr;
341 
342  while ( ( ptr = (struct in_addr *) *listptr++ ) == NULL )
343  continue;
344 
345  return inet_ntoa( *ptr );
346 }
347 
348 int
349 plHost_ID( ClientData clientData, Tcl_Interp *interp, int argc, char **argv )
350 {
351  register struct hostent *hostptr;
352  char hostname[100];
353 
354  if ( gethostname( hostname, 100 ) )
355  {
356  Tcl_AppendResult( interp, "Error -- cannot get host name",
357  (char *) NULL );
358  return TCL_ERROR;
359  }
360 
361  if ( ( hostptr = gethostbyname( hostname ) ) == NULL )
362  {
363  Tcl_AppendResult( interp, "Error -- cannot get host info for node ",
364  hostname, (char *) NULL );
365  return TCL_ERROR;
366  }
367 
368  Tcl_SetResult( interp,
369  get_inet( hostptr->h_addr_list, hostptr->h_length ),
370  TCL_VOLATILE );
371 
372  return TCL_OK;
373 }
374 
375 #endif // PLD_dp
376 
377 //
378 //--------------------------------------------------------------------------
379 //
380 // pl_PacketReceive --
381 //
382 // This procedure is a modified version of Tdp_PacketReceive,
383 // from the Tcl-DP distribution. It reads the socket,
384 // returning a complete packet. If the entire packet cannot
385 // be read, the partial packet is buffered until the rest is
386 // available. Some capabilities have been removed from the
387 // original, such as the check for a non-server TCP socket,
388 // since there's no access to the optFlags array from here,
389 // and the peek capability, since I don't need it.
390 //
391 // Results:
392 // Packet contents stored in pdfs->buffer and pdfs->bp set
393 // to the number of bytes read (zero if incomplete).
394 //
395 // Side effects:
396 // The file descriptor passed in is read.
397 //
398 //--------------------------------------------------------------------------
399 //
400 int
401 pl_PacketReceive( Tcl_Interp *interp, PLiodev *iodev, PDFstrm *pdfs )
402 {
403  int j, numRead;
404  unsigned int packetLen, header[2];
405  int headerSize;
406  unsigned char hbuf[8];
407  const char *errMsg;
408 
409  pdfs->bp = 0;
410 
411  //
412  // Read in the header (8 bytes)
413  //
414  headerSize = 8;
415  numRead = pl_Read( iodev->fd, (char *) hbuf, headerSize );
416 
417  if ( numRead <= 0 )
418  {
419 #ifdef DEBUG
420  fprintf( stderr, "Incorrect header read, numRead = %d\n", numRead );
421 #endif
422  goto readError;
423  }
424 
425  //
426  // Check for incomplete read. If so, put it back and return.
427  //
428  if ( numRead < headerSize )
429  {
430 #ifdef DEBUG
431  fprintf( stderr, "Incomplete header read, numRead = %d\n", numRead );
432 #endif
433  pl_Unread( iodev->fd, (char *) hbuf, numRead, 1 );
434  Tcl_ResetResult( interp );
435  return TCL_OK;
436  }
437 
438  //
439  // Convert header character stream into ints. This works when the
440  // connecting machine has a different size int and takes care of the
441  // endian problem to boot. It is also mostly backward compatible since
442  // network byte ordering (big endian) is used.
443  //
444 
445  j = 0;
446 
447  header[0] = 0;
448  header[0] |= (unsigned int) ( hbuf[j++] << 24 );
449  header[0] |= (unsigned int) ( hbuf[j++] << 16 );
450  header[0] |= (unsigned int) ( hbuf[j++] << 8 );
451  header[0] |= hbuf[j++];
452 
453  header[1] = 0;
454  header[1] |= (unsigned int) ( hbuf[j++] << 24 );
455  header[1] |= (unsigned int) ( hbuf[j++] << 16 );
456  header[1] |= (unsigned int) ( hbuf[j++] << 8 );
457  header[1] |= hbuf[j++];
458 
459  //
460  // Format of each packet:
461  //
462  // First 4 bytes are PACKET_MAGIC.
463  // Next 4 bytes are packetLen.
464  // Next packetLen-headerSize is zero terminated string
465  //
466  if ( header[0] != PACKET_MAGIC )
467  {
468  fprintf( stderr, "Badly formatted packet, numRead = %d\n", numRead );
469  Tcl_AppendResult( interp, "Error reading from ", iodev->typeName,
470  ": badly formatted packet", (char *) NULL );
471  return TCL_ERROR;
472  }
473  packetLen = header[1] - (unsigned int) headerSize;
474 
475  //
476  // Expand the size of the buffer, as needed.
477  //
478 
479  if ( header[1] > (unsigned) pdfs->bufmax )
480  {
481  free( (void *) pdfs->buffer );
482  pdfs->bufmax = header[1] + 32;
483  pdfs->buffer = (unsigned char *) malloc( pdfs->bufmax );
484  }
485 
486  //
487  // Read in the packet, and if it's not all there, put it back.
488  //
489  // We have to be careful here, because we could block if just the
490  // header came in (making the file readable at the beginning of this
491  // function) but the rest of the packet is still out on the network.
492  //
493 
494  if ( iodev->type == 0 )
495  {
496  numRead = pl_Read( iodev->fd, (char *) pdfs->buffer, (int) packetLen );
497  }
498  else
499  {
500 #ifdef PLD_dp
501  if ( Tdp_FDIsReady( iodev->fd ) & TCL_FILE_READABLE )
502  {
503  numRead = pl_Read( iodev->fd, (char *) pdfs->buffer, packetLen );
504  }
505  else
506  {
507 #ifdef DEBUG
508  fprintf( stderr, "Packet not ready, putting back header\n" );
509 #endif
510  pl_Unread( iodev->fd, (char *) hbuf, headerSize, 1 );
511  Tcl_ResetResult( interp );
512  return TCL_OK;
513  }
514 #endif
515  }
516 
517  if ( numRead <= 0 )
518  {
519  goto readError;
520  }
521 
522  if ( (unsigned) numRead != packetLen )
523  {
524 #ifdef DEBUG
525  fprintf( stderr, "Incomplete packet read, numRead = %d\n", numRead );
526 #endif
527  pl_Unread( iodev->fd, (char *) pdfs->buffer, numRead, 1 );
528  pl_Unread( iodev->fd, (char *) hbuf, headerSize, 1 );
529  return TCL_OK;
530  }
531 
532  pdfs->bp = (size_t) numRead;
533 #ifdef DEBUG
534  fprintf( stderr, "received %d byte packet starting with:", numRead );
535  for ( j = 0; j < 4; j++ )
536  {
537  fprintf( stderr, " %x", 0x000000FF & (unsigned long) pdfs->buffer[j] );
538  }
539  fprintf( stderr, "\n" );
540 #endif
541 
542  return TCL_OK;
543 
544 readError:
545  //
546  //
547  // If we're in non-blocking mode, and this would block, return.
548  // If the connection is closed (numRead == 0), don't return an
549  // error message. Otherwise, return one.
550  //
551  // In either case, we close the file, delete the file handler, and
552  // return a null string.
553  //
554 
555  if ( errno == EWOULDBLOCK || errno == EAGAIN )
556  {
557  Tcl_ResetResult( interp );
558  return TCL_OK;
559  }
560 
561  // Record the error before closing the file
562  if ( numRead != 0 )
563  {
564  errMsg = Tcl_PosixError( interp );
565  }
566  else
567  {
568  errMsg = NULL; // Suppresses spurious compiler warning
569  }
570 
571  //
572  // Remove the file handler and close the file.
573  //
574  if ( iodev->type == 0 )
575  {
576 // Exclude UNIX-only feature
577 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ ) && !defined ( __CYGWIN__ )
578  Tk_DeleteFileHandler( iodev->fd );
579 #endif
580  close( iodev->fd );
581  }
582  pl_FreeReadBuffer( iodev->fd );
583 
584  Tcl_ResetResult( interp );
585  if ( numRead == 0 )
586  {
587  return TCL_OK;
588  }
589  else
590  {
591  Tcl_AppendResult( interp, "pl_PacketReceive -- error reading from ",
592  iodev->typeName, ": ", errMsg, (char *) NULL );
593  return TCL_ERROR;
594  }
595 }
596 
597 //
598 //--------------------------------------------------------------------------
599 //
600 // pl_PacketSend --
601 //
602 // This procedure is a modified version of Tdp_PacketSend,
603 // from the Tcl-DP distribution. It writes a complete packet
604 // to a socket or file-oriented device.
605 //
606 // Results:
607 // A standard tcl result.
608 //
609 // Side effects:
610 // The specified buffer is written to the file descriptor passed
611 // in.
612 //
613 //--------------------------------------------------------------------------
614 //
615 
616 int
617 pl_PacketSend( Tcl_Interp * interp, PLiodev *iodev, PDFstrm *pdfs )
618 {
619  int j, numSent;
620  unsigned char hbuf[8];
621  unsigned int packetLen, header[2];
622  size_t len;
623  char *buffer, tmp[256];
624 
625  //
626  // Format up the packet:
627  // First 4 bytes are PACKET_MAGIC.
628  // Next 4 bytes are packetLen.
629  // Next packetLen-8 bytes are buffer contents.
630  //
631 
632  packetLen = (unsigned int) pdfs->bp + 8;
633 
634  header[0] = PACKET_MAGIC;
635  header[1] = packetLen;
636 
637  //
638  // Convert header ints to character stream.
639  // Network byte ordering (big endian) is used.
640  //
641 
642  j = 0;
643 
644  hbuf[j++] = (unsigned char) ( ( header[0] & (unsigned long) 0xFF000000 ) >> 24 );
645  hbuf[j++] = (unsigned char) ( ( header[0] & (unsigned long) 0x00FF0000 ) >> 16 );
646  hbuf[j++] = (unsigned char) ( ( header[0] & (unsigned long) 0x0000FF00 ) >> 8 );
647  hbuf[j++] = (unsigned char) ( header[0] & (unsigned long) 0x000000FF );
648 
649  hbuf[j++] = (unsigned char) ( ( header[1] & (unsigned long) 0xFF000000 ) >> 24 );
650  hbuf[j++] = (unsigned char) ( ( header[1] & (unsigned long) 0x00FF0000 ) >> 16 );
651  hbuf[j++] = (unsigned char) ( ( header[1] & (unsigned long) 0x0000FF00 ) >> 8 );
652  hbuf[j++] = (unsigned char) ( header[1] & (unsigned long) 0x000000FF );
653 
654  //
655  // Send it off, with error checking.
656  // Simulate writev using memcpy to put together
657  // the msg so it can go out in a single write() call.
658  //
659 
660  len = pdfs->bp + 8;
661  buffer = (char *) malloc( len );
662 
663  memcpy( buffer, (char *) hbuf, 8 );
664  memcpy( buffer + 8, (char *) pdfs->buffer, pdfs->bp );
665 
666 #ifdef DEBUG
667  fprintf( stderr, "sending %z byte packet starting with:", len );
668  for ( j = 0; j < 12; j++ )
669  {
670  fprintf( stderr, " %x", 0x000000FF & (unsigned long) buffer[j] );
671  }
672  fprintf( stderr, "\n" );
673 #endif
674  numSent = (int) write( iodev->fd, buffer, len );
675 
676  free( buffer );
677 
678  if ( (unsigned) numSent != packetLen )
679  {
680  if ( ( errno == 0 ) || ( errno == EWOULDBLOCK || errno == EAGAIN ) )
681  {
682  //
683  // Non-blocking I/O: return number of bytes actually sent.
684  //
685  Tcl_ResetResult( interp );
686  sprintf( tmp, "%d", numSent - 8 );
687  Tcl_SetResult( interp, tmp, TCL_VOLATILE );
688  return TCL_OK;
689  }
690  else if ( errno == EPIPE )
691  {
692  //
693  // Got a broken pipe signal, which means the far end closed
694  // the connection. Close the file and return 0 bytes sent.
695  //
696  if ( iodev->type == 0 )
697  {
698  close( iodev->fd );
699  }
700  sprintf( tmp, "0" );
701  Tcl_SetResult( interp, tmp, TCL_VOLATILE );
702  return TCL_OK;
703  }
704  else
705  {
706  Tcl_AppendResult( interp, "pl_PacketSend -- error writing to ",
707  iodev->typeName, ": ",
708  Tcl_PosixError( interp ), (char *) NULL );
709  }
710 
711  return TCL_ERROR;
712  }
713 
714  //
715  // Return the number of bytes sent (minus the header).
716  //
717  sprintf( tmp, "%d", numSent - 8 );
718  Tcl_SetResult( interp, tmp, TCL_VOLATILE );
719  return TCL_OK;
720 }
721 
722 #else
723 int
725 {
726  return 0;
727 }
728 
729 #endif // defined(PLD_tk) || defined (ENABLE_tk)