libt2n: (gerd) doxygenize
[libt2n] / src / socket_handler.cpp
CommitLineData
a11e19b7
GE
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>
644c4d26 37#include <iostream>
a11e19b7
GE
38
39#include "socket_handler.hxx"
40#include "t2n_exception.hxx"
41
42using namespace std;
43
44namespace libt2n
45{
46
94247295 47/// set options like fast reuse and keepalive every socket should have
a11e19b7
GE
48void socket_handler::set_socket_options(int sock)
49{
50 int i=1;
51
52 /* fast reuse enable */
53 if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
54 {
55 string err="error setting socket option: ";
56 err+=strerror(errno);
57 log(error, err);
58 throw t2n_communication_error(err);
59 }
60
61 /* keepalive enable */
62 if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
63 {
64 string err="error setting socket option: ";
65 err+=strerror(errno);
66 log(error, err);
67 throw t2n_communication_error(err);
68 }
69
70 /* close on exec */
71 int fdflags;
72 fdflags=fcntl(sock,F_GETFD, 0);
73 if (fdflags < 0)
74 {
75 string err="fcntl error on socket: ";
76 err+=strerror(errno);
77 log(error, err);
78 throw t2n_communication_error(err);
79 }
80 fdflags |= FD_CLOEXEC;
81 if (fcntl(sock,F_SETFD,fdflags) < 0)
82 {
83 string err="fcntl error on socket: ";
84 err+=strerror(errno);
85 log(error, err);
86 throw t2n_communication_error(err);
87 }
88
89 /* non-blocking mode */
90 int flflags;
91 flflags=fcntl(sock,F_GETFL,0);
92 if (flflags < 0)
93 {
94 string err="fcntl error on socket: ";
95 err+=strerror(errno);
96 log(error, err);
97 throw t2n_communication_error(err);
98 }
99 flflags |= O_NONBLOCK;
100 if (fcntl(sock,F_SETFL,flflags) < 0)
101 {
102 string err="fcntl error on socket: ";
103 err+=strerror(errno);
104 log(error, err);
105 throw t2n_communication_error(err);
106 }
107}
108
94247295
GE
109/// close the underlying socket connection. Don't call directly, use the version provided
110/// by the connection class you are using.
a11e19b7
GE
111void socket_handler::close()
112{
644c4d26
GE
113 // graceful shutdown
114 shutdown(sock,SHUT_RDWR);
a11e19b7
GE
115 ::close(sock);
116}
117
94247295 118/// is the underlying socket connection still open?
644c4d26
GE
119bool socket_handler::is_closed()
120{
121 int r=fcntl(sock,F_GETFL);
122
123 return !(r & O_ACCMODE);
124}
125
94247295
GE
126/** @brief check if new data is waiting on the raw socket
127 @param usec_timeout wait until new data is found, max timeout usecs.
128 -1: wait endless
129 NULL: no timeout
130*/
a11e19b7
GE
131bool socket_handler::data_waiting(long long usec_timeout)
132{
133 // just our socket
134 fd_set active_fd_set;
135 FD_ZERO (&active_fd_set);
136 FD_SET (sock, &active_fd_set);
137
138 /* set timeout */
139 struct timeval tval;
140 struct timeval *timeout_ptr;
141
142 if (usec_timeout == -1)
143 timeout_ptr = NULL;
144 else
145 {
146 timeout_ptr = &tval;
147
148 // timeout von long long usec in int sec + int usec umrechnen
149 tval.tv_sec = usec_timeout / 1000000;
150 tval.tv_usec = usec_timeout % 1000000;
151 }
152
153 if(select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr) > 0)
154 return true;
155 else
156 return false;
157}
158
94247295
GE
159/** @brief read data from the raw socket and copy it into the provided buffer
160 @param buffer the buffer where to append the new data
161 @param usec_timeout wait until new data is found, max timeout usecs.
162 -1: wait endless
163 NULL: no timeout
164*/
a11e19b7
GE
165bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
166{
07e98688
GE
167 // fast path for timeout==0
168 if (usec_timeout==0 || data_waiting(usec_timeout))
a11e19b7
GE
169 return fill_buffer(buffer);
170 else
171 return false;
172}
173
94247295
GE
174/** @brief read data from the raw socket and copy it into the provided buffer. Returns
175 instantly if no data is waiting.
176 @param buffer the buffer where to append the new data
177*/
a11e19b7
GE
178bool socket_handler::fill_buffer(std::string& buffer)
179{
180 bool try_again=false;
181
182 char socket_buffer[recv_buffer_size];
183
184 int nbytes = read (sock, socket_buffer, recv_buffer_size);
185 if (nbytes < 0)
186 {
187 if (errno == EAGAIN)
188 return false; // no data was waiting
189 else if (errno == EINTR)
190 {
191 // interrupted, try again
192 try_again=true;
193 }
194 else
195 {
196 log(error,string("error reading from socket : ")+strerror(errno));
197 // TODO: exception?
198 return false;
199 }
200 }
201
202 // End-of-file
203 if (nbytes == 0 && !try_again)
204 {
205 close();
206 return false;
207 }
208
209 // Data read -> store it
210 if (nbytes > 0)
211 buffer.assign(socket_buffer,nbytes);
212
213 // more data waiting -> recurse
07e98688 214 if (data_waiting(0))
a11e19b7
GE
215 fill_buffer(buffer);
216
217 if (nbytes > 0)
218 return true;
219 else
220 return false;
221}
222
94247295
GE
223/// writes raw data to the socket. Don't use directly, use the write() function provided by the
224/// connection because it encapsulates the data.
644c4d26 225void socket_handler::socket_write(const std::string& data)
a11e19b7
GE
226{
227 int offset = 0;
228 while (offset < data.size())
229 {
230 unsigned int write_size=write_block_size;
231
232 if (offset+write_size > data.size())
233 write_size = data.size()-offset;
234
235 int rtn;
236 while ((rtn=::write(sock, data.data()+offset, write_size)) &&
237 rtn == -1 && (errno == EAGAIN || errno == EINTR))
238 {
239 usleep (80000);
240 log(debug,"resuming write() call after EAGAIN or EINTR");
241 }
242
243 if (rtn == -1)
244 {
245 log(error,string("write() returned ")+strerror(errno));
246 // TODO: exception?
247 return;
248 }
249 else if (rtn != write_size)
250 {
251 ostringstream msg;
252 msg << "write() wrote " << rtn << " bytes, should have been "
253 << write_size << " (complete: " << data.size() << ")";
254
255 log(error,msg.str());
256
257 // TODO: exception?
258 return;
259 }
260
261 offset += write_size;
262 }
263
264 return;
265}
266
267}