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