blob: e6318f0e408e84bac369a1fd266a308392c5ac6c [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program 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 General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#include <cc++/address.h>
41#include <cc++/socket.h>
42#include <cc++/file.h>
43#include <cc++/thread.h>
44#include <cc++/exception.h>
45#include <cc++/export.h>
46#include <cc++/unix.h>
47
48#include <cc++/unix.h>
49#include <fcntl.h>
50#include <cerrno>
51#include <cstdlib>
52#ifndef WIN32
53#include <netinet/tcp.h>
54#include <sys/un.h>
55#endif
56
57#ifdef WIN32
58#include <io.h>
59#endif
60
61#ifdef CCXX_NAMESPACES
62namespace ost {
63using namespace std;
64#endif
65
66#ifndef WIN32
67
68UnixSocket::UnixSocket(const char* pathname, int backlog) :
69Socket(AF_UNIX, SOCK_STREAM, 0)
70{
71 struct sockaddr_un addr;
72 socklen_t len;
73 unsigned slen = strlen(pathname);
74
75 if(slen > sizeof(addr.sun_path))
76 slen = sizeof(addr.sun_path);
77
78 path = NULL;
79 memset(&addr, 0, sizeof(addr));
80 addr.sun_family = AF_UNIX;
81 memcpy( addr.sun_path, pathname, slen );
82
83#ifdef __SUN_LEN
84 len = sizeof(addr.sun_len) + strlen(addr.sun_path) + sizeof(addr.sun_family) + 1;
85 addr.sun_len = len;
86#else
87 len = strlen(addr.sun_path) + sizeof(addr.sun_family) + 1;
88#endif
89 remove(pathname);
90 if(bind(so, (struct sockaddr *)&addr, len)) {
91 endSocket();
92 error(errBindingFailed);
93 return;
94 }
95
96 path = new char[slen + 1];
97 strcpy(path, pathname);
98
99 if(listen(so, backlog)) {
100 endSocket();
101 error(errBindingFailed);
102 return;
103 }
104 state = BOUND;
105 }
106
107
108UnixSocket::~UnixSocket()
109{
110 close();
111}
112
113void UnixSocket::close(void)
114{
115 endSocket();
116 if(path) {
117 remove(path);
118 delete[] path;
119 path = NULL;
120 }
121}
122
123UnixStream::UnixStream(UnixSocket &server, int size, bool throwflag, timeout_t to) :
124Socket(accept(server.so, NULL, NULL)) ,streambuf()
125#ifdef HAVE_OLD_IOSTREAM
126,iostream()
127#else
128,iostream((streambuf *)this)
129#endif
130,bufsize(0), gbuf(NULL), pbuf(NULL)
131{
132#ifdef HAVE_OLD_IOSTREAM
133 init((streambuf *)this);
134#endif
135
136 timeout = to;
137 setError(throwflag);
138
139 allocate(size);
140 Socket::state = CONNECTED;
141}
142
143UnixStream::UnixStream(const char* pathname, int size, bool throwflag, timeout_t to) :
144Socket(AF_UNIX, SOCK_STREAM, 0), streambuf(),
145#ifdef HAVE_OLD_IOSTREAM
146iostream(),
147#else
148iostream((streambuf *)this),
149#endif
150bufsize(0), gbuf(NULL), pbuf(NULL)
151{
152#ifdef HAVE_OLD_IOSTREAM
153 init((streambuf *)this);
154#endif
155 timeout = to;
156 setError(throwflag);
157 connect(pathname, size);
158}
159
160UnixStream::~UnixStream()
161{
162 endStream();
163}
164
165void UnixStream::connect(const char* pathname, int size)
166{
167 struct sockaddr_un addr;
168 socklen_t len;
169 unsigned slen = strlen(pathname);
170
171 if(slen > sizeof(addr.sun_path))
172 slen = sizeof(addr.sun_path);
173
174 memset(&addr, 0, sizeof(addr));
175 addr.sun_family = AF_UNIX;
176 memcpy( addr.sun_path, pathname, slen );
177#ifdef __SUN_LEN
178 len = sizeof(addr.sun_len) + strlen(addr.sun_path) + sizeof(addr.sun_family) + 1;
179 addr.sun_len = len;
180#else
181 len = strlen(addr.sun_path) + sizeof(addr.sun_family);
182#endif
183 if(::connect(so, (struct sockaddr *)&addr, len) != 0) {
184 connectError();
185 endSocket();
186 return;
187 }
188
189 allocate(size);
190 Socket::state = CONNECTED;
191}
192
193UnixStream::UnixStream(bool throwflag) :
194Socket(PF_UNIX, SOCK_STREAM, 0), streambuf(),
195#ifdef HAVE_OLD_IOSTREAM
196iostream(),
197#else
198iostream((streambuf *)this),
199#endif
200timeout(0), bufsize(0), gbuf(NULL), pbuf(NULL)
201{
202#ifdef HAVE_OLD_IOSTREAM
203 init((streambuf *)this);
204#endif
205 setError(throwflag);
206}
207
208UnixStream::UnixStream(const UnixStream &source) :
209Socket(dup(source.so)), streambuf(),
210#ifdef HAVE_OLD_IOSTREAM
211iostream()
212#else
213iostream((streambuf *)this)
214#endif
215{
216#ifdef HAVE_OLD_IOSTREAM
217 init((streambuf *)this);
218#endif
219 bufsize = source.bufsize;
220 allocate(bufsize);
221}
222
223void UnixStream::endStream(void)
224{
225 if(bufsize)
226 sync();
227
228 if(gbuf)
229 delete[] gbuf;
230 if(pbuf)
231 delete[] pbuf;
232 gbuf = pbuf = NULL;
233 bufsize = 0;
234 endSocket();
235}
236
237void UnixStream::allocate(int size)
238{
239 if(size < 2) {
240 bufsize = 1;
241 return;
242 }
243
244 gbuf = new char[size];
245 pbuf = new char[size];
246 if(!pbuf || !gbuf) {
247 error(errResourceFailure);
248 return;
249 }
250 bufsize = size;
251 clear();
252
253#if (defined(__GNUC__) && (__GNUC__ < 3)) && !defined(WIN32) && !defined(STLPORT)
254 setb(gbuf, gbuf + size, 0);
255#endif
256 setg(gbuf, gbuf + size, gbuf + size);
257 setp(pbuf, pbuf + size);
258}
259
260int UnixStream::doallocate()
261{
262 if(bufsize)
263 return 0;
264
265 allocate(1);
266 return 1;
267}
268
269int UnixStream::uflow(void)
270{
271 int ret = underflow();
272
273 if (ret == EOF)
274 return EOF;
275
276 if (bufsize != 1)
277 gbump(1);
278
279 return ret;
280}
281
282int UnixStream::underflow(void)
283{
284 int rlen = 1;
285 unsigned char ch;
286
287 if(bufsize == 1) {
288 if(Socket::state == STREAM)
289 rlen = ::read(so, (char *)&ch, 1);
290 else if(timeout && !Socket::isPending(pendingInput, timeout)) {
291 clear(ios::failbit | rdstate());
292 error(errTimeout);
293 return EOF;
294 }
295 else
296 rlen = ::recv(so, (char *)&ch, 1, 0);
297 if(rlen < 1) {
298 if(rlen < 0) {
299 clear(ios::failbit | rdstate());
300 error(errInput);
301 }
302 return EOF;
303 }
304 return ch;
305 }
306
307 if(!gptr())
308 return EOF;
309
310 if(gptr() < egptr())
311 return (unsigned char)*gptr();
312
313 rlen = (gbuf + bufsize) - eback();
314 if(Socket::state == STREAM)
315 rlen = ::read(so, (char *)eback(), rlen);
316 else if(timeout && !Socket::isPending(pendingInput, timeout)) {
317 clear(ios::failbit | rdstate());
318 error(errTimeout);
319 return EOF;
320 }
321 else
322 rlen = ::recv(so, (char *)eback(), rlen, 0);
323 if(rlen < 1) {
324 if(rlen < 0) {
325 clear(ios::failbit | rdstate());
326 error(errInput);
327 }
328 return EOF;
329 }
330
331 setg(eback(), eback(), eback() + rlen);
332 return (unsigned char) *gptr();
333}
334
335bool UnixStream::isPending(Pending pending, timeout_t timeout)
336{
337 if(pending == pendingInput && in_avail())
338 return true;
339 else if(pending == pendingOutput)
340 flush();
341
342 return Socket::isPending(pending, timeout);
343}
344
345int UnixStream::sync(void)
346{
347 overflow(EOF);
348 setg(gbuf, gbuf + bufsize, gbuf + bufsize);
349 return 0;
350}
351
352int UnixStream::overflow(int c)
353{
354 unsigned char ch;
355 int rlen, req;
356
357 if(bufsize == 1) {
358 if(c == EOF)
359 return 0;
360
361 ch = (unsigned char)(c);
362 if(Socket::state == STREAM)
363 rlen = ::write(so, (const char *)&ch, 1);
364 else
365 rlen = ::send(so, (const char *)&ch, 1, 0);
366 if(rlen < 1) {
367 if(rlen < 0) {
368 clear(ios::failbit | rdstate());
369 error(errOutput);
370 }
371 return EOF;
372 }
373 else
374 return c;
375 }
376
377 if(!pbase())
378 return EOF;
379
380 req = pptr() - pbase();
381 if(req) {
382 if(Socket::state == STREAM)
383 rlen = ::write(so, (const char *)pbase(), req);
384 else
385 rlen = ::send(so, (const char *)pbase(), req, 0);
386 if(rlen < 1) {
387 if(rlen < 0) {
388 clear(ios::failbit | rdstate());
389 error(errOutput);
390 }
391 return EOF;
392 }
393 req -= rlen;
394 }
395
396 // if write "partial", rebuffer remainder
397
398 if(req)
399 memcpy(pptr(), pptr() + rlen, req);
400 setp(pbuf + req, pbuf + bufsize);
401
402 if(c != EOF) {
403 *pptr() = (unsigned char)c;
404 pbump(1);
405 }
406 return c;
407}
408
409unixstream::unixstream() :
410UnixStream()
411{
412 setError(false); /* no exceptions */
413}
414
415unixstream::unixstream(const char *pathname, int buf) :
416UnixStream()
417{
418 setError(false);
419 open(pathname, buf);
420}
421
422unixstream::unixstream(UnixSocket &server, int buf) :
423UnixStream()
424{
425 setError(false);
426 open(server, buf);
427}
428
429bool unixstream::operator!() const
430{
431 return (Socket::state != CONNECTED) ? true : false;
432}
433
434void unixstream::open(UnixSocket &unixsock, int buf)
435{
436 endStream();
437 so = accept(unixsock.so, NULL, NULL);
438 if(so == INVALID_SOCKET)
439 return;
440
441 allocate(buf);
442 Socket::state = CONNECTED;
443}
444
445void unixstream::close(void)
446{
447 if(Socket::state == AVAILABLE)
448 return;
449
450 endStream();
451 so = socket(AF_UNIX, SOCK_STREAM, 0);
452 if(so != INVALID_SOCKET)
453 Socket::state = AVAILABLE;
454}
455
456UnixSession::UnixSession(const char* pathname, int size, int pri, int stack) :
457Thread(pri, stack), UnixStream()
458{
459 struct sockaddr_un addr;
460 unsigned slen = strlen(pathname);
461
462 if(slen > sizeof(addr.sun_path))
463 slen = sizeof(addr.sun_path);
464 socklen_t len;
465 setCompletion(false);
466 setError(false);
467 allocate(size);
468
469 memset(&addr, 0, sizeof(addr));
470 addr.sun_family = AF_UNIX;
471 memcpy( addr.sun_path, pathname, slen );
472
473#ifdef __SUN_LEN
474 len = sizeof(addr.sun_len) + strlen(addr.sun_path) + sizeof(addr.sun_family) + 1;
475 addr.sun_len = len;
476#else
477 len = strlen(addr.sun_path) + sizeof(addr.sun_family);
478#endif
479 if(::connect(so, (struct sockaddr *)&addr, len ) != 0) {
480 #ifdef WIN32
481 if( WSAGetLastError() == WAEISCONN )
482 #else
483 if( EINPROGRESS == errno )
484 #endif
485 {
486 Socket::state = CONNECTING;
487 }
488 else {
489 endSocket();
490 Socket::state = INITIAL;
491 }
492 return;
493 }
494
495 setCompletion(true);
496 Socket::state = CONNECTED;
497}
498
499
500UnixSession::UnixSession(UnixSocket &s,int size, int pri, int stack) :
501Thread(pri, stack), UnixStream(s, size)
502{
503 setCompletion(true);
504 setError(false);
505}
506
507UnixSession::~UnixSession()
508{
509 terminate();
510 endStream();
511}
512
513int UnixSession::waitConnection(timeout_t timeout)
514{
515 long sockopt = 0;
516 socklen_t len = sizeof(sockopt);
517
518 switch(Socket::state) {
519 case INITIAL:
520 return -1;
521 case CONNECTED:
522 break;
523 case CONNECTING:
524 if(!Socket::isPending(pendingOutput, timeout)) {
525 endSocket();
526 Socket::state = INITIAL;
527 return -1;
528 }
529
530 getsockopt(so, SOL_SOCKET, SO_ERROR, (char *)&sockopt, &len);
531 if(sockopt) {
532 endSocket();
533 Socket::state = INITIAL;
534 return -1;
535 }
536 case AVAILABLE:
537 case BOUND:
538 case STREAM:
539 break;
540 }
541 Socket::state = CONNECTED;
542 return 0;
543}
544
545void UnixSession::initial(void)
546{
547 if(waitConnection(60000))
548 exit();
549}
550
551#endif // ndef WIN32
552#ifdef CCXX_NAMESPACES
553}
554#endif