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