libt2n: (gerd) refactored connection classes
[libt2n] / src / socket_handler.cpp
... / ...
CommitLineData
1/***************************************************************************
2 * Copyright (C) 2006 by Gerd v. Egidy *
3 * gve@intra2net.com *
4 * *
5 * This library is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU Lesser General Public License version *
7 * 2.1 as published by the Free Software Foundation. *
8 * *
9 * This library is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19
20#include <stdio.h>
21#include <errno.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/time.h>
28#include <arpa/inet.h>
29#include <netinet/in.h>
30#include <netdb.h>
31#include <fcntl.h>
32#include <time.h>
33#include <pwd.h>
34#include <grp.h>
35
36#include <sstream>
37
38#include "socket_handler.hxx"
39#include "t2n_exception.hxx"
40
41using namespace std;
42
43namespace libt2n
44{
45
46void socket_handler::set_socket_options(int sock)
47{
48 int i=1;
49
50 /* fast reuse enable */
51 if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
52 {
53 string err="error setting socket option: ";
54 err+=strerror(errno);
55 log(error, err);
56 throw t2n_communication_error(err);
57 }
58
59 /* keepalive enable */
60 if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
61 {
62 string err="error setting socket option: ";
63 err+=strerror(errno);
64 log(error, err);
65 throw t2n_communication_error(err);
66 }
67
68 /* close on exec */
69 int fdflags;
70 fdflags=fcntl(sock,F_GETFD, 0);
71 if (fdflags < 0)
72 {
73 string err="fcntl error on socket: ";
74 err+=strerror(errno);
75 log(error, err);
76 throw t2n_communication_error(err);
77 }
78 fdflags |= FD_CLOEXEC;
79 if (fcntl(sock,F_SETFD,fdflags) < 0)
80 {
81 string err="fcntl error on socket: ";
82 err+=strerror(errno);
83 log(error, err);
84 throw t2n_communication_error(err);
85 }
86
87 /* non-blocking mode */
88 int flflags;
89 flflags=fcntl(sock,F_GETFL,0);
90 if (flflags < 0)
91 {
92 string err="fcntl error on socket: ";
93 err+=strerror(errno);
94 log(error, err);
95 throw t2n_communication_error(err);
96 }
97 flflags |= O_NONBLOCK;
98 if (fcntl(sock,F_SETFL,flflags) < 0)
99 {
100 string err="fcntl error on socket: ";
101 err+=strerror(errno);
102 log(error, err);
103 throw t2n_communication_error(err);
104 }
105}
106
107void socket_handler::close()
108{
109 ::close(sock);
110}
111
112bool socket_handler::data_waiting(long long usec_timeout)
113{
114 // just our socket
115 fd_set active_fd_set;
116 FD_ZERO (&active_fd_set);
117 FD_SET (sock, &active_fd_set);
118
119 /* set timeout */
120 struct timeval tval;
121 struct timeval *timeout_ptr;
122
123 if (usec_timeout == -1)
124 timeout_ptr = NULL;
125 else
126 {
127 timeout_ptr = &tval;
128
129 // timeout von long long usec in int sec + int usec umrechnen
130 tval.tv_sec = usec_timeout / 1000000;
131 tval.tv_usec = usec_timeout % 1000000;
132 }
133
134 if(select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr) > 0)
135 return true;
136 else
137 return false;
138}
139
140bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
141{
142 // fast path for timeout==-1
143 if (usec_timeout==-1 || data_waiting(usec_timeout))
144 return fill_buffer(buffer);
145 else
146 return false;
147}
148
149bool socket_handler::fill_buffer(std::string& buffer)
150{
151 bool try_again=false;
152
153 char socket_buffer[recv_buffer_size];
154
155 int nbytes = read (sock, socket_buffer, recv_buffer_size);
156 if (nbytes < 0)
157 {
158 if (errno == EAGAIN)
159 return false; // no data was waiting
160 else if (errno == EINTR)
161 {
162 // interrupted, try again
163 try_again=true;
164 }
165 else
166 {
167 log(error,string("error reading from socket : ")+strerror(errno));
168 // TODO: exception?
169 return false;
170 }
171 }
172
173 // End-of-file
174 if (nbytes == 0 && !try_again)
175 {
176 close();
177 return false;
178 }
179
180 // Data read -> store it
181 if (nbytes > 0)
182 buffer.assign(socket_buffer,nbytes);
183
184 // more data waiting -> recurse
185 if (data_waiting())
186 fill_buffer(buffer);
187
188 if (nbytes > 0)
189 return true;
190 else
191 return false;
192}
193
194void socket_handler::write(const std::string& data)
195{
196 int offset = 0;
197 while (offset < data.size())
198 {
199 unsigned int write_size=write_block_size;
200
201 if (offset+write_size > data.size())
202 write_size = data.size()-offset;
203
204 int rtn;
205 while ((rtn=::write(sock, data.data()+offset, write_size)) &&
206 rtn == -1 && (errno == EAGAIN || errno == EINTR))
207 {
208 usleep (80000);
209 log(debug,"resuming write() call after EAGAIN or EINTR");
210 }
211
212 if (rtn == -1)
213 {
214 log(error,string("write() returned ")+strerror(errno));
215 // TODO: exception?
216 return;
217 }
218 else if (rtn != write_size)
219 {
220 ostringstream msg;
221 msg << "write() wrote " << rtn << " bytes, should have been "
222 << write_size << " (complete: " << data.size() << ")";
223
224 log(error,msg.str());
225
226 // TODO: exception?
227 return;
228 }
229
230 offset += write_size;
231 }
232
233 return;
234}
235
236}