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