GPL v2.
[metaproxy-moved-to-github.git] / src / pipe.cpp
1 /* $Id: pipe.cpp,v 1.12 2007-05-09 21:23:09 adam Exp $
2    Copyright (c) 2005-2007, Index Data.
3
4 This file is part of Metaproxy.
5
6 Metaproxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Metaproxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include "config.hpp"
23
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27
28 #include <signal.h>
29 #include <errno.h>
30 #ifdef WIN32
31 #include <winsock.h>
32 #else
33 #include <netinet/in.h>
34 #include <netdb.h>
35 #include <arpa/inet.h>
36 #include <netinet/tcp.h>
37
38 #include <fcntl.h>
39 #endif
40
41 #if HAVE_SYS_SOCKET_H
42 #include <sys/socket.h>
43 #endif
44 #if HAVE_SYS_SELECT_H
45 #include <sys/select.h>
46 #endif
47
48 #include <boost/thread/thread.hpp>
49 #include <boost/thread/mutex.hpp>
50 #include <boost/thread/condition.hpp>
51
52 #include <stdio.h>
53
54 #include <deque>
55
56 #include <yazpp/socket-observer.h>
57 #include <yaz/log.h>
58
59 #include "pipe.hpp"
60
61 namespace mp = metaproxy_1;
62
63 namespace metaproxy_1 {
64     class Pipe::Rep : public boost::noncopyable {
65         friend class Pipe;
66         Rep();
67         int m_fd[2];
68         int m_socket;
69         bool nonblock(int s);
70         void close(int &fd);
71     };
72 }
73
74 using namespace mp;
75
76 void Pipe::Rep::close(int &fd)
77 {
78 #ifdef WIN32
79     if (fd != -1)
80         ::closesocket(fd);
81 #else
82     if (fd != -1)
83         ::close(fd);
84 #endif
85     fd = -1;
86 }
87
88 Pipe::Rep::Rep()
89 {
90     m_fd[0] = m_fd[1] = -1;
91     m_socket = -1;
92 }
93
94 bool Pipe::Rep::nonblock(int s)
95 {
96 #ifdef WIN32
97     unsigned long tru = 1;
98     if (ioctlsocket(s, FIONBIO, &tru) < 0)
99         return false;
100 #else
101     if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
102         return false;
103 #ifndef MSG_NOSIGNAL
104     signal (SIGPIPE, SIG_IGN);
105 #endif
106 #endif
107     return true;
108 }
109
110 Pipe::Pipe(int port_to_use) : m_p(new Rep)
111 {
112 #ifdef WIN32
113     WSADATA wsaData;
114     WORD wVersionRequested = MAKEWORD(2, 0);
115     if (WSAStartup( wVersionRequested, &wsaData ))
116         throw Pipe::Error("WSAStartup failed");
117 #else
118     port_to_use = 0;  // we'll just use pipe on Unix
119 #endif
120     if (port_to_use)
121     {
122         // create server socket
123         m_p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
124         if (m_p->m_socket < 0)
125             throw Pipe::Error("could not create socket");
126 #ifndef WIN32
127         unsigned long one = 1;
128         if (setsockopt(m_p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*) 
129                        &one, sizeof(one)) < 0)
130             throw Pipe::Error("setsockopt error");
131 #endif
132         // bind server socket
133         struct sockaddr_in add;
134         add.sin_family = AF_INET;
135         add.sin_port = htons(port_to_use);
136         add.sin_addr.s_addr = INADDR_ANY;
137         struct sockaddr *addr = ( struct sockaddr *) &add;
138       
139         if (bind(m_p->m_socket, addr, sizeof(struct sockaddr_in)))
140             throw Pipe::Error("could not bind on socket");
141         
142         if (listen(m_p->m_socket, 3) < 0)
143             throw Pipe::Error("could not listen on socket");
144
145         // client socket
146         unsigned int tmpadd;
147         tmpadd = (unsigned) inet_addr("127.0.0.1");
148         if (tmpadd)
149             memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
150         else
151             throw Pipe::Error("inet_addr failed");
152             
153         m_p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
154         if (m_p->m_fd[1] < 0)
155             throw Pipe::Error("could not create socket");
156         
157         m_p->nonblock(m_p->m_fd[1]);
158
159         if (connect(m_p->m_fd[1], addr, sizeof(*addr)) < 0)
160         {
161 #ifdef WIN32
162             if (WSAGetLastError() != WSAEWOULDBLOCK)
163                 throw Pipe::Error("could not connect to socket");
164 #else
165             if (errno != EINPROGRESS)
166                 throw Pipe::Error("could not connect to socket");
167 #endif
168         }
169
170         // server accept
171         struct sockaddr caddr;
172 #ifdef WIN32
173         int caddr_len = sizeof(caddr);
174 #else
175         socklen_t caddr_len = sizeof(caddr);
176 #endif
177         m_p->m_fd[0] = accept(m_p->m_socket, &caddr, &caddr_len);
178         if (m_p->m_fd[0] < 0)
179             throw Pipe::Error("could not accept on socket");
180
181         // complete connect
182         fd_set write_set;
183         FD_ZERO(&write_set);
184         FD_SET(m_p->m_fd[1], &write_set);
185         int r = select(m_p->m_fd[1]+1, 0, &write_set, 0, 0);
186         if (r != 1)
187             throw Pipe::Error("could not complete connect");
188
189         m_p->close(m_p->m_socket);
190     }
191     else
192     {
193 #ifndef WIN32
194         if (pipe(m_p->m_fd))
195             throw Pipe::Error("pipe failed");
196         else
197         {
198             assert(m_p->m_fd[0] >= 0);
199             assert(m_p->m_fd[1] >= 0);
200         }
201 #endif
202     }
203 }
204
205 Pipe::~Pipe()
206 {
207     m_p->close(m_p->m_fd[0]);
208     m_p->close(m_p->m_fd[1]);
209     m_p->close(m_p->m_socket);
210 #ifdef WIN32
211     WSACleanup();
212 #endif
213 }
214
215 int &Pipe::read_fd() const
216 {
217     return m_p->m_fd[0];
218 }
219
220 int &Pipe::write_fd() const
221 {
222     return m_p->m_fd[1];
223 }
224
225 /*
226  * Local variables:
227  * c-basic-offset: 4
228  * indent-tabs-mode: nil
229  * c-file-style: "stroustrup"
230  * End:
231  * vim: shiftwidth=4 tabstop=8 expandtab
232  */
233