Add socket pipe utility
[yaz-moved-to-github.git] / src / spipe.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file spipe.c
8  * \brief Implements socket-pipes
9  *
10  */
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <assert.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/nmem.h>
21 #include <yaz/log.h>
22 #include <yaz/spipe.h>
23
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27
28 #ifdef WIN32
29 #include <winsock2.h>
30 #define YAZ_INVALID_SOCKET INVALID_SOCKET
31 #else
32 #define YAZ_INVALID_SOCKET -1
33 #include <fcntl.h>
34 #endif
35
36 #if HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif
39 #if HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #if HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #if HAVE_NETINET_TCP_H
46 #include <netinet/tcp.h>
47 #endif
48 #if HAVE_SYS_SOCKET_H
49 #include <sys/socket.h>
50 #endif
51
52 #include <netinet/in.h>
53 #include <netdb.h>
54 #include <arpa/inet.h>
55 #include <netinet/tcp.h>
56
57 struct yaz_spipe {
58     int m_fd[2];
59     int m_socket;
60 };
61
62 static void yaz_spipe_close(int *fd)
63 {
64 #ifdef WIN32
65     if (*fd != YAZ_INVALID_SOCKET)
66         closesocket(*fd);
67 #else
68     if (*fd != YAZ_INVALID_SOCKET)
69         close(*fd);
70 #endif
71     *fd = YAZ_INVALID_SOCKET;
72 }
73
74 static int nonblock(int s)
75 {
76 #ifdef WIN32
77     unsigned long tru = 1;
78     if (ioctlsocket(s, FIONBIO, &tru))
79         return -1;
80 #else
81     if (fcntl(s, F_SETFL, O_NONBLOCK))
82         return -1;
83 #endif
84     return 0;
85 }
86
87 yaz_spipe_t yaz_spipe_create(int port_to_use, WRBUF *err_msg)
88 {
89     yaz_spipe_t p = xmalloc(sizeof(*p));
90
91 #ifdef WIN32
92     {
93         WSADATA wsaData;
94         WORD wVersionRequested = MAKEWORD(2, 0);
95         if (WSAStartup( wVersionRequested, &wsaData))
96         {
97             if (err_msg)
98                 wrbuf_printf(*err_msg, "WSAStartup failed");
99             xfree(p);
100             return 0;
101         }
102     }
103 #endif
104     p->m_fd[0] = p->m_fd[1] = YAZ_INVALID_SOCKET;
105     p->m_socket = YAZ_INVALID_SOCKET;
106
107     if (port_to_use)
108     {
109         struct sockaddr_in add;
110         struct sockaddr *addr = 0;
111         unsigned int tmpadd;
112         struct sockaddr caddr;
113 #ifdef WIN32
114         int caddr_len = sizeof(caddr);
115 #else
116         socklen_t caddr_len = sizeof(caddr);
117 #endif
118         fd_set write_set;
119
120         // create server socket
121         p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
122         if (p->m_socket == YAZ_INVALID_SOCKET)
123         {
124             if (err_msg)
125                 wrbuf_printf(*err_msg, "socket call failed");
126             yaz_spipe_destroy(p);
127             return 0;
128         }
129 #ifndef WIN32
130         {
131             unsigned long one = 1;
132             if (setsockopt(p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*) 
133                            &one, sizeof(one)))
134             {
135                 if (err_msg)
136                     wrbuf_printf(*err_msg, "setsockopt call failed");
137                 yaz_spipe_destroy(p);
138                 return 0;
139             }
140         }
141 #endif
142         // bind server socket
143         add.sin_family = AF_INET;
144         add.sin_port = htons(port_to_use);
145         add.sin_addr.s_addr = INADDR_ANY;
146         addr = ( struct sockaddr *) &add;
147         
148         if (bind(p->m_socket, addr, sizeof(struct sockaddr_in)))
149         {
150             if (err_msg)
151                 wrbuf_printf(*err_msg, "could not bind to socket");
152             yaz_spipe_destroy(p);
153             return 0;
154         }
155         
156         if (listen(p->m_socket, 3) < 0)
157         {
158             if (err_msg)
159                 wrbuf_printf(*err_msg, "could not listen on socket");
160             yaz_spipe_destroy(p);
161             return 0;
162         }
163
164         // client socket
165         tmpadd = (unsigned) inet_addr("127.0.0.1");
166         if (!tmpadd)
167         {
168             if (err_msg)
169                 wrbuf_printf(*err_msg, "inet_addr failed");
170             yaz_spipe_destroy(p);
171             return 0;
172         }
173         
174         memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
175         p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
176         if (p->m_fd[1] == YAZ_INVALID_SOCKET)
177         {
178             if (err_msg)
179                 wrbuf_printf(*err_msg, "socket call failed (2)");
180             yaz_spipe_destroy(p);
181             return 0;
182         }
183         nonblock(p->m_fd[1]);
184
185         if (connect(p->m_fd[1], addr, sizeof(*addr)))
186         {
187             if (
188 #ifdef WIN32
189             WSAGetLastError() != WSAEWOULDBLOCK
190 #else
191             errno != EINPROGRESS
192 #endif
193                 )
194             {
195                 if (err_msg)
196                     wrbuf_printf(*err_msg, "connect call failed");
197                 yaz_spipe_destroy(p);
198                 return 0;
199             }
200         }
201
202         /* server accept */
203         p->m_fd[0] = accept(p->m_socket, &caddr, &caddr_len);
204         if (p->m_fd[0] == YAZ_INVALID_SOCKET)
205         {
206             if (err_msg)
207                 wrbuf_printf(*err_msg, "accept failed");
208             yaz_spipe_destroy(p);
209             return 0;
210         }
211
212         /* complete connect */
213         FD_ZERO(&write_set);
214         FD_SET(p->m_fd[1], &write_set);
215         if (select(p->m_fd[1]+1, 0, &write_set, 0, 0) != 1)
216         {
217             if (err_msg)
218                 wrbuf_printf(*err_msg, "could not complete connect");
219             yaz_spipe_destroy(p);
220             return 0;
221         }
222         yaz_spipe_close(&p->m_socket);
223     }
224     else
225     {
226 #ifdef WIN32
227         yaz_spipe_destroy(p);
228         return 0;
229 #else
230         if (pipe(p->m_fd))
231         {
232             if (err_msg)
233                 wrbuf_printf(*err_msg, "pipe call failed");
234             yaz_spipe_destroy(p);
235             return 0;
236         }
237         assert(p->m_fd[0] != YAZ_INVALID_SOCKET);
238         assert(p->m_fd[1] != YAZ_INVALID_SOCKET);
239 #endif
240     }
241
242     return p;
243 }
244
245 void yaz_spipe_destroy(yaz_spipe_t p)
246 {
247     yaz_spipe_close(&p->m_fd[0]);
248     yaz_spipe_close(&p->m_fd[1]);
249     yaz_spipe_close(&p->m_socket);
250     xfree(p);
251 #ifdef WIN32
252     WSACleanup();
253 #endif
254 }
255
256 int yaz_spipe_get_read_fd(yaz_spipe_t p)
257 {
258     return p->m_fd[0];
259 }
260
261 int yaz_spipe_get_write_fd(yaz_spipe_t p)
262 {
263     return p->m_fd[1];
264 }
265
266
267 /*
268  * Local variables:
269  * c-basic-offset: 4
270  * c-file-style: "Stroustrup"
271  * indent-tabs-mode: nil
272  * End:
273  * vim: shiftwidth=4 tabstop=8 expandtab
274  */
275