7ff79a83b1315fd4c791a79a312eaa484bb8dc0d
[oweals/cde.git] / cde / lib / tt / lib / mp / mp_stream_socket.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
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 $                                                   
28 /*
29  *
30  * mp_socket.cc
31  *
32  * Copyright (c) 1990 by Sun Microsystems, Inc.
33  */
34 #include "tt_options.h"
35 #include <stdio.h>
36 #include "mp/mp_stream_socket.h"
37 #if defined(linux)
38 #include <sys/poll.h>
39 #else
40 #include <poll.h>
41 #endif
42 #include <sys/socket.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <memory.h>
46 #include <errno.h>
47 #include "util/tt_global_env.h"
48 #include "util/tt_host.h"
49 #include "util/tt_port.h"
50
51 #if defined(OPT_TLI)
52 #       include <mp/mp_rpc_fns.h>
53 #       include <tiuser.h>
54 #  if defined(OPT_BUG_USL)
55    extern int t_errno;
56 #  endif
57 #else
58 #       include <netinet/tcp.h>
59 #endif
60
61 #include <sys/time.h>
62
63 #if defined(ultrix)
64 extern "C" unsigned long inet_addr(char *);
65 #endif
66
67 #include <arpa/inet.h>
68
69 #if defined(OPT_BUG_USL)
70 extern char *t_errlist[];
71
72 char *t_strerror(int t_errno)
73 {
74         return(t_errlist[t_errno]);
75 }
76 #endif
77
78 /* 
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.
82  */
83 _Tt_stream_socket::
84 _Tt_stream_socket()
85 {
86         _is_source = 0;
87         _msgsock = -1;
88         _sock = -1;
89         _hostaddr.sin_port = 0;
90         _hostaddr.sin_family = 0;
91         memset(&_hostaddr.sin_addr, 0, sizeof(struct in_addr));
92
93 }
94
95 _Tt_stream_socket::
96 _Tt_stream_socket(_Tt_host_ptr &host, int portnum)
97 {
98         _msgsock = -1;
99         _host = host;
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;
104         _is_source = 0;
105         _sock = -1;
106 }
107
108
109 /* 
110  * Closes the connection on the socket.
111  */
112 _Tt_stream_socket::
113 ~_Tt_stream_socket()
114 {
115 #ifndef OPT_TLI
116         close(_sock);
117         close(_msgsock);
118 #else
119         t_close(_sock);
120         if (_msgsock != _sock) {
121                 t_close(_msgsock);
122         }
123 #endif
124 }
125
126 int _Tt_stream_socket::
127 sock()
128 {
129         return(_sock);
130 }
131
132 /* 
133  * Returns the file descriptor associated with a socket.
134  */
135 int _Tt_stream_socket::
136 fd()
137 {
138         if (_is_source) {
139                 if ((_msgsock != -1) || (accept() != -1)) {
140                         return(_msgsock);
141                 } else {
142                         return(-1);
143                 }
144         } else {
145                 return(_sock);
146         }
147 }
148
149
150 /* 
151  * Returns the external port number attached to a socket.
152  */
153 int _Tt_stream_socket::
154 port()
155 {
156 #if defined(OPT_TLI)
157         return(_port);
158 #else
159         return((int)ntohs(_hostaddr.sin_port));
160 #endif
161 }
162
163
164 /* 
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.
170  */
171 int _Tt_stream_socket::
172 init(int init_as_source)
173 {
174         _Tt_host        host;
175
176 #if defined(OPT_TLI)
177         _sock = t_open("/dev/tcp", O_RDWR, 0);
178         if (_sock < 0) {
179                 _tt_syslog( 0, LOG_ERR,
180                             "_Tt_stream_socket::init(): t_open(): %s",
181                             t_strerror( t_errno ) );
182                 return 0;
183         }
184 #else
185 #if defined(linux) || defined(CSRG_BASED)
186         socklen_t       len;
187 #else
188         int             len;
189 #endif
190         int             optval = 1;
191
192         _sock = socket(AF_INET, SOCK_STREAM, 0);
193         if (_sock < 0) {
194                 _tt_syslog( 0, LOG_ERR,
195                             "_Tt_stream_socket::init(): socket(): %m" );
196                 return 0;
197         }
198 #endif
199         if (-1==fcntl(_sock, F_SETFD, 1)) {
200                 _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
201                             "fcntl(F_SETFD): %m");
202         }               
203
204         _is_source = init_as_source;
205         if (init_as_source) { /* 'from' end of socket */
206 #if !defined(OPT_TLI)
207 #ifndef linux
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" );
212                         close(_sock);
213                         return(0);
214                 }
215 #endif
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" );
220                 }
221
222                 if (bind(_sock, (struct sockaddr *)&_hostaddr,
223                          sizeof(_hostaddr)) < 0) {
224                         close(_sock);
225                         return(0);
226                 }
227                 len = sizeof(sockaddr_in);
228 #if defined(_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
229                 if (getsockname(_sock, (sockaddr *)&_hostaddr, (size_t *)&len)
230                                                         < 0) {
231 #else
232                 if (getsockname(_sock, (sockaddr *)&_hostaddr, &len) < 0) {
233 #endif
234                         return(0);
235                 }
236                 return(listen(_sock,5) == 0);
237 #else
238                 struct t_bind   *bind;
239
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 ) );
245                         return(0);
246                 }
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.
254                 bind->addr.len = 0;
255                 bind->qlen = 8;
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);
260                         return(0);
261                 }
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 ) );
269                         return(0);
270                 }
271 #endif // !OPT_TLI
272         } else { // 'to' end of socket
273                 //
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.
278                 //
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.
284                 // 
285                 _Tt_host_ptr    local_host;
286                 if ( (_tt_global->get_local_host(local_host) ) && 
287                         ( _host->stringaddr() == local_host->stringaddr() ) ) {
288                         //
289                         // _host_ == local_host => (both on same host)
290                         //
291                         _hostaddr.sin_addr.s_addr = inet_addr((char *)"127.0.0.1");         
292                 } else {
293                         //
294                         // _host_ != local_host => (on different hosts)
295                         //
296                         memcpy((char *)&_hostaddr.sin_addr,
297                         (char *)_host->addr(),
298                         _host->addr_length());
299                 }          
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 
304                 // mechanisms.
305
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" );
310                 }
311
312
313 #ifndef linux
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" );
318                         close(_sock);
319                         return(0);
320                 }
321 #endif
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" );
326                         close(_sock);
327                         return(0);
328                 }
329
330 #if defined(sun)
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" );
337                 }
338                 
339 #endif // sun
340                 if (connect(_sock,
341                             (struct sockaddr *)&_hostaddr,
342                             sizeof(_hostaddr)) < 0) {
343                         close(_sock);
344                         return(0);
345                 }
346 #else
347                 t_call          *sndcall, *rcvcall;
348
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 ) );
352                         return(0);
353                 }
354                 (void)_tt_tli_set_nodelay(_sock);
355                 sndcall = (t_call *)t_alloc(_sock, T_CALL, 0);
356                 if (sndcall == 0) {
357                         _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::init(): "
358                                     "t_alloc(T_CALL,0): %s",
359                                     t_strerror( t_errno ) );
360                         return(0);
361                 }
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);
368                 if (rcvcall == 0) {
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 ) );
375                         }
376                         return(0);
377                 }
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 ) );
386                         }
387                         if (t_free((char *)rcvcall, T_CALL) < 0) {
388                                 _tt_syslog( 0, LOG_ERR, "t_free(rcvcall): %s",
389                                             t_strerror( t_errno ) );
390                         }
391                         t_close(_sock);
392                         return(0);
393                 }
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 ) );
398                 }
399                 if (t_free((char *)rcvcall, T_CALL) < 0) {
400                         _tt_syslog( 0, LOG_ERR, "t_free(rcvcall): %s",
401                                     t_strerror( t_errno ) );
402                 }
403 #endif                          // !OPT_TLI
404
405         }
406
407         return(1);
408 }
409
410
411 /* 
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.
415  * 
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.
420  */
421 int _Tt_stream_socket::
422 send(char *msg, int len)
423 {
424         int     rval;
425
426 #ifndef OPT_TLI
427         if ((rval = ::send(_sock, msg, len, 0)) == len) {
428                 return(rval);
429         } else {
430                 close(_sock);
431                 return(0);
432         }
433 #else
434 #if defined(OPT_BUG_USL)
435         t_sync(_sock);
436 #endif
437         if ((rval = t_snd(_sock, msg, len, 0)) == len) {
438                 return(rval);
439         } else {
440                 _tt_syslog(0, LOG_ERR,
441                            "==> ERROR FROM SEND: len = %d, rval = %d, err = %d\n",
442                            len, rval, t_errno);
443                 t_close(_sock);
444                 return(0);
445         }
446 #endif                          // !OPT_TLI
447 }
448
449
450 int _Tt_stream_socket::
451 accept()
452 {
453         if (_msgsock == -1) {
454 #ifndef OPT_TLI
455 #if defined(linux) || defined(CSRG_BASED)
456                 socklen_t               addrlen = sizeof(sockaddr_in);
457 #else
458                 int                     addrlen = sizeof(sockaddr_in);
459 #endif
460                 sockaddr_in             saddr;
461
462 #if defined(_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
463                 _msgsock = ::accept(_sock, (struct sockaddr *)&saddr,
464                                     (size_t *)&addrlen);
465 #else
466                 _msgsock = ::accept(_sock, (struct sockaddr *)&saddr,
467                                     &addrlen);
468 #endif
469                 if (_msgsock < 0) {
470                         _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
471                                     "accept(): %m" );
472                         return -1;
473                 }
474                 if (-1==fcntl(_msgsock, F_SETFD, 1)) {
475                         _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
476                                     "fcntl(F_SETFD): %m");
477                 }               
478 #else
479                 int                     rval;
480                 struct t_call          *call_data;
481                 struct t_bind          *bind_data;
482
483                 call_data = (struct t_call *)t_alloc(_sock, T_CALL, T_ALL);
484
485                 if (t_listen(_sock, call_data) < 0) {
486                         _Tt_string errstr(t_strerror(t_errno));
487                         if (t_errno == TSYSERR) {
488                                 // Add in errno info
489                                 errstr = errstr.cat(": ").cat(strerror(errno));
490                         }
491                         _tt_syslog( 0, LOG_ERR, "_Tt_stream_socket::accept(): "
492                                     "t_listen(): %s", (char *) errstr );
493                         return(-1);
494                 }
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.
504                 
505                 _msgsock = t_open("/dev/tcp", O_RDWR, 0);
506                 if (_msgsock < 0) {
507                         _tt_syslog( 0, LOG_ERR,
508                                     "_Tt_stream_socket::accept(): "
509                                     "t_open(): %s",
510                                     t_strerror( t_errno ) );
511                         t_free((char *)call_data, T_CALL);
512                         return -1;
513                 }
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);
520                         return -1;
521                 }
522                 
523                 rval = t_accept(_sock, _msgsock, call_data);
524                 if (rval == -1) {
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);
529                         return(-1);
530                 } else {
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");
536                         }               
537                 }
538                 t_free((char *)bind_data, T_BIND);
539                 t_free((char *)call_data, T_CALL);
540                 t_close(_sock);
541                 _sock = _msgsock;
542 #endif                          // !OPT_TLI
543         }
544
545         return(_msgsock);
546 }
547
548
549 /* 
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.
553
554 * --> sockets can be set to be nonblocking. Should this be the default
555 * for the sockets the mp opens?
556 */
557 int _Tt_stream_socket::
558 recv(char *msg, int msglen)
559 {
560         int rval;
561         
562         if (_msgsock == -1 || accept() == -1) {
563                 return(-1);
564         }
565         
566 #ifndef OPT_TLI
567         if ((rval = ::recv(_msgsock, msg, msglen, 0)) < 0) {
568                 close(_msgsock); 
569                 return(-1);
570         }
571 #else
572         int                     flags;
573         
574         rval = t_rcv(_msgsock, msg, msglen, &flags);
575         if (rval == -1) {
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 ) );
581                                 return(-1);
582                         } else {
583                                 return(0);
584                         }
585                 } else {
586                         _tt_syslog( 0, LOG_ERR, "t_rcv(): %s",
587                                     t_strerror( t_errno ) );
588                         return(-1);
589                 }
590         }
591 #endif                          // !OPT_TLI
592         msg[rval] = 0;
593         return(rval);
594 }
595
596
597
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.
601
602 int _Tt_stream_socket::
603 read_would_block()
604 {
605         struct pollfd fds[1];
606
607         fds[0].fd = _msgsock;
608         fds[0].events = POLLIN;
609         fds[0].revents = 0;
610
611         while(-1 == poll(fds, (sizeof fds)/(sizeof (struct pollfd)), 0)) {
612                 if (errno==EAGAIN || errno==EINTR) {
613                         // interrupted, try again.
614                 } else {
615                         // something is wrong
616                         return -1;
617                 }
618         }
619
620         if (0 != (fds[0].revents & (POLLHUP|POLLNVAL|POLLERR)) ) {
621                 return -1;
622         } else if (0 != (fds[0].revents & POLLIN) ) {
623                 return 1;
624         } else {
625                 return 0;
626         }
627 }
628
629