2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 //%% (c) Copyright 1993, 1994 Hewlett-Packard Company
24 //%% (c) Copyright 1993, 1994 International Business Machines Corp.
25 //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
26 //%% (c) Copyright 1993, 1994 Novell, Inc.
27 //%% $TOG: mp_stream_socket.C /main/10 1998/03/19 18:58:53 mgreess $
32 * Copyright (c) 1990 by Sun Microsystems, Inc.
34 #include "tt_options.h"
36 #include "mp/mp_stream_socket.h"
42 #include <sys/socket.h>
47 #include "util/tt_global_env.h"
48 #include "util/tt_host.h"
49 #include "util/tt_port.h"
52 # include <mp/mp_rpc_fns.h>
54 # if defined(OPT_BUG_USL)
58 # include <netinet/tcp.h>
64 extern "C" unsigned long inet_addr(char *);
67 #include <arpa/inet.h>
69 #if defined(OPT_BUG_USL)
70 extern char *t_errlist[];
72 char *t_strerror(int t_errno)
74 return(t_errlist[t_errno]);
79 * Constructs a socket object. Using (char *)0 for host means use the
80 * current host. Specifying a portnum of 0 indicates that the first
81 * available port number should be chosen.
89 _hostaddr.sin_port = 0;
90 _hostaddr.sin_family = 0;
91 memset(&_hostaddr.sin_addr, 0, sizeof(struct in_addr));
96 _Tt_stream_socket(_Tt_host_ptr &host, int portnum)
100 memset(&_hostaddr, 0, sizeof(_hostaddr));
101 _hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);
102 _hostaddr.sin_port = htons(portnum);
103 _hostaddr.sin_family = AF_INET;
110 * Closes the connection on the socket.
120 if (_msgsock != _sock) {
126 int _Tt_stream_socket::
133 * Returns the file descriptor associated with a socket.
135 int _Tt_stream_socket::
139 if ((_msgsock != -1) || (accept() != -1)) {
151 * Returns the external port number attached to a socket.
153 int _Tt_stream_socket::
159 return((int)ntohs(_hostaddr.sin_port));
165 * Initializes a stream socket. This method must be called before using a
166 * socket to receive messages. The method assumes that if hostname is
167 * (char *)0 then this is the "from" end of a socket. Otherwise, it is
168 * assumed that this is an object which connects to a socket (presumed
169 * already open) on the host named in _host.
171 int _Tt_stream_socket::
172 init(int init_as_source)
177 _sock = t_open("/dev/tcp", O_RDWR, 0);
179 _tt_syslog( 0, LOG_ERR,
180 "_Tt_stream_socket::init(): t_open(): %s",
181 t_strerror( t_errno ) );
185 #if defined(linux) || defined(CSRG_BASED)
192 _sock = socket(AF_INET, SOCK_STREAM, 0);
194 _tt_syslog( 0, LOG_ERR,
195 "_Tt_stream_socket::init(): socket(): %m" );
199 if (-1==fcntl(_sock, F_SETFD, 1)) {
200 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
201 "fcntl(F_SETFD): %m");
204 _is_source = init_as_source;
205 if (init_as_source) { /* 'from' end of socket */
206 #if !defined(OPT_TLI)
208 if (setsockopt(_sock, SOL_SOCKET, SO_USELOOPBACK,
209 (char *)&optval, sizeof(int)) == -1) {
210 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
211 "setsockopt(SO_USELOOPBACK): %m" );
216 if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY,
217 (char *)&optval, sizeof(int)) == -1) {
218 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
219 "setsockopt(TCP_NODELAY): %m" );
222 if (bind(_sock, (struct sockaddr *)&_hostaddr,
223 sizeof(_hostaddr)) < 0) {
227 len = sizeof(sockaddr_in);
228 #if defined(_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
229 if (getsockname(_sock, (sockaddr *)&_hostaddr, (size_t *)&len)
232 if (getsockname(_sock, (sockaddr *)&_hostaddr, &len) < 0) {
236 return(listen(_sock,5) == 0);
240 if ((bind = (struct t_bind *)t_alloc(_sock, T_BIND, T_ADDR)) ==
241 (struct t_bind *)0) {
242 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
243 "t_alloc(T_BIND): %s",
244 t_strerror( t_errno ) );
247 // We use random port selection.
248 // This means that we always let t_bind choose a
249 // suitable port number for us and we ignore the
250 // portnum argument given in the constructor for this
251 // class. If we were ever to claim _Tt_stream_socket
252 // as a general C++ wrapper class for socket/TLI, this
253 // would have to change.
256 if (t_bind(_sock, bind, bind) < 0) {
257 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
258 "t_bind(): %s", t_strerror( t_errno ) );
259 t_free((char *)bind, T_BIND);
262 _port = ntohs(((sockaddr_in *)(bind->addr.buf))->sin_port);
263 t_free((char *)bind, T_BIND);
264 _srequest = (struct t_call *)t_alloc(_sock, T_CALL, T_ADDR);
265 if (_srequest == (t_call *)0) {
266 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
267 "t_alloc(T_CALL): %s",
268 t_strerror( t_errno ) );
272 } else { // 'to' end of socket
274 // If both Client and Server or (To and From) sockets are
275 // on the same host, it is better to use the localhost ip
276 // address. This removes ToolTalks dependency on the state
277 // of the network and permits standalone operation.
279 // In this instance 'local_host' is the host where ttsession
280 // is running. '_host' is the host where the 'procid'
281 // proccess is running. If both are on the same host, copy
282 // in the the localhost ip address rather than the actual
283 // "configured" ip address.
285 _Tt_host_ptr local_host;
286 if ( (_tt_global->get_local_host(local_host) ) &&
287 ( _host->stringaddr() == local_host->stringaddr() ) ) {
289 // _host_ == local_host => (both on same host)
291 _hostaddr.sin_addr.s_addr = inet_addr((char *)"127.0.0.1");
294 // _host_ != local_host => (on different hosts)
296 memcpy((char *)&_hostaddr.sin_addr,
297 (char *)_host->addr(),
298 _host->addr_length());
300 #if !defined(OPT_TLI)
301 // set up socket options to insure that a close will
302 // immediately send the message to a socket. This is
303 // essential for the use of sockets as signalling
306 if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY,
307 (char *)&optval, sizeof(int)) == -1) {
308 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
309 "setsockopt(TCP_NODELAY): %m" );
314 if (setsockopt(_sock, SOL_SOCKET, SO_USELOOPBACK,
315 (char *)&optval, sizeof(int)) == -1) {
316 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
317 "setsockopt(SO_USELOOPBACK): %m" );
322 if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR,
323 (char *)&optval, sizeof(int)) == -1) {
324 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
325 "setsockopt(SO_REUSEADDR): %m" );
331 // XXX: It's not at all clear that we need to do this
332 // anywhere.. default seems to be don't linger anyway.
333 if (setsockopt(_sock, SOL_SOCKET, ~SO_LINGER,
334 (char *)&optval, sizeof(int)) == -1) {
335 _tt_syslog( 0, LOG_WARNING, "_Tt_stream_socket::init(): "
336 "setsockopt(~SO_LINGER): %m" );
341 (struct sockaddr *)&_hostaddr,
342 sizeof(_hostaddr)) < 0) {
347 t_call *sndcall, *rcvcall;
349 if (t_bind(_sock, 0, 0) < 0) {
350 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
351 "t_bind(,0,0): %s", t_strerror( t_errno ) );
354 (void)_tt_tli_set_nodelay(_sock);
355 sndcall = (t_call *)t_alloc(_sock, T_CALL, 0);
357 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
358 "t_alloc(T_CALL,0): %s",
359 t_strerror( t_errno ) );
362 sndcall->addr.maxlen = sizeof(_hostaddr);
363 sndcall->addr.len = sizeof(_hostaddr);
364 sndcall->addr.buf = (char *)&_hostaddr;
365 sndcall->opt.len = 0;
366 sndcall->udata.len = 0;
367 rcvcall = (t_call *)t_alloc(_sock, T_CALL, T_OPT|T_ADDR);
369 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
370 "t_alloc(T_CALL, T_OPT|T_ADDR): %s",
371 t_strerror( t_errno ) );
372 if (t_free((char *)sndcall, T_CALL) < 0) {
373 _tt_syslog( 0, LOG_ERR, "t_free(): %s",
374 t_strerror( t_errno ) );
378 rcvcall->udata.maxlen = 0;
379 if (t_connect(_sock, sndcall, rcvcall) < 0) {
380 _tt_syslog( 0, LOG_ERR, "t_connect(): %s",
381 t_strerror( t_errno ) );
382 sndcall->addr.buf = 0;
383 if (t_free((char *)sndcall, T_CALL) < 0) {
384 _tt_syslog( 0, LOG_ERR, "t_free(sndcall): %s",
385 t_strerror( t_errno ) );
387 if (t_free((char *)rcvcall, T_CALL) < 0) {
388 _tt_syslog( 0, LOG_ERR, "t_free(rcvcall): %s",
389 t_strerror( t_errno ) );
394 sndcall->addr.buf = 0;
395 if (t_free((char *)sndcall, T_CALL) < 0) {
396 _tt_syslog( 0, LOG_ERR, "t_free(sndcall): %s",
397 t_strerror( t_errno ) );
399 if (t_free((char *)rcvcall, T_CALL) < 0) {
400 _tt_syslog( 0, LOG_ERR, "t_free(rcvcall): %s",
401 t_strerror( t_errno ) );
412 * Sends a message to a socket. Since the socket object is used by the mp
413 * primarily to signal clients, a close is done after the write to insure
414 * the message gets flushed out to the receiving socket.
416 * --> It would be extremely helpful if all the really "fatal" cases of
417 * failing to write to a socket can be identified since this information
418 * could be fed back to the mp server to determine when a process has
419 * died or is no longer listening on a socket.
421 int _Tt_stream_socket::
422 send(char *msg, int len)
427 if ((rval = ::send(_sock, msg, len, 0)) == len) {
434 #if defined(OPT_BUG_USL)
437 if ((rval = t_snd(_sock, msg, len, 0)) == len) {
440 _tt_syslog(0, LOG_ERR,
441 "==> ERROR FROM SEND: len = %d, rval = %d, err = %d\n",
450 int _Tt_stream_socket::
453 if (_msgsock == -1) {
455 #if defined(linux) || defined(CSRG_BASED)
456 socklen_t addrlen = sizeof(sockaddr_in);
458 int addrlen = sizeof(sockaddr_in);
462 #if defined(_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
463 _msgsock = ::accept(_sock, (struct sockaddr *)&saddr,
466 _msgsock = ::accept(_sock, (struct sockaddr *)&saddr,
470 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
474 if (-1==fcntl(_msgsock, F_SETFD, 1)) {
475 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
476 "fcntl(F_SETFD): %m");
480 struct t_call *call_data;
481 struct t_bind *bind_data;
483 call_data = (struct t_call *)t_alloc(_sock, T_CALL, T_ALL);
485 if (t_listen(_sock, call_data) < 0) {
486 _Tt_string errstr(t_strerror(t_errno));
487 if (t_errno == TSYSERR) {
489 errstr = errstr.cat(": ").cat(strerror(errno));
491 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
492 "t_listen(): %s", (char *) errstr );
495 // Since we expect no further connections on this
496 // endpoint, it would theoretically be possible
497 // to use the same fd in arg 1 and arg 2 of t_accept.
498 // This appears to be an actual advantage of TLI over
499 // sockets -- I don't think you can do this with
500 // sockets. However, I can't get it to work!
501 // So what I do is open a new endpoint, accept to
502 // that, and then close the original fd since we
503 // don't need it any more.
505 _msgsock = t_open("/dev/tcp", O_RDWR, 0);
507 _tt_syslog( 0, LOG_ERR,
508 "_Tt_stream_socket::accept(): "
510 t_strerror( t_errno ) );
511 t_free((char *)call_data, T_CALL);
514 bind_data = (struct t_bind *)t_alloc(_msgsock, T_BIND, T_ALL);
515 if (t_bind(_msgsock, bind_data, bind_data) < 0) {
516 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
517 "t_bind(): %s", t_strerror( t_errno ) );
518 if (bind_data) t_free((char *)bind_data, T_BIND);
519 if (call_data) t_free((char *)call_data, T_CALL);
523 rval = t_accept(_sock, _msgsock, call_data);
525 _tt_syslog( 0, LOG_ERR, "t_accept(): %s",
526 t_strerror( t_errno ) );
527 t_free((char *)bind_data, T_BIND);
528 t_free((char *)call_data, T_CALL);
531 (void)_tt_tli_set_nodelay(_msgsock);
532 if (-1==fcntl(_msgsock, F_SETFD, 1)) {
533 _tt_syslog( 0, LOG_ERR,
534 "_Tt_stream_socket::accept(): "
535 "fcntl(F_SETFD): %m");
538 t_free((char *)bind_data, T_BIND);
539 t_free((char *)call_data, T_CALL);
550 * Receives a message from a socket. This method will block if there is
551 * no input so if nonblocking is required it should be called only after
552 * the socket fd has been checked for activity.
554 * --> sockets can be set to be nonblocking. Should this be the default
555 * for the sockets the mp opens?
557 int _Tt_stream_socket::
558 recv(char *msg, int msglen)
562 if (_msgsock == -1 || accept() == -1) {
567 if ((rval = ::recv(_msgsock, msg, msglen, 0)) < 0) {
574 rval = t_rcv(_msgsock, msg, msglen, &flags);
576 if (t_errno == TLOOK) {
577 if (t_look(_msgsock) == T_DISCONNECT &&
578 t_rcvdis(_msgsock,(struct t_discon *)0) < 0) {
579 _tt_syslog( 0, LOG_ERR, "t_rcvdis(): %s",
580 t_strerror( t_errno ) );
586 _tt_syslog( 0, LOG_ERR, "t_rcv(): %s",
587 t_strerror( t_errno ) );
598 // read_would_block is not a predicate. It returns 1 if a read
599 // is safe (would not block. It returns 0 if a read would block,
600 // and -1 if there is some error condition.
602 int _Tt_stream_socket::
605 struct pollfd fds[1];
607 fds[0].fd = _msgsock;
608 fds[0].events = POLLIN;
611 while(-1 == poll(fds, (sizeof fds)/(sizeof (struct pollfd)), 0)) {
612 if (errno==EAGAIN || errno==EINTR) {
613 // interrupted, try again.
615 // something is wrong
620 if (0 != (fds[0].revents & (POLLHUP|POLLNVAL|POLLERR)) ) {
622 } else if (0 != (fds[0].revents & POLLIN) ) {