1 /*
2  *    Support routines for file descriptors (FD)
3  *
4  *	James Peterson, 1987
5  *
6  * Copyright (C) 1987 MCC
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation, and that the name of MCC not be used in
13  * advertising or publicity pertaining to distribution of the software without
14  * specific, written prior permission.  MCC makes no
15  * representations about the suitability of this software for any purpose.  It
16  * is provided "as is" without express or implied warranty.
17  *
18  * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20  * EVENT SHALL MCC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
24  * PERFORMANCE OF THIS SOFTWARE.
25  *
26  */
27 /*
28  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
29  *
30  * Permission is hereby granted, free of charge, to any person obtaining a
31  * copy of this software and associated documentation files (the "Software"),
32  * to deal in the Software without restriction, including without limitation
33  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34  * and/or sell copies of the Software, and to permit persons to whom the
35  * Software is furnished to do so, subject to the following conditions:
36  *
37  * The above copyright notice and this permission notice (including the next
38  * paragraph) shall be included in all copies or substantial portions of the
39  * Software.
40  *
41  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47  * DEALINGS IN THE SOFTWARE.
48  *
49  */
50 
51 #include <X11/Xpoll.h>          /* for XFD_* macros - must come before
52                                    scope.h include to avoid INT32 clash */
53 
54 #include "scope.h"
55 
56 #include <unistd.h>
57 #include <sys/uio.h>            /* for struct iovec, used by socket.h */
58 #include <sys/socket.h>         /* for AF_INET, SOCK_STREAM, ... */
59 #include <sys/ioctl.h>          /* for FIONCLEX, FIONBIO, ... */
60 #if !defined(FIOCLEX) && defined(HAVE_SYS_FILIO_H)
61 #include <sys/filio.h>
62 #endif
63 #include <fcntl.h>
64 #include <netinet/in.h>         /* struct sockaddr_in */
65 #include <sys/un.h>             /* struct sockaddr_un */
66 #include <netinet/tcp.h>
67 #include <netdb.h>              /* struct servent * and struct hostent * */
68 #include <errno.h>              /* for EINTR, EADDRINUSE, ... */
69 
70 #ifndef USE_XTRANS
71 #include <arpa/inet.h>          /* for inet_addr */
72 #endif
73 
74 /*
75   All of this code is to support the handling of file descriptors (FD).
76   The idea is to keep a table of the FDs that are in use and why.
77   For each FD that is open for input, we keep the name of a procedure
78   to call if input arrives for that FD.  When an FD is created
79   (by an open, pipe, socket, ...) declare that by calling UsingFD.
80   When it is no longer in use (close ...), call NotUsingFD.
81 */
82 
83 /* ************************************************************ */
84 /*								*/
85 /*								*/
86 /* ************************************************************ */
87 
88 void
InitializeFD(void)89 InitializeFD(void)
90 {
91     int i;
92 
93     enterprocedure("InitializeFD");
94     /* get the number of file descriptors the system will let us use */
95 #ifdef _SC_OPEN_MAX
96     MaxFD = sysconf(_SC_OPEN_MAX);
97 #elif defined(HAVE_GETDTABLESIZE)
98     MaxFD = getdtablesize();
99 #else
100     MaxFD = _NFILE - 1;
101 #endif
102     if (MaxFD > FD_SETSIZE) {
103         MaxFD = FD_SETSIZE;
104     }
105 
106     /* allocate space for a File Descriptor (FD) Table */
107     FDD = calloc(MaxFD, sizeof(struct FDDescriptor));
108     if (FDD == NULL) {
109         panic("Can't allocate memory for file descriptor table");
110     }
111     FDinfo = calloc(MaxFD, sizeof(struct fdinfo));
112     if (FDinfo == NULL) {
113         panic("Can't allocate memory for file descriptor info table");
114     }
115 
116     /* be sure all fd's are closed and marked not busy */
117     for (i = 0; i < MaxFD; i++) {
118         /* 0, 1, 2 are special (stdin, stdout, stderr) */
119         if (i > 2)
120             close(i);
121         /* FDD[i].Busy = false; - not needed since false==0 */
122     }
123 
124     /* save one FD for single file input or output like debugging */
125     /* also the getservbyname call is currently using an FD */
126     MaxFD -= 4;
127 
128     nFDsInUse = 0; /* stdin, stdout, stderr */
129     FD_ZERO(&ReadDescriptors);
130     HighestFD = 0;
131 
132     UsingFD(fileno(stdin), NULL, NULL, NULL);
133     UsingFD(fileno(stdout), NULL, NULL, NULL);
134     UsingFD(fileno(stderr), NULL, NULL, NULL);
135 }
136 
137 /* ************************************************************ */
138 
139 void
UsingFD(FD fd,void (* Handler)(int),void (* FlushHandler)(int),XtransConnInfo trans_conn)140 UsingFD(FD fd,
141         void (*Handler) (int),
142         void (*FlushHandler) (int),
143         XtransConnInfo trans_conn)
144 {
145     if (FDD[fd].Busy)
146         NotUsingFD(fd);
147     nFDsInUse += 1;
148 
149     FDD[fd].Busy = true;
150     FDD[fd].InputHandler = Handler;
151     FDD[fd].FlushHandler = FlushHandler;
152 #ifdef USE_XTRANS
153     FDD[fd].trans_conn = trans_conn;
154 #endif
155     if (Handler == NULL)
156         FD_CLR(fd, &ReadDescriptors); /* clear fd bit */
157     else
158         FD_SET(fd, &ReadDescriptors); /* set fd bit */
159 
160     if (fd > HighestFD)
161         HighestFD = fd;
162 
163     if (nFDsInUse >= MaxFD)
164         panic("no more FDs");
165 
166     debug(128,
167           (stderr, "Using FD %d, %d of %d in use\n", fd, nFDsInUse, MaxFD));
168 }
169 
170 /* ************************************************************ */
171 
172 void
NotUsingFD(FD fd)173 NotUsingFD(FD fd)
174 {
175     debug(128, (stderr, "Not Using FD %d\n", fd));
176 
177     if (FDD[fd].Busy)
178         nFDsInUse -= 1;
179 
180     FDD[fd].Busy = false;
181     FD_CLR(fd, &ReadDescriptors); /* clear fd bit */
182 
183     while (!FDD[HighestFD].Busy && HighestFD > 0)
184         HighestFD -= 1;
185 
186     debug(128, (stderr, "Highest FD %d, in use %d\n", HighestFD, nFDsInUse));
187 }
188 
189 /* ************************************************************ */
190 
191 #ifdef USE_XTRANS
192 XtransConnInfo
GetXTransConnInfo(FD fd)193 GetXTransConnInfo(FD fd)
194 {
195     return FDD[fd].trans_conn;
196 }
197 #endif
198 
199 /* ************************************************************ */
200 
201 void
CloseFD(FD fd)202 CloseFD(FD fd)
203 {
204 #ifdef USE_XTRANS
205     XtransConnInfo conn = GetXTransConnInfo(fd);
206 
207     if (conn)
208         _X11TransClose(conn);
209     else
210 #endif
211         close(fd);
212 
213     NotUsingFD(fd);
214 }
215 
216 /* ************************************************************ */
217 
218 static void
EOFonFD(FD fd)219 EOFonFD(FD fd)
220 {
221     enterprocedure("EOFonFD");
222     debug(128, (stderr, "EOF on %d\n", fd));
223     CloseFD(fd);
224 }
225 
226 FD
AcceptConnection(FD ConnectionSocket)227 AcceptConnection(FD ConnectionSocket)
228 {
229     FD ClientFD;
230     struct sockaddr_in from;
231     socklen_t len = sizeof(from);
232     int tmp = 1;
233 
234     enterprocedure("ConnectToClient");
235 
236     ClientFD = accept(ConnectionSocket, (struct sockaddr *) &from, &len);
237     debug(4, (stderr, "Connect To Client: FD %d\n", ClientFD));
238     if (ClientFD < 0 && errno == EWOULDBLOCK) {
239         debug(4, (stderr, "Almost blocked accepting FD %d\n", ClientFD));
240         panic("Can't connect to Client");
241     }
242     if (ClientFD < 0) {
243         debug(4, (stderr, "NewConnection: error %d\n", errno));
244         panic("Can't connect to Client");
245     }
246 
247 #ifdef FD_CLOEXEC
248     (void) fcntl(ClientFD, F_SETFD, FD_CLOEXEC);
249 #else
250     (void) ioctl(ClientFD, FIOCLEX, 0);
251 #endif
252     /* ultrix reads hang on Unix sockets, hpux reads fail */
253 #if defined(O_NONBLOCK) && (!defined(ultrix) && !defined(hpux))
254     (void) fcntl(ClientFD, F_SETFL, O_NONBLOCK);
255 #else
256 #ifdef FIOSNBIO
257     ioctl(ClientFD, FIOSNBIO, &ON);
258 #else
259     (void) fcntl(ClientFD, F_SETFL, FNDELAY);
260 #endif
261 #endif
262     (void) setsockopt(ClientFD, IPPROTO_TCP, TCP_NODELAY, (char *) &tmp,
263                       sizeof(int));
264     return (ClientFD);
265 }
266 
267 FD
MakeConnection(const char * server,short port,int report,XtransConnInfo * trans_conn)268 MakeConnection(const char *server, short port, int report,
269                XtransConnInfo *trans_conn  /* transport connection object */)
270 {
271     FD ServerFD;
272 
273 #ifdef USE_XTRANS
274     char address[256];
275     int connect_stat;
276     const char *protocols[] = { "local", "unix", "tcp", "inet6", "inet", NULL };
277     const char **s;
278 
279     enterprocedure("ConnectToServer");
280     s = protocols;
281     while (*s) {
282         *trans_conn = NULL;
283         snprintf(address, sizeof(address), "%s/%s:%ld",
284                  *s++, server, port - ServerBasePort);
285         debug(4, (stderr, "Trying %s ", address));
286         *trans_conn = _X11TransOpenCOTSClient(address);
287         if (*trans_conn == NULL) {
288             debug(1, (stderr, "OpenCOTSClient %s failed\n", address));
289             continue;
290         }
291         debug(4, (stderr, "Opened "));
292         if ((connect_stat = _X11TransConnect(*trans_conn, address)) < 0) {
293             if ((connect_stat != TRANS_TRY_CONNECT_AGAIN) ||
294                 (_X11TransConnect(*trans_conn, address) < 0)) {
295                 _X11TransClose(*trans_conn);
296                 *trans_conn = NULL;
297                 debug(1, (stderr, "TransConnect %s failed\n", address));
298                 continue;
299             }
300         }
301         debug(4, (stderr, "Connected\n"));
302         break;
303     }
304     if (*trans_conn == NULL) {
305         panic("Can't open connection to Server");
306     }
307 
308     ServerFD = _X11TransGetConnectionNumber(*trans_conn);
309 #else                           /* !USE_XTRANS */
310     char HostName[512];
311     struct sockaddr_in sin;
312     struct sockaddr_un saun;
313     struct sockaddr *saddr;
314     int salen;
315     struct hostent *hp;
316     int tmp = 1;
317 #ifndef	SO_DONTLINGER
318     struct linger linger;
319 #endif                          /* SO_DONTLINGER */
320 
321     enterprocedure("ConnectToServer");
322 
323     /* establish a socket to the name server for this host */
324     /* determine the host machine for this process */
325     if (*server == '\0') {
326         saun.sun_family = AF_UNIX;
327         snprintf(saun.sun_path, sizeof(saun.sun_path),
328                  "/tmp/.X11-unix/X%d", port - 6000);
329         salen = sizeof(saun.sun_family) + strlen(saun.sun_path) + 1;
330         saddr = (struct sockaddr *) &saun;
331     }
332     else {
333         debug(4, (stderr, "try to connect on %s\n", server));
334 
335         bzero((char *) &sin, sizeof(sin));
336         sin.sin_addr.s_addr = inet_addr(server);
337         if ((long) sin.sin_addr.s_addr == -1) {
338             hp = gethostbyname(server);
339             if (hp == 0) {
340                 perror("gethostbyname failed");
341                 debug(1, (stderr, "gethostbyname failed for %s\n", server));
342                 panic("Can't open connection to Server");
343             }
344             memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
345         }
346 
347         sin.sin_family = AF_INET;
348 
349         if (port == ScopePort && strcmp(server, ScopeHost) == 0) {
350             char error_message[100];
351 
352             snprintf(error_message, sizeof(error_message),
353                      "Trying to attach to myself: %s,%d\n",
354                      server, sin.sin_port);
355             panic(error_message);
356         }
357 
358         sin.sin_port = htons(port);
359         salen = sizeof(sin);
360         saddr = (struct sockaddr *) &sin;
361     }
362 
363     ServerFD = socket(saddr->sa_family, SOCK_STREAM, 0);
364     if (ServerFD < 0) {
365         perror("socket() to Server failed");
366         debug(1, (stderr, "socket failed\n"));
367         panic("Can't open connection to Server");
368     }
369     (void) setsockopt(ServerFD, SOL_SOCKET, SO_REUSEADDR, (char *) NULL, 0);
370 #ifdef SO_USELOOPBACK
371     (void) setsockopt(ServerFD, SOL_SOCKET, SO_USELOOPBACK, (char *) NULL, 0);
372 #endif
373     (void) setsockopt(ServerFD, IPPROTO_TCP, TCP_NODELAY, (char *) &tmp,
374                       sizeof(int));
375 #ifdef	SO_DONTLINGER
376     (void) setsockopt(ServerFD, SOL_SOCKET, SO_DONTLINGER, (char *) NULL, 0);
377 #else                           /* SO_DONTLINGER */
378     linger.l_onoff = 0;
379     linger.l_linger = 0;
380     (void) setsockopt(ServerFD, SOL_SOCKET, SO_LINGER, (char *) &linger,
381                       sizeof linger);
382 #endif                          /* SO_DONTLINGER */
383 
384     /* ******************************************************** */
385     /* try to connect to Server */
386 
387     if (connect(ServerFD, saddr, salen) < 0) {
388         debug(4, (stderr, "connect returns errno of %d\n", errno));
389         if (errno != 0)
390             if (report)
391                 perror("connect");
392         switch (errno) {
393         case ECONNREFUSED:
394             /* experience says this is because there is no Server
395                to connect to */
396             (void) close(ServerFD);
397             debug(1, (stderr, "No Server\n"));
398             if (report)
399                 warn("Can't open connection to Server");
400             return (-1);
401 
402         default:
403             (void) close(ServerFD);
404             panic("Can't open connection to Server");
405         }
406     }
407 #endif                          /* USE_XTRANS */
408 
409     debug(4, (stderr, "Connect To Server: FD %d\n", ServerFD));
410     return (ServerFD);
411 }
412 
413 /* ************************************************************ */
414 /*								*/
415 /*     Main Loop -- wait for input from any source and Process  */
416 /*								*/
417 /* ************************************************************ */
418 
419 int
MainLoop(void)420 MainLoop(void)
421 {
422     enterprocedure("MainLoop");
423 
424     while (true) {
425         fd_set rfds, wfds, xfds;
426         short nfds;
427         short fd;
428 
429         /* wait for something */
430 
431         /* rfds = ReadDescriptors & ~BlockedReadDescriptors; */
432         rfds = ReadDescriptors;
433         XFD_UNSET(&rfds, &BlockedReadDescriptors);
434 
435         xfds = rfds;
436 
437         /* wfds = ReadDescriptors & WriteDescriptors; */
438         XFD_ANDSET(&wfds, &ReadDescriptors, &WriteDescriptors);
439 
440         debug(128,
441               (stderr,
442                "select %d, rfds = %#lx, wfds = %#lx, RD=%#lx, BRD=%#lx, WD=%#lx\n",
443                HighestFD + 1, __XFDS_BITS(&rfds, 0), __XFDS_BITS(&wfds, 0),
444                __XFDS_BITS(&ReadDescriptors, 0),
445                __XFDS_BITS(&BlockedReadDescriptors, 0),
446                __XFDS_BITS(&WriteDescriptors, 0)));
447 
448         if (Interrupt || (!XFD_ANYSET(&rfds) && !XFD_ANYSET(&wfds))) {
449             ReadCommands();
450             Interrupt = 0;
451             continue;
452         }
453         nfds =
454             select(HighestFD + 1, &rfds, &wfds, &xfds, (struct timeval *) NULL);
455         debug(128,
456               (stderr,
457                "select nfds = %d, rfds = %#lx, wfds = %#lx, xfds = %#lx\n",
458                nfds, __XFDS_BITS(&rfds, 0), __XFDS_BITS(&wfds, 0),
459                __XFDS_BITS(&xfds, 0)));
460 
461         if (nfds < 0) {
462             if (errno == EINTR)
463                 continue; /* to end of while loop */
464             debug(1, (stderr, "Bad select - errno = %d\n", errno));
465             if (errno == EBADF) {
466                 /* one of the bits in rfds is invalid, close down
467                    files until it goes away */
468                 EOFonFD(HighestFD);
469                 continue;
470             }
471 
472             if (Interrupt) {
473                 ReadCommands();
474                 Interrupt = 0;
475             }
476             else {
477                 panic("Select returns error");
478             }
479             continue; /* to end of while loop */
480         }
481 
482         if (nfds == 0) {
483             TimerExpired();
484             continue;
485         }
486 
487         /* check each fd to see if it has input */
488         for (fd = 0; fd <= HighestFD; fd++) {
489             /*
490                check all returned fd's; this prevents
491                starvation of later clients by earlier clients
492              */
493 
494             if (FD_ISSET(fd, &rfds)) {
495                 if (FDD[fd].InputHandler == NULL) {
496                     debug(1, (stderr, "FD %d has NULL handler\n", fd));
497                     panic("FD selected with no handler");
498                 }
499                 else
500                     (FDD[fd].InputHandler) (fd);
501             }
502             if (FD_ISSET(fd, &wfds)) {
503                 if (FDD[fd].FlushHandler == NULL) {
504                     panic("FD selected with no flush handler");
505                 }
506                 else
507                     (FDD[fd].FlushHandler) (fd);
508             }
509         }
510     }
511     return 0;
512 }
513