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