libt2n: (gerd) fixes & testcase
[libt2n] / src / socket_handler.cpp
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 #include <iostream>
38
39 #include "socket_handler.hxx"
40 #include "t2n_exception.hxx"
41
42 using namespace std;
43
44 namespace libt2n
45 {
46
47 void 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
108 void socket_handler::close()
109 {
110     // graceful shutdown
111     shutdown(sock,SHUT_RDWR);
112     ::close(sock);
113 }
114
115 bool socket_handler::is_closed()
116 {
117     int r=fcntl(sock,F_GETFL);
118
119     return !(r & O_ACCMODE);
120 }
121
122 bool 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
150 bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
151 {
152     // fast path for timeout==-1
153     if (usec_timeout==-1 || data_waiting(usec_timeout))
154         return fill_buffer(buffer);
155     else
156         return false;
157 }
158
159 bool 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
195     if (data_waiting())
196         fill_buffer(buffer);
197
198     if (nbytes > 0)
199         return true;
200     else
201         return false;
202 }
203
204 void socket_handler::socket_write(const std::string& data)
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 }