1 // Module: Log4CPLUS
2 // File: socket-unix.cxx
3 // Created: 4/2003
4 // Author: Tad E. Smith
5 //
6 //
7 // Copyright 2003-2013 Tad E. Smith
8 //
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 //
13 // http://www.apache.org/licenses/LICENSE-2.0
14 //
15 // Unless required by applicable law or agreed to in writing, software
16 // distributed under the License is distributed on an "AS IS" BASIS,
17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 // See the License for the specific language governing permissions and
19 // limitations under the License.
20
21
22 #include <log4cplus/config.hxx>
23 #if defined (LOG4CPLUS_USE_BSD_SOCKETS)
24
25 #include <cstring>
26 #include <vector>
27 #include <algorithm>
28 #include <cerrno>
29 #include <log4cplus/internal/socket.h>
30 #include <log4cplus/helpers/loglog.h>
31 #include <log4cplus/thread/syncprims-pub-impl.h>
32 #include <log4cplus/spi/loggingevent.h>
33 #include <log4cplus/helpers/stringhelper.h>
34
35 #ifdef LOG4CPLUS_HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38
39 #ifdef LOG4CPLUS_HAVE_SYS_SOCKET_H
40 #include <sys/socket.h>
41 #endif
42
43 #if defined (LOG4CPLUS_HAVE_NETINET_IN_H)
44 #include <netinet/in.h>
45 #endif
46
47 #if defined (LOG4CPLUS_HAVE_NETINET_TCP_H)
48 #include <netinet/tcp.h>
49 #endif
50
51 #if defined (LOG4CPLUS_HAVE_ARPA_INET_H)
52 #include <arpa/inet.h>
53 #endif
54
55 #if defined (LOG4CPLUS_HAVE_ERRNO_H)
56 #include <errno.h>
57 #endif
58
59 #ifdef LOG4CPLUS_HAVE_NETDB_H
60 #include <netdb.h>
61 #endif
62
63 #ifdef LOG4CPLUS_HAVE_FCNTL_H
64 #include <fcntl.h>
65 #endif
66
67 #ifdef LOG4CPLUS_HAVE_UNISTD_H
68 #include <unistd.h>
69 #endif
70
71 #ifdef LOG4CPLUS_HAVE_POLL_H
72 #include <poll.h>
73 #endif
74
75
76 namespace log4cplus { namespace helpers {
77
78 // from lockfile.cxx
79 LOG4CPLUS_PRIVATE bool trySetCloseOnExec (int fd,
80 helpers::LogLog & loglog = helpers::getLogLog ());
81
82
83 namespace
84 {
85
86
87 #if ! defined (LOG4CPLUS_SINGLE_THREADED)
88 // We need to use log4cplus::thread here to work around compilation
89 // problem on AIX.
90 static log4cplus::thread::Mutex ghbn_mutex;
91
92 #endif
93
94
95 static
96 int
get_host_by_name(char const * hostname,std::string * name,struct sockaddr_in * addr)97 get_host_by_name (char const * hostname, std::string * name,
98 struct sockaddr_in * addr)
99 {
100 #if defined (LOG4CPLUS_HAVE_GETADDRINFO)
101 struct addrinfo hints;
102 std::memset (&hints, 0, sizeof (hints));
103 hints.ai_family = AF_INET;
104 hints.ai_socktype = SOCK_STREAM;
105 hints.ai_protocol = IPPROTO_TCP;
106 hints.ai_flags = AI_CANONNAME;
107
108 if (inet_addr (hostname) != static_cast<in_addr_t>(-1))
109 hints.ai_flags |= AI_NUMERICHOST;
110
111 struct addrinfo * res = 0;
112 int ret = getaddrinfo (hostname, 0, &hints, &res);
113 if (ret != 0)
114 return ret;
115
116 struct addrinfo const & ai = *res;
117 assert (ai.ai_family == AF_INET);
118
119 if (name)
120 *name = ai.ai_canonname;
121
122 if (addr)
123 std::memcpy (addr, ai.ai_addr, ai.ai_addrlen);
124
125 freeaddrinfo (res);
126
127 #else
128 #if ! defined (LOG4CPLUS_SINGLE_THREADED)
129 // We need to use log4cplus::thread here to work around
130 // compilation problem on AIX.
131 log4cplus::thread::MutexGuard guard (ghbn_mutex);
132
133 #endif
134
135 struct ::hostent * hp = gethostbyname (hostname);
136 if (! hp)
137 return 1;
138 assert (hp->h_addrtype == AF_INET);
139
140 if (name)
141 *name = hp->h_name;
142
143 if (addr)
144 {
145 assert (hp->h_length <= sizeof (addr->sin_addr));
146 std::memcpy (&addr->sin_addr, hp->h_addr_list[0], hp->h_length);
147 }
148
149 #endif
150
151 return 0;
152 }
153
154
155 } // namespace
156
157
158 /////////////////////////////////////////////////////////////////////////////
159 // Global Methods
160 /////////////////////////////////////////////////////////////////////////////
161
162 SOCKET_TYPE
openSocket(unsigned short port,SocketState & state)163 openSocket(unsigned short port, SocketState& state)
164 {
165 int sock = ::socket(AF_INET, SOCK_STREAM, 0);
166 if(sock < 0) {
167 return INVALID_SOCKET_VALUE;
168 }
169
170 struct sockaddr_in server = sockaddr_in ();
171 server.sin_family = AF_INET;
172 server.sin_addr.s_addr = INADDR_ANY;
173 server.sin_port = htons(port);
174
175 int optval = 1;
176 socklen_t optlen = sizeof (optval);
177 int ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &optval, optlen );
178 if (ret != 0)
179 {
180 int const eno = errno;
181 helpers::getLogLog ().warn (LOG4CPLUS_TEXT ("setsockopt() failed: ")
182 + helpers::convertIntegerToString (eno));
183 }
184
185 int retval = bind(sock, reinterpret_cast<struct sockaddr*>(&server),
186 sizeof(server));
187 if (retval < 0)
188 goto error;
189
190 if (::listen(sock, 10))
191 goto error;
192
193 state = ok;
194 return to_log4cplus_socket (sock);
195
196 error:
197 close (sock);
198 return INVALID_SOCKET_VALUE;
199 }
200
201
202 SOCKET_TYPE
connectSocket(const tstring & hostn,unsigned short port,bool udp,SocketState & state)203 connectSocket(const tstring& hostn, unsigned short port, bool udp, SocketState& state)
204 {
205 struct sockaddr_in server;
206 int sock;
207 int retval;
208
209 std::memset (&server, 0, sizeof (server));
210 retval = get_host_by_name (LOG4CPLUS_TSTRING_TO_STRING(hostn).c_str(),
211 0, &server);
212 if (retval != 0)
213 return INVALID_SOCKET_VALUE;
214
215 server.sin_port = htons(port);
216 server.sin_family = AF_INET;
217
218 sock = ::socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0);
219 if(sock < 0) {
220 return INVALID_SOCKET_VALUE;
221 }
222
223 socklen_t namelen = sizeof (server);
224 while (
225 (retval = ::connect(sock, reinterpret_cast<struct sockaddr*>(&server),
226 namelen))
227 == -1
228 && (errno == EINTR))
229 ;
230 if (retval == INVALID_OS_SOCKET_VALUE)
231 {
232 ::close(sock);
233 return INVALID_SOCKET_VALUE;
234 }
235
236 state = ok;
237 return to_log4cplus_socket (sock);
238 }
239
240
241 namespace
242 {
243
244 //! Helper for accept_wrap().
245 template <typename T, typename U>
246 struct socklen_var
247 {
248 typedef T type;
249 };
250
251
252 template <typename U>
253 struct socklen_var<void, U>
254 {
255 typedef U type;
256 };
257
258
259 // Some systems like HP-UX have socklen_t but accept() does not use it
260 // as type of its 3rd parameter. This wrapper works around this
261 // incompatibility.
262 template <typename accept_sockaddr_ptr_type, typename accept_socklen_type>
263 static
264 SOCKET_TYPE
accept_wrap(int (* accept_func)(int,accept_sockaddr_ptr_type,accept_socklen_type *),SOCKET_TYPE sock,struct sockaddr * sa,socklen_t * len)265 accept_wrap (
266 int (* accept_func) (int, accept_sockaddr_ptr_type, accept_socklen_type *),
267 SOCKET_TYPE sock, struct sockaddr * sa, socklen_t * len)
268 {
269 typedef typename socklen_var<accept_socklen_type, socklen_t>::type
270 socklen_var_type;
271 socklen_var_type l = static_cast<socklen_var_type>(*len);
272 SOCKET_TYPE result
273 = static_cast<SOCKET_TYPE>(
274 accept_func (sock, sa,
275 reinterpret_cast<accept_socklen_type *>(&l)));
276 *len = static_cast<socklen_t>(l);
277 return result;
278 }
279
280
281 } // namespace
282
283
284 SOCKET_TYPE
acceptSocket(SOCKET_TYPE sock,SocketState & state)285 acceptSocket(SOCKET_TYPE sock, SocketState& state)
286 {
287 struct sockaddr_in net_client;
288 socklen_t len = sizeof(struct sockaddr);
289 int clientSock;
290
291 while(
292 (clientSock = accept_wrap (accept, to_os_socket (sock),
293 reinterpret_cast<struct sockaddr*>(&net_client), &len))
294 == -1
295 && (errno == EINTR))
296 ;
297
298 if(clientSock != INVALID_OS_SOCKET_VALUE) {
299 state = ok;
300 }
301
302 return to_log4cplus_socket (clientSock);
303 }
304
305
306
307 int
closeSocket(SOCKET_TYPE sock)308 closeSocket(SOCKET_TYPE sock)
309 {
310 return ::close(to_os_socket (sock));
311 }
312
313
314 int
shutdownSocket(SOCKET_TYPE sock)315 shutdownSocket(SOCKET_TYPE sock)
316 {
317 return ::shutdown(to_os_socket (sock), SHUT_RDWR);
318 }
319
320
321 long
read(SOCKET_TYPE sock,SocketBuffer & buffer)322 read(SOCKET_TYPE sock, SocketBuffer& buffer)
323 {
324 long res, readbytes = 0;
325
326 do
327 {
328 res = ::read(to_os_socket (sock), buffer.getBuffer() + readbytes,
329 buffer.getMaxSize() - readbytes);
330 if( res <= 0 ) {
331 return res;
332 }
333 readbytes += res;
334 } while( readbytes < static_cast<long>(buffer.getMaxSize()) );
335
336 return readbytes;
337 }
338
339
340
341 long
write(SOCKET_TYPE sock,const SocketBuffer & buffer)342 write(SOCKET_TYPE sock, const SocketBuffer& buffer)
343 {
344 #if defined(MSG_NOSIGNAL)
345 int flags = MSG_NOSIGNAL;
346 #else
347 int flags = 0;
348 #endif
349 return ::send( to_os_socket (sock), buffer.getBuffer(), buffer.getSize(),
350 flags );
351 }
352
353
354 long
write(SOCKET_TYPE sock,const std::string & buffer)355 write(SOCKET_TYPE sock, const std::string & buffer)
356 {
357 #if defined(MSG_NOSIGNAL)
358 int flags = MSG_NOSIGNAL;
359 #else
360 int flags = 0;
361 #endif
362 return ::send (to_os_socket (sock), buffer.c_str (), buffer.size (),
363 flags);
364 }
365
366
367 tstring
getHostname(bool fqdn)368 getHostname (bool fqdn)
369 {
370 char const * hostname = "unknown";
371 int ret;
372 std::vector<char> hn (1024, 0);
373
374 while (true)
375 {
376 ret = ::gethostname (&hn[0], static_cast<int>(hn.size ()) - 1);
377 if (ret == 0)
378 {
379 hostname = &hn[0];
380 break;
381 }
382 #if defined (LOG4CPLUS_HAVE_ENAMETOOLONG)
383 else if (ret != 0 && errno == ENAMETOOLONG)
384 // Out buffer was too short. Retry with buffer twice the size.
385 hn.resize (hn.size () * 2, 0);
386 #endif
387 else
388 break;
389 }
390
391 if (ret != 0 || (ret == 0 && ! fqdn))
392 return LOG4CPLUS_STRING_TO_TSTRING (hostname);
393
394 std::string full_hostname;
395 ret = get_host_by_name (hostname, &full_hostname, 0);
396 if (ret == 0)
397 hostname = full_hostname.c_str ();
398
399 return LOG4CPLUS_STRING_TO_TSTRING (hostname);
400 }
401
402
403 int
setTCPNoDelay(SOCKET_TYPE sock,bool val)404 setTCPNoDelay (SOCKET_TYPE sock, bool val)
405 {
406 #if (defined (SOL_TCP) || defined (IPPROTO_TCP)) && defined (TCP_NODELAY)
407 #if defined (SOL_TCP)
408 int level = SOL_TCP;
409
410 #elif defined (IPPROTO_TCP)
411 int level = IPPROTO_TCP;
412
413 #endif
414
415 int result;
416 int enabled = static_cast<int>(val);
417 if ((result = setsockopt(sock, level, TCP_NODELAY, &enabled,
418 sizeof(enabled))) != 0)
419 set_last_socket_error (errno);
420
421 return result;
422
423 #else
424 // No recognizable TCP_NODELAY option.
425 return 0;
426
427 #endif
428 }
429
430
431 //
432 // ServerSocket OS dependent stuff
433 //
434
ServerSocket(unsigned short port)435 ServerSocket::ServerSocket(unsigned short port)
436 {
437 int fds[2] = {-1, -1};
438 int ret;
439
440 sock = openSocket (port, state);
441 if (sock == INVALID_SOCKET_VALUE)
442 goto error;
443
444 #if defined (LOG4CPLUS_HAVE_PIPE2) && defined (O_CLOEXEC)
445 ret = pipe2 (fds, O_CLOEXEC);
446 if (ret != 0)
447 goto error;
448
449 #elif defined (LOG4CPLUS_HAVE_PIPE)
450 ret = pipe (fds);
451 if (ret != 0)
452 goto error;
453
454 trySetCloseOnExec (fds[0]);
455 trySetCloseOnExec (fds[1]);
456
457 #else
458 # error You are missing both pipe() or pipe2().
459 #endif
460
461 interruptHandles[0] = fds[0];
462 interruptHandles[1] = fds[1];
463 return;
464
465 error:;
466 err = get_last_socket_error ();
467 state = not_opened;
468
469 if (sock != INVALID_SOCKET_VALUE)
470 closeSocket (sock);
471
472 if (fds[0] != -1)
473 ::close (fds[0]);
474
475 if (fds[1] != -1)
476 ::close (fds[1]);
477 }
478
479 Socket
accept()480 ServerSocket::accept ()
481 {
482 struct pollfd pollfds[2];
483
484 struct pollfd & interrupt_pipe = pollfds[0];
485 interrupt_pipe.fd = interruptHandles[0];
486 interrupt_pipe.events = POLLIN;
487 interrupt_pipe.revents = 0;
488
489 struct pollfd & accept_fd = pollfds[1];
490 accept_fd.fd = to_os_socket (sock);
491 accept_fd.events = POLLIN;
492 accept_fd.revents = 0;
493
494 do
495 {
496 interrupt_pipe.revents = 0;
497 accept_fd.revents = 0;
498
499 int ret = poll (pollfds, 2, -1);
500 switch (ret)
501 {
502 // Error.
503 case -1:
504 if (errno == EINTR)
505 // Signal has interrupted the call. Just re-run it.
506 continue;
507
508 set_last_socket_error (errno);
509 return Socket (INVALID_SOCKET_VALUE, not_opened, errno);
510
511 // Timeout. This should not happen though.
512 case 0:
513 continue;
514
515 default:
516 // Some descriptor is ready.
517
518 if ((interrupt_pipe.revents & POLLIN) == POLLIN)
519 {
520 // Read byte from interruption pipe.
521
522 helpers::getLogLog ().debug (
523 LOG4CPLUS_TEXT ("ServerSocket::accept- ")
524 LOG4CPLUS_TEXT ("accept() interrupted by other thread"));
525
526 char ch;
527 ret = ::read (interrupt_pipe.fd, &ch, 1);
528 if (ret == -1)
529 {
530 int const eno = errno;
531 helpers::getLogLog ().warn (
532 LOG4CPLUS_TEXT ("ServerSocket::accept- read() failed: ")
533 + helpers::convertIntegerToString (eno));
534 set_last_socket_error (eno);
535 return Socket (INVALID_SOCKET_VALUE, not_opened, eno);
536 }
537
538 // Return Socket with state set to accept_interrupted.
539
540 return Socket (INVALID_SOCKET_VALUE, accept_interrupted, 0);
541 }
542 else if ((accept_fd.revents & POLLIN) == POLLIN)
543 {
544 helpers::getLogLog ().debug (
545 LOG4CPLUS_TEXT ("ServerSocket::accept- ")
546 LOG4CPLUS_TEXT ("accepting connection"));
547
548 SocketState st = not_opened;
549 SOCKET_TYPE clientSock = acceptSocket (sock, st);
550 int eno = 0;
551 if (clientSock == INVALID_SOCKET_VALUE)
552 eno = get_last_socket_error ();
553
554 return Socket (clientSock, st, eno);
555 }
556 else
557 return Socket (INVALID_SOCKET_VALUE, not_opened, 0);
558 }
559 }
560 while (true);
561 }
562
563
564 void
interruptAccept()565 ServerSocket::interruptAccept ()
566 {
567 char ch = 'I';
568 int ret;
569
570 do
571 {
572 ret = ::write (interruptHandles[1], &ch, 1);
573 }
574 while (ret == -1 && errno == EINTR);
575
576 if (ret == -1)
577 {
578 int const eno = errno;
579 helpers::getLogLog ().warn (
580 LOG4CPLUS_TEXT ("ServerSocket::interruptAccept- write() failed: ")
581 + helpers::convertIntegerToString (eno));
582 }
583 }
584
585
~ServerSocket()586 ServerSocket::~ServerSocket()
587 {
588 if (interruptHandles[0] != -1)
589 ::close (interruptHandles[0]);
590
591 if (interruptHandles[1] != -1)
592 ::close (interruptHandles[1]);
593 }
594
595 } } // namespace log4cplus
596
597 #endif // LOG4CPLUS_USE_BSD_SOCKETS
598