Add socket pipe utility
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 20 May 2010 11:32:48 +0000 (13:32 +0200)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 20 May 2010 11:32:48 +0000 (13:32 +0200)
The socket pipe has same purpose as Unix pipe . Unfortunately
Windows pipes do not work on select/poll.

include/yaz/Makefile.am
include/yaz/spipe.h [new file with mode: 0644]
src/Makefile.am
src/spipe.c [new file with mode: 0644]
win/makefile

index 28dd159..015c5a7 100644 (file)
@@ -20,7 +20,8 @@ pkginclude_HEADERS= backend.h ccl.h ccl_xml.h cql.h rpn2cql.h comstack.h \
  z-univ.h z-oclcui.h zes-expi.h zes-exps.h zes-order.h zes-pquery.h \
  zes-psched.h zes-admin.h zes-pset.h zes-update.h zes-update0.h \
  zoom.h z-charneg.h charneg.h soap.h srw.h zgdu.h matchstr.h json.h \
  z-univ.h z-oclcui.h zes-expi.h zes-exps.h zes-order.h zes-pquery.h \
  zes-psched.h zes-admin.h zes-pset.h zes-update.h zes-update0.h \
  zoom.h z-charneg.h charneg.h soap.h srw.h zgdu.h matchstr.h json.h \
- file_glob.h dirent.h thread_id.h gettimeofday.h shptr.h thread_create.h
+ file_glob.h dirent.h thread_id.h gettimeofday.h shptr.h thread_create.h \
+ spipe.h
 
 EXTRA_DIST = yaz-version.h.in
 
 
 EXTRA_DIST = yaz-version.h.in
 
diff --git a/include/yaz/spipe.h b/include/yaz/spipe.h
new file mode 100644 (file)
index 0000000..1b48650
--- /dev/null
@@ -0,0 +1,78 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Index Data nor the names of its contributors
+ *       may be used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file spipe.h
+ * \brief socket-pipe header
+ */
+#ifndef YAZ_SPIPE_H
+#define YAZ_SPIPE_H
+
+#include <stddef.h>
+#include <time.h>
+#include <yaz/yconfig.h>
+#include <yaz/wrbuf.h>
+
+YAZ_BEGIN_CDECL
+
+/** \brief YAZ socket pipe opaque pointer */
+typedef struct yaz_spipe *yaz_spipe_t;
+
+/** \brief create socket pipe
+    \param port_to_use port that we temporarily bind to
+    \param err_msg error message reference (0 if not to be set)
+    \returns 0 on failure; != 0 on success
+ */
+YAZ_EXPORT yaz_spipe_t yaz_spipe_create(int port_to_use, WRBUF *err_msg);
+
+/** \brief destroys socket pipe
+    \param p socket pipe pointer
+ */
+YAZ_EXPORT void yaz_spipe_destroy(yaz_spipe_t p);
+
+/** \brief returns reading socket
+    \param p socket pipe pointer
+ */
+YAZ_EXPORT int yaz_spipe_get_read_fd(yaz_spipe_t p);
+
+/** \brief returns writing socket
+    \param p socket pipe pointer
+ */
+YAZ_EXPORT int yaz_spipe_get_write_fd(yaz_spipe_t p);
+
+YAZ_END_CDECL
+
+#endif
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index cfa34cb..d8dd1e9 100644 (file)
@@ -103,7 +103,7 @@ libyaz_la_SOURCES=version.c options.c log.c \
   iconv_encode_marc8.c iconv_encode_iso_8859_1.c iconv_encode_wchar.c \
   iconv_decode_marc8.c iconv_decode_iso5426.c iconv_decode_danmarc.c sc.c \
   json.c xml_include.c file_glob.c dirent.c mutex-p.h mutex.c condvar.c \
   iconv_encode_marc8.c iconv_encode_iso_8859_1.c iconv_encode_wchar.c \
   iconv_decode_marc8.c iconv_decode_iso5426.c iconv_decode_danmarc.c sc.c \
   json.c xml_include.c file_glob.c dirent.c mutex-p.h mutex.c condvar.c \
-  thread_id.c gettimeofday.c thread_create.c
+  thread_id.c gettimeofday.c thread_create.c spipe.c
 
 libyaz_la_LDFLAGS=-version-info $(YAZ_VERSION_INFO)
 
 
 libyaz_la_LDFLAGS=-version-info $(YAZ_VERSION_INFO)
 
diff --git a/src/spipe.c b/src/spipe.c
new file mode 100644 (file)
index 0000000..f42c8f1
--- /dev/null
@@ -0,0 +1,275 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+
+/**
+ * \file spipe.c
+ * \brief Implements socket-pipes
+ *
+ */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <yaz/xmalloc.h>
+#include <yaz/nmem.h>
+#include <yaz/log.h>
+#include <yaz/spipe.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#define YAZ_INVALID_SOCKET INVALID_SOCKET
+#else
+#define YAZ_INVALID_SOCKET -1
+#include <fcntl.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+
+struct yaz_spipe {
+    int m_fd[2];
+    int m_socket;
+};
+
+static void yaz_spipe_close(int *fd)
+{
+#ifdef WIN32
+    if (*fd != YAZ_INVALID_SOCKET)
+        closesocket(*fd);
+#else
+    if (*fd != YAZ_INVALID_SOCKET)
+        close(*fd);
+#endif
+    *fd = YAZ_INVALID_SOCKET;
+}
+
+static int nonblock(int s)
+{
+#ifdef WIN32
+    unsigned long tru = 1;
+    if (ioctlsocket(s, FIONBIO, &tru))
+        return -1;
+#else
+    if (fcntl(s, F_SETFL, O_NONBLOCK))
+        return -1;
+#endif
+    return 0;
+}
+
+yaz_spipe_t yaz_spipe_create(int port_to_use, WRBUF *err_msg)
+{
+    yaz_spipe_t p = xmalloc(sizeof(*p));
+
+#ifdef WIN32
+    {
+        WSADATA wsaData;
+        WORD wVersionRequested = MAKEWORD(2, 0);
+        if (WSAStartup( wVersionRequested, &wsaData))
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "WSAStartup failed");
+            xfree(p);
+            return 0;
+        }
+    }
+#endif
+    p->m_fd[0] = p->m_fd[1] = YAZ_INVALID_SOCKET;
+    p->m_socket = YAZ_INVALID_SOCKET;
+
+    if (port_to_use)
+    {
+        struct sockaddr_in add;
+        struct sockaddr *addr = 0;
+        unsigned int tmpadd;
+        struct sockaddr caddr;
+#ifdef WIN32
+        int caddr_len = sizeof(caddr);
+#else
+        socklen_t caddr_len = sizeof(caddr);
+#endif
+        fd_set write_set;
+
+        // create server socket
+        p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
+        if (p->m_socket == YAZ_INVALID_SOCKET)
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "socket call failed");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+#ifndef WIN32
+        {
+            unsigned long one = 1;
+            if (setsockopt(p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*) 
+                           &one, sizeof(one)))
+            {
+                if (err_msg)
+                    wrbuf_printf(*err_msg, "setsockopt call failed");
+                yaz_spipe_destroy(p);
+                return 0;
+            }
+        }
+#endif
+        // bind server socket
+        add.sin_family = AF_INET;
+        add.sin_port = htons(port_to_use);
+        add.sin_addr.s_addr = INADDR_ANY;
+        addr = ( struct sockaddr *) &add;
+        
+        if (bind(p->m_socket, addr, sizeof(struct sockaddr_in)))
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "could not bind to socket");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+        
+        if (listen(p->m_socket, 3) < 0)
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "could not listen on socket");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+
+        // client socket
+        tmpadd = (unsigned) inet_addr("127.0.0.1");
+        if (!tmpadd)
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "inet_addr failed");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+        
+        memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
+        p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
+        if (p->m_fd[1] == YAZ_INVALID_SOCKET)
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "socket call failed (2)");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+        nonblock(p->m_fd[1]);
+
+        if (connect(p->m_fd[1], addr, sizeof(*addr)))
+        {
+            if (
+#ifdef WIN32
+            WSAGetLastError() != WSAEWOULDBLOCK
+#else
+            errno != EINPROGRESS
+#endif
+                )
+            {
+                if (err_msg)
+                    wrbuf_printf(*err_msg, "connect call failed");
+                yaz_spipe_destroy(p);
+                return 0;
+            }
+        }
+
+        /* server accept */
+        p->m_fd[0] = accept(p->m_socket, &caddr, &caddr_len);
+        if (p->m_fd[0] == YAZ_INVALID_SOCKET)
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "accept failed");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+
+        /* complete connect */
+        FD_ZERO(&write_set);
+        FD_SET(p->m_fd[1], &write_set);
+        if (select(p->m_fd[1]+1, 0, &write_set, 0, 0) != 1)
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "could not complete connect");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+        yaz_spipe_close(&p->m_socket);
+    }
+    else
+    {
+#ifdef WIN32
+        yaz_spipe_destroy(p);
+        return 0;
+#else
+        if (pipe(p->m_fd))
+        {
+            if (err_msg)
+                wrbuf_printf(*err_msg, "pipe call failed");
+            yaz_spipe_destroy(p);
+            return 0;
+        }
+        assert(p->m_fd[0] != YAZ_INVALID_SOCKET);
+        assert(p->m_fd[1] != YAZ_INVALID_SOCKET);
+#endif
+    }
+
+    return p;
+}
+
+void yaz_spipe_destroy(yaz_spipe_t p)
+{
+    yaz_spipe_close(&p->m_fd[0]);
+    yaz_spipe_close(&p->m_fd[1]);
+    yaz_spipe_close(&p->m_socket);
+    xfree(p);
+#ifdef WIN32
+    WSACleanup();
+#endif
+}
+
+int yaz_spipe_get_read_fd(yaz_spipe_t p)
+{
+    return p->m_fd[0];
+}
+
+int yaz_spipe_get_write_fd(yaz_spipe_t p)
+{
+    return p->m_fd[1];
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 77d27e2..7933622 100644 (file)
@@ -505,6 +505,7 @@ MISC_OBJS= \
    $(OBJDIR)\iconv_decode_danmarc.obj \
    $(OBJDIR)\mutex.obj \
    $(OBJDIR)\thread_create.obj \
    $(OBJDIR)\iconv_decode_danmarc.obj \
    $(OBJDIR)\mutex.obj \
    $(OBJDIR)\thread_create.obj \
+   $(OBJDIR)\spipe.obj \
    $(OBJDIR)\gettimeofday.obj \
    $(OBJDIR)\json.obj \
    $(OBJDIR)\sc.obj \
    $(OBJDIR)\gettimeofday.obj \
    $(OBJDIR)\json.obj \
    $(OBJDIR)\sc.obj \