about summary refs log tree commit diff
path: root/converter/ppm/ppmtompeg/psocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/ppm/ppmtompeg/psocket.c')
-rw-r--r--converter/ppm/ppmtompeg/psocket.c452
1 files changed, 452 insertions, 0 deletions
diff --git a/converter/ppm/ppmtompeg/psocket.c b/converter/ppm/ppmtompeg/psocket.c
new file mode 100644
index 00000000..707f1d84
--- /dev/null
+++ b/converter/ppm/ppmtompeg/psocket.c
@@ -0,0 +1,452 @@
+/*===========================================================================*
+                              psocket.c
+==============================================================================
+
+   low level communication facilities for Ppmtompeg parallel operation
+
+   By Bryan Henderson 2004.10.13.  Contributed to the public domain by
+   its author.
+
+============================================================================*/
+
+#define _XOPEN_SOURCE 500 /* Make sure stdio.h contains pclose() */
+/* _ALL_SOURCE is needed on AIX to make the C library include the 
+   socket services (e.g. define struct sockaddr) 
+
+   Note that AIX standards.h actually sets feature declaration macros such
+   as _XOPEN_SOURCE, unless they are already set.
+*/
+#define _ALL_SOURCE
+#define __EXTENSIONS__
+  /* __EXTENSIONS__ is for a broken Sun C library (uname SunOS kosh 5.8 
+     generic_108528-16 sun4u sparc).  When you define _XOPEN_SOURCE,
+     it's vnode.h and resource.h fail to define some data types that they
+     need (e.g. timestruct_t).  But with __EXTENSIONS__, they declare the
+     needed types anyway.  Our #include <sys/socket.h> causes the broken
+     header files to get included.
+  */
+
+/* On AIX, pm_config.h includes standards.h, which expects to be included
+   after feature declaration macros such as _XOPEN_SOURCE.  So we include
+   pm_config.h as late as possible.
+*/
+
+#include "pm_config.h" /* For POSIX_IS_IMPLIED */
+
+#ifdef POSIX_IS_IMPLIED
+/* The OpenBSD C library, at least, is broken in that when _XOPEN_SOURCE
+   is defined, its sys/socket.h refers to types "u_char", etc. but does
+   not define them.  But it is also one of the C libraries where
+   POSIX is implied so that we don't need to define _XOPEN_SOURCE in order
+   to get the POSIX routines such as pclose() defined.  So we circumvent
+   the problem by undefining _XOPEN_SOURCE:
+*/
+#undef _XOPEN_SOURCE
+#endif
+
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "pm.h"
+#include "pm_c_util.h"
+#include "nstring.h"
+
+#include "gethostname.h"
+
+#include "psocket.h"
+
+
+/* We use type socklenx_t where we should use socklen_t from the C
+  library, but older systems don't have socklen_t.  And there doesn't
+  appear to be any way for the preprocessor to know whether it exists
+  or not.  On older systems with no socklen_t, a message length is a
+  signed integer, but on modern systems, socklen_t is an unsigned
+  integer.  Until we have some kind of build-time check for the existence
+  of socklen_t, we just use this socklenx_t, which is an unsigned
+  integer, and accept compiler warnings on older system.
+  -Bryan 2001.04.22.
+*/
+typedef unsigned int socklenx_t;
+
+#ifndef SOMAXCONN
+#define SOMAXCONN 5
+#endif
+
+
+
+static void PM_GNU_PRINTF_ATTR(1,2)
+errorExit(const char format[], ...) {
+
+    const char * const hostname = GetHostName();
+
+    va_list args;
+
+    va_start(args, format);
+
+    fprintf(stderr, "%s: FATAL ERROR.  ", hostname);
+    strfree(hostname);
+    vfprintf(stderr, format, args);
+    fputc('\n', stderr);
+
+    exit(1);
+
+    va_end(args);
+}
+
+
+
+static void
+unmarshallInt(unsigned char const buffer[],
+              int *         const valueP) {
+/*----------------------------------------------------------------------------
+  Interpret a number which is formatted for one of our network packets.
+
+  To wit, 32 bit big-endian pure binary.
+-----------------------------------------------------------------------------*/
+    union {
+        uint32_t value;
+        unsigned char bytes[4];
+    } converter;
+
+    memcpy(&converter.bytes, buffer, 4);
+
+    /* Note that contrary to what the C data types suggest, ntohl() is
+       a 32 bit converter, even if a C "long" is bigger than that.
+    */
+    *valueP = ntohl(converter.value);
+}
+
+
+
+static void
+safeRead(int             const fd, 
+         unsigned char * const buf, 
+         unsigned int    const nbyte) {
+/*----------------------------------------------------------------------------
+    Safely read from file 'fd'.  Keep reading until we get
+    'nbyte' bytes.
+-----------------------------------------------------------------------------*/
+    unsigned int numRead;
+
+    numRead = 0;  /* initial value */
+
+    while (numRead < nbyte) {
+        int const result = read(fd, &buf[numRead], nbyte-numRead);
+
+        if (result == -1)
+            errorExit("read (of %u bytes (total %u) ) returned "
+                      "errno %d (%s)",
+                      nbyte-numRead, nbyte, errno, strerror(errno));
+        else 
+            numRead += result;
+    }
+}
+
+
+
+void
+ReadBytes(int             const fd,
+          unsigned char * const buf,
+          unsigned int    const nbyte) {
+
+    safeRead(fd, buf, nbyte);
+}
+
+
+
+void
+ReadInt(int   const socketFd,
+        int * const valueP) {
+
+    unsigned char buffer[4];
+
+    safeRead(socketFd, buffer, sizeof(buffer));
+
+    unmarshallInt(buffer, valueP);
+}
+
+
+
+static void
+marshallInt(int              const value,
+            unsigned char (* const bufferP)[]) {
+/*----------------------------------------------------------------------------
+   Put the number 'value' into the buffer at *bufferP in the form required
+   for one of our network packets.
+
+   To wit, 32 bit big-endian pure binary.
+-----------------------------------------------------------------------------*/
+    union {
+        uint32_t value;
+        unsigned char bytes[4];
+    } converter;
+
+    unsigned char testbuffer[4];
+
+    /* Note that contrary to what the C data types suggest, htonl() is
+       a 32 bit converter, even if a C "long" is bigger than that.
+    */
+    converter.value = htonl(value);
+
+    (*bufferP)[0] = 7;
+    memcpy(testbuffer, &converter.bytes, 4);
+    memcpy(*bufferP, &converter.bytes, 4);
+}
+
+
+
+static void
+safeWrite(int             const fd, 
+          unsigned char * const buf, 
+          unsigned int    const nbyte) {
+/*----------------------------------------------------------------------------
+  Safely write to file 'fd'.  Keep writing until we write 'nbyte'
+  bytes.
+-----------------------------------------------------------------------------*/
+    unsigned int numWritten;
+
+    numWritten = 0;  /* initial value */
+
+    while (numWritten < nbyte) {
+        int const result = write(fd, &buf[numWritten], nbyte-numWritten);
+
+        if (result == -1) 
+            errorExit("write (of %u bytes (total %u) ) returned "
+                      "errno %d (%s)",
+                      nbyte-numWritten, nbyte, errno, strerror(errno));
+        numWritten += result;
+    }
+}
+
+
+
+void
+WriteBytes(int             const fd,
+           unsigned char * const buf,
+           unsigned int    const nbyte) {
+
+    safeWrite(fd, buf, nbyte);
+}
+
+
+
+void
+WriteInt(int const socketFd,
+         int const value) {
+
+    unsigned char buffer[4];
+
+    marshallInt(value, &buffer);
+
+    safeWrite(socketFd, buffer, sizeof(buffer));
+}
+
+
+
+void
+ConnectToSocket(const char *      const machineName, 
+                int               const portNum, 
+                struct hostent ** const hostEnt,
+                int *             const socketFdP,
+                const char **     const errorP) {
+/*----------------------------------------------------------------------------
+   Create a socket and connect it to the specified TCP endpoint.
+
+   That endpoint is fundamentally defined by 'machineName' and
+   'portNum', but *hostEnt is the address of a host entry that caches
+   the results of the host name lookup.  If *hostEnt is non-null, we
+   use it.  If *hostEnt is NULL, we look up the information and update
+   **hostEnt.
+-----------------------------------------------------------------------------*/
+    int rc;
+    
+    *errorP = NULL;  /* initial value */
+
+    if ((*hostEnt) == NULL) {
+        (*hostEnt) = gethostbyname(machineName);
+        if ((*hostEnt) == NULL)
+            asprintfN(errorP, "Couldn't get host by name (%s)", machineName);
+    }
+    if (!*errorP) {
+        rc = socket(AF_INET, SOCK_STREAM, 0);
+        if (rc < 0)
+            asprintfN(errorP, "socket() failed with errno %d (%s)", 
+                      errno, strerror(errno));
+        else {
+            int const socketFd = rc;
+            
+            int rc;
+            unsigned short tempShort;
+            struct sockaddr_in  nameEntry;
+            
+            nameEntry.sin_family = AF_INET;
+            memset((void *) nameEntry.sin_zero, 0, 8);
+            memcpy((void *) &(nameEntry.sin_addr.s_addr),
+                   (void *) (*hostEnt)->h_addr_list[0],
+                   (size_t) (*hostEnt)->h_length);
+            tempShort = portNum;
+            nameEntry.sin_port = htons(tempShort);
+            
+            rc = connect(socketFd, (struct sockaddr *) &nameEntry,
+                         sizeof(struct sockaddr));
+            
+            if (rc != 0)
+                asprintfN(errorP, 
+                          "connect() to host '%s', port %d failed with "
+                          "errno %d (%s)",
+                          machineName, portNum, errno, strerror(errno));
+            else {
+                *errorP = NULL;
+                *socketFdP = socketFd;
+            }
+            if (*errorP)
+                close(socketFd);
+        }
+    }
+}
+
+
+
+static bool
+portInUseErrno(int const testErrno) {
+/*----------------------------------------------------------------------------
+   Return TRUE iff 'testErrno' is what a bind() would return if one requestd
+   a port number that is unavailable (but other port numbers might be).
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    switch (testErrno) {
+    case EINVAL:
+    case EADDRINUSE:
+    case EADDRNOTAVAIL:
+        retval = TRUE;
+        break;
+    default:
+        retval = FALSE;
+    }
+    return retval;
+}
+
+
+
+static void
+bindToUnusedPort(int              const socketFd,
+                 unsigned short * const portNumP,
+                 const char **    const errorP) {
+    
+    bool foundPort;
+    unsigned short trialPortNum;
+
+    *errorP = NULL;  /* initial value */
+
+    for (foundPort = FALSE, trialPortNum = 2048; 
+         !foundPort && trialPortNum < 16384 && !*errorP; 
+         ++trialPortNum) {
+        
+        struct sockaddr_in nameEntry;
+        int rc;
+        
+        memset((char *) &nameEntry, 0, sizeof(nameEntry));
+        nameEntry.sin_family = AF_INET;
+        nameEntry.sin_port   = htons(trialPortNum);
+
+        rc = bind(socketFd, (struct sockaddr *) &nameEntry,
+                  sizeof(struct sockaddr));
+
+        if (rc == 0) {
+            foundPort = TRUE;
+            *portNumP = trialPortNum;
+        } else if (!portInUseErrno(errno))
+            asprintfN(errorP, "bind() of TCP port number %hu failed "
+                      "with errno %d (%s)", 
+                      trialPortNum, errno, strerror(errno));
+    }
+    
+    if (!*errorP && !foundPort)
+        asprintfN(errorP, "Unable to find a free port.  Every TCP port "
+                  "in the range 2048-16383 is in use");
+}
+
+
+
+void
+CreateListeningSocket(int *         const socketP,
+                      int *         const portNumP,
+                      const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Create a TCP socket and bind it to the first unused port number we
+   can find.
+
+   Return as *socketP a file handle for the socket (on which Caller can
+   listen()), and as *portNumP the TCP port number (to which Caller's
+   partner can connect).
+-----------------------------------------------------------------------------*/
+    int rc;
+    
+    rc = socket(AF_INET, SOCK_STREAM, 0);
+    if (rc < 0)
+        asprintfN(errorP,
+                  "Unable to create socket.  "
+                  "socket() failed with errno %d (%s)",
+                  errno, strerror(errno));
+    else {
+        int const socketFd = rc;
+
+        unsigned short portNum;
+
+        *socketP = socketFd;
+
+        bindToUnusedPort(socketFd, &portNum, errorP);
+        if (!*errorP) {
+            int rc;
+
+            *portNumP = portNum;
+
+            /* would really like to wait for 1+numMachines machines,
+              but this is max allowable, unfortunately
+            */
+            rc = listen(socketFd, SOMAXCONN);
+            if (rc != 0)
+                asprintfN(errorP, "Unable to listen on TCP socket.  "
+                          "listen() fails with errno %d (%s)", 
+                          errno, strerror(errno));
+        }
+        if (*errorP)
+            close(socketFd);
+    }
+}
+
+
+
+void
+AcceptConnection(int           const listenSocketFd,
+                 int *         const connectSocketFdP,
+                 const char ** const errorP) {
+
+    struct sockaddr otherSocket;
+    socklenx_t      otherSize;
+        /* This is an ugly dual-meaning variable.  As input to accept(),
+           it is the storage size of 'otherSocket'.  As output, it is the 
+           data length of 'otherSocket'.
+        */
+    int             rc;
+    
+    otherSize = sizeof(otherSocket);
+
+    rc = accept(listenSocketFd, &otherSocket, &otherSize);
+
+    if (rc < 0)
+        asprintfN(errorP, "accept() failed with errno %d (%s).  ",
+                  errno, strerror(errno));
+    else {
+        *connectSocketFdP = rc;
+        *errorP = NULL;
+    }
+}