connection.c revision 4e185dc0
1/***********************************************************
2
3Copyright 1987, 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
26
27                        All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Digital not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45******************************************************************/
46/*****************************************************************
47 *  Stuff to create connections --- OS dependent
48 *
49 *      EstablishNewConnections, CreateWellKnownSockets, ResetWellKnownSockets,
50 *      CloseDownConnection,
51 *	OnlyListToOneClient,
52 *      ListenToAllClients,
53 *
54 *      (WaitForSomething is in its own file)
55 *
56 *      In this implementation, a client socket table is not kept.
57 *      Instead, what would be the index into the table is just the
58 *      file descriptor of the socket.  This won't work for if the
59 *      socket ids aren't small nums (0 - 2^8)
60 *
61 *****************************************************************/
62
63#include <X11/Xpoll.h>
64
65#ifdef HAVE_DIX_CONFIG_H
66#include <dix-config.h>
67#endif
68
69#ifdef WIN32
70#include <X11/Xwinsock.h>
71#endif
72#include <X11/X.h>
73#include <X11/Xproto.h>
74#define XSERV_t
75#define TRANS_SERVER
76#define TRANS_REOPEN
77#include <X11/Xtrans/Xtrans.h>
78#include <X11/Xtrans/Xtransint.h>
79#include <errno.h>
80#include <signal.h>
81#include <stdio.h>
82#include <stdlib.h>
83
84#ifndef WIN32
85#include <sys/socket.h>
86
87#if defined(TCPCONN)
88#include <netinet/in.h>
89#include <arpa/inet.h>
90#ifdef apollo
91#ifndef NO_TCP_H
92#include <netinet/tcp.h>
93#endif
94#else
95#ifdef CSRG_BASED
96#include <sys/param.h>
97#endif
98#include <netinet/tcp.h>
99#endif
100#include <arpa/inet.h>
101#endif
102
103#include <sys/uio.h>
104
105#endif                          /* WIN32 */
106#include "misc.h"               /* for typedef of pointer */
107#include "osdep.h"
108#include "opaque.h"
109#include "dixstruct.h"
110#include "xace.h"
111
112#define Pid_t pid_t
113
114#ifdef HAVE_GETPEERUCRED
115#include <ucred.h>
116#include <zone.h>
117#else
118#define zoneid_t int
119#endif
120
121#include "probes.h"
122
123struct ospoll   *server_poll;
124
125int MaxClients = 0;
126Bool NewOutputPending;          /* not yet attempted to write some new output */
127Bool NoListenAll;               /* Don't establish any listening sockets */
128
129static Bool RunFromSmartParent; /* send SIGUSR1 to parent process */
130Bool RunFromSigStopParent;      /* send SIGSTOP to our own process; Upstart (or
131                                   equivalent) will send SIGCONT back. */
132static char dynamic_display[7]; /* display name */
133Bool PartialNetwork;            /* continue even if unable to bind all addrs */
134static Pid_t ParentProcess;
135
136int GrabInProgress = 0;
137
138static void
139QueueNewConnections(int curconn, int ready, void *data);
140
141static void
142set_poll_client(ClientPtr client);
143
144static void
145set_poll_clients(void);
146
147static XtransConnInfo *ListenTransConns = NULL;
148static int *ListenTransFds = NULL;
149static int ListenTransCount;
150
151static void ErrorConnMax(XtransConnInfo /* trans_conn */ );
152
153static XtransConnInfo
154lookup_trans_conn(int fd)
155{
156    if (ListenTransFds) {
157        int i;
158
159        for (i = 0; i < ListenTransCount; i++)
160            if (ListenTransFds[i] == fd)
161                return ListenTransConns[i];
162    }
163
164    return NULL;
165}
166
167/* Set MaxClients */
168
169void
170InitConnectionLimits(void)
171{
172    MaxClients = MAXCLIENTS;
173
174#ifdef DEBUG
175    ErrorF("InitConnectionLimits: MaxClients = %d\n", MaxClients);
176#endif
177}
178
179/*
180 * If SIGUSR1 was set to SIG_IGN when the server started, assume that either
181 *
182 *  a- The parent process is ignoring SIGUSR1
183 *
184 * or
185 *
186 *  b- The parent process is expecting a SIGUSR1
187 *     when the server is ready to accept connections
188 *
189 * In the first case, the signal will be harmless, in the second case,
190 * the signal will be quite useful.
191 */
192static void
193InitParentProcess(void)
194{
195#if !defined(WIN32)
196    OsSigHandlerPtr handler;
197
198    handler = OsSignal(SIGUSR1, SIG_IGN);
199    if (handler == SIG_IGN)
200        RunFromSmartParent = TRUE;
201    OsSignal(SIGUSR1, handler);
202    ParentProcess = getppid();
203#endif
204}
205
206void
207NotifyParentProcess(void)
208{
209#if !defined(WIN32)
210    if (displayfd >= 0) {
211        if (write(displayfd, display, strlen(display)) != strlen(display))
212            FatalError("Cannot write display number to fd %d\n", displayfd);
213        if (write(displayfd, "\n", 1) != 1)
214            FatalError("Cannot write display number to fd %d\n", displayfd);
215        close(displayfd);
216        displayfd = -1;
217    }
218    if (RunFromSmartParent) {
219        if (ParentProcess > 1) {
220            kill(ParentProcess, SIGUSR1);
221        }
222    }
223    if (RunFromSigStopParent)
224        raise(SIGSTOP);
225#endif
226}
227
228static Bool
229TryCreateSocket(int num, int *partial)
230{
231    char port[20];
232
233    snprintf(port, sizeof(port), "%d", num);
234
235    return (_XSERVTransMakeAllCOTSServerListeners(port, partial,
236                                                  &ListenTransCount,
237                                                  &ListenTransConns) >= 0);
238}
239
240/*****************
241 * CreateWellKnownSockets
242 *    At initialization, create the sockets to listen on for new clients.
243 *****************/
244
245void
246CreateWellKnownSockets(void)
247{
248    int i;
249    int partial;
250
251    /* display is initialized to "0" by main(). It is then set to the display
252     * number if specified on the command line. */
253
254    if (NoListenAll) {
255        ListenTransCount = 0;
256    }
257    else if ((displayfd < 0) || explicit_display) {
258        if (TryCreateSocket(atoi(display), &partial) &&
259            ListenTransCount >= 1)
260            if (!PartialNetwork && partial)
261                FatalError ("Failed to establish all listening sockets");
262    }
263    else { /* -displayfd and no explicit display number */
264        Bool found = 0;
265        for (i = 0; i < 65536 - X_TCP_PORT; i++) {
266            if (TryCreateSocket(i, &partial) && !partial) {
267                found = 1;
268                break;
269            }
270            else
271                CloseWellKnownConnections();
272        }
273        if (!found)
274            FatalError("Failed to find a socket to listen on");
275        snprintf(dynamic_display, sizeof(dynamic_display), "%d", i);
276        display = dynamic_display;
277        LogSetDisplay();
278    }
279
280    ListenTransFds = xallocarray(ListenTransCount, sizeof (int));
281    if (ListenTransFds == NULL)
282        FatalError ("Failed to create listening socket array");
283
284    for (i = 0; i < ListenTransCount; i++) {
285        int fd = _XSERVTransGetConnectionNumber(ListenTransConns[i]);
286
287        ListenTransFds[i] = fd;
288        SetNotifyFd(fd, QueueNewConnections, X_NOTIFY_READ, NULL);
289
290        if (!_XSERVTransIsLocal(ListenTransConns[i]))
291            DefineSelf (fd);
292    }
293
294    if (ListenTransCount == 0 && !NoListenAll)
295        FatalError
296            ("Cannot establish any listening sockets - Make sure an X server isn't already running");
297
298#if !defined(WIN32)
299    OsSignal(SIGPIPE, SIG_IGN);
300    OsSignal(SIGHUP, AutoResetServer);
301#endif
302    OsSignal(SIGINT, GiveUp);
303    OsSignal(SIGTERM, GiveUp);
304    ResetHosts(display);
305
306    InitParentProcess();
307
308#ifdef XDMCP
309    XdmcpInit();
310#endif
311}
312
313void
314ResetWellKnownSockets(void)
315{
316    int i;
317
318    ResetOsBuffers();
319
320    for (i = 0; i < ListenTransCount; i++) {
321        int status = _XSERVTransResetListener(ListenTransConns[i]);
322
323        if (status != TRANS_RESET_NOOP) {
324            if (status == TRANS_RESET_FAILURE) {
325                /*
326                 * ListenTransConns[i] freed by xtrans.
327                 * Remove it from out list.
328                 */
329
330                RemoveNotifyFd(ListenTransFds[i]);
331                ListenTransFds[i] = ListenTransFds[ListenTransCount - 1];
332                ListenTransConns[i] = ListenTransConns[ListenTransCount - 1];
333                ListenTransCount -= 1;
334                i -= 1;
335            }
336            else if (status == TRANS_RESET_NEW_FD) {
337                /*
338                 * A new file descriptor was allocated (the old one was closed)
339                 */
340
341                int newfd = _XSERVTransGetConnectionNumber(ListenTransConns[i]);
342
343                ListenTransFds[i] = newfd;
344            }
345        }
346    }
347    for (i = 0; i < ListenTransCount; i++)
348        SetNotifyFd(ListenTransFds[i], QueueNewConnections, X_NOTIFY_READ, NULL);
349
350    ResetAuthorization();
351    ResetHosts(display);
352    /*
353     * restart XDMCP
354     */
355#ifdef XDMCP
356    XdmcpReset();
357#endif
358}
359
360void
361CloseWellKnownConnections(void)
362{
363    int i;
364
365    for (i = 0; i < ListenTransCount; i++) {
366        if (ListenTransConns[i] != NULL) {
367            _XSERVTransClose(ListenTransConns[i]);
368            ListenTransConns[i] = NULL;
369            if (ListenTransFds != NULL)
370                RemoveNotifyFd(ListenTransFds[i]);
371        }
372    }
373    ListenTransCount = 0;
374}
375
376static void
377AuthAudit(ClientPtr client, Bool letin,
378          struct sockaddr *saddr, int len,
379          unsigned int proto_n, char *auth_proto, int auth_id)
380{
381    char addr[128];
382    char client_uid_string[64];
383    LocalClientCredRec *lcc;
384
385#ifdef XSERVER_DTRACE
386    pid_t client_pid = -1;
387    zoneid_t client_zid = -1;
388#endif
389
390    if (!len)
391        strlcpy(addr, "local host", sizeof(addr));
392    else
393        switch (saddr->sa_family) {
394        case AF_UNSPEC:
395#if defined(UNIXCONN) || defined(LOCALCONN)
396        case AF_UNIX:
397#endif
398            strlcpy(addr, "local host", sizeof(addr));
399            break;
400#if defined(TCPCONN)
401        case AF_INET:
402            snprintf(addr, sizeof(addr), "IP %s",
403                     inet_ntoa(((struct sockaddr_in *) saddr)->sin_addr));
404            break;
405#if defined(IPv6) && defined(AF_INET6)
406        case AF_INET6:{
407            char ipaddr[INET6_ADDRSTRLEN];
408
409            inet_ntop(AF_INET6, &((struct sockaddr_in6 *) saddr)->sin6_addr,
410                      ipaddr, sizeof(ipaddr));
411            snprintf(addr, sizeof(addr), "IP %s", ipaddr);
412        }
413            break;
414#endif
415#endif
416        default:
417            strlcpy(addr, "unknown address", sizeof(addr));
418        }
419
420    if (GetLocalClientCreds(client, &lcc) != -1) {
421        int slen;               /* length written to client_uid_string */
422
423        strcpy(client_uid_string, " ( ");
424        slen = 3;
425
426        if (lcc->fieldsSet & LCC_UID_SET) {
427            snprintf(client_uid_string + slen,
428                     sizeof(client_uid_string) - slen,
429                     "uid=%ld ", (long) lcc->euid);
430            slen = strlen(client_uid_string);
431        }
432
433        if (lcc->fieldsSet & LCC_GID_SET) {
434            snprintf(client_uid_string + slen,
435                     sizeof(client_uid_string) - slen,
436                     "gid=%ld ", (long) lcc->egid);
437            slen = strlen(client_uid_string);
438        }
439
440        if (lcc->fieldsSet & LCC_PID_SET) {
441#ifdef XSERVER_DTRACE
442            client_pid = lcc->pid;
443#endif
444            snprintf(client_uid_string + slen,
445                     sizeof(client_uid_string) - slen,
446                     "pid=%ld ", (long) lcc->pid);
447            slen = strlen(client_uid_string);
448        }
449
450        if (lcc->fieldsSet & LCC_ZID_SET) {
451#ifdef XSERVER_DTRACE
452            client_zid = lcc->zoneid;
453#endif
454            snprintf(client_uid_string + slen,
455                     sizeof(client_uid_string) - slen,
456                     "zoneid=%ld ", (long) lcc->zoneid);
457            slen = strlen(client_uid_string);
458        }
459
460        snprintf(client_uid_string + slen, sizeof(client_uid_string) - slen,
461                 ")");
462        FreeLocalClientCreds(lcc);
463    }
464    else {
465        client_uid_string[0] = '\0';
466    }
467
468#ifdef XSERVER_DTRACE
469    XSERVER_CLIENT_AUTH(client->index, addr, client_pid, client_zid);
470#endif
471    if (auditTrailLevel > 1) {
472        if (proto_n)
473            AuditF("client %d %s from %s%s\n  Auth name: %.*s ID: %d\n",
474                   client->index, letin ? "connected" : "rejected", addr,
475                   client_uid_string, (int) proto_n, auth_proto, auth_id);
476        else
477            AuditF("client %d %s from %s%s\n",
478                   client->index, letin ? "connected" : "rejected", addr,
479                   client_uid_string);
480
481    }
482}
483
484XID
485AuthorizationIDOfClient(ClientPtr client)
486{
487    if (client->osPrivate)
488        return ((OsCommPtr) client->osPrivate)->auth_id;
489    else
490        return None;
491}
492
493/*****************************************************************
494 * ClientAuthorized
495 *
496 *    Sent by the client at connection setup:
497 *                typedef struct _xConnClientPrefix {
498 *                   CARD8	byteOrder;
499 *                   BYTE	pad;
500 *                   CARD16	majorVersion, minorVersion;
501 *                   CARD16	nbytesAuthProto;
502 *                   CARD16	nbytesAuthString;
503 *                 } xConnClientPrefix;
504 *
505 *     	It is hoped that eventually one protocol will be agreed upon.  In the
506 *        mean time, a server that implements a different protocol than the
507 *        client expects, or a server that only implements the host-based
508 *        mechanism, will simply ignore this information.
509 *
510 *****************************************************************/
511
512const char *
513ClientAuthorized(ClientPtr client,
514                 unsigned int proto_n, char *auth_proto,
515                 unsigned int string_n, char *auth_string)
516{
517    OsCommPtr priv;
518    Xtransaddr *from = NULL;
519    int family;
520    int fromlen;
521    XID auth_id;
522    const char *reason = NULL;
523    XtransConnInfo trans_conn;
524
525    priv = (OsCommPtr) client->osPrivate;
526    trans_conn = priv->trans_conn;
527
528    /* Allow any client to connect without authorization on a launchd socket,
529       because it is securely created -- this prevents a race condition on launch */
530    if (trans_conn->flags & TRANS_NOXAUTH) {
531        auth_id = (XID) 0L;
532    }
533    else {
534        auth_id =
535            CheckAuthorization(proto_n, auth_proto, string_n, auth_string,
536                               client, &reason);
537    }
538
539    if (auth_id == (XID) ~0L) {
540        if (_XSERVTransGetPeerAddr(trans_conn, &family, &fromlen, &from) != -1) {
541            if (InvalidHost((struct sockaddr *) from, fromlen, client))
542                AuthAudit(client, FALSE, (struct sockaddr *) from,
543                          fromlen, proto_n, auth_proto, auth_id);
544            else {
545                auth_id = (XID) 0;
546#ifdef XSERVER_DTRACE
547                if ((auditTrailLevel > 1) || XSERVER_CLIENT_AUTH_ENABLED())
548#else
549                if (auditTrailLevel > 1)
550#endif
551                    AuthAudit(client, TRUE,
552                              (struct sockaddr *) from, fromlen,
553                              proto_n, auth_proto, auth_id);
554            }
555
556            free(from);
557        }
558
559        if (auth_id == (XID) ~0L) {
560            if (reason)
561                return reason;
562            else
563                return "Client is not authorized to connect to Server";
564        }
565    }
566#ifdef XSERVER_DTRACE
567    else if ((auditTrailLevel > 1) || XSERVER_CLIENT_AUTH_ENABLED())
568#else
569    else if (auditTrailLevel > 1)
570#endif
571    {
572        if (_XSERVTransGetPeerAddr(trans_conn, &family, &fromlen, &from) != -1) {
573            AuthAudit(client, TRUE, (struct sockaddr *) from, fromlen,
574                      proto_n, auth_proto, auth_id);
575
576            free(from);
577        }
578    }
579    priv->auth_id = auth_id;
580    priv->conn_time = 0;
581
582#ifdef XDMCP
583    /* indicate to Xdmcp protocol that we've opened new client */
584    XdmcpOpenDisplay(priv->fd);
585#endif                          /* XDMCP */
586
587    XaceHook(XACE_AUTH_AVAIL, client, auth_id);
588
589    /* At this point, if the client is authorized to change the access control
590     * list, we should getpeername() information, and add the client to
591     * the selfhosts list.  It's not really the host machine, but the
592     * true purpose of the selfhosts list is to see who may change the
593     * access control list.
594     */
595    return ((char *) NULL);
596}
597
598static void
599ClientReady(int fd, int xevents, void *data)
600{
601    ClientPtr client = data;
602
603    if (xevents & X_NOTIFY_ERROR) {
604        CloseDownClient(client);
605        return;
606    }
607    if (xevents & X_NOTIFY_READ)
608        mark_client_ready(client);
609    if (xevents & X_NOTIFY_WRITE) {
610        ospoll_mute(server_poll, fd, X_NOTIFY_WRITE);
611        NewOutputPending = TRUE;
612    }
613}
614
615static ClientPtr
616AllocNewConnection(XtransConnInfo trans_conn, int fd, CARD32 conn_time)
617{
618    OsCommPtr oc;
619    ClientPtr client;
620
621    oc = malloc(sizeof(OsCommRec));
622    if (!oc)
623        return NullClient;
624    oc->trans_conn = trans_conn;
625    oc->fd = fd;
626    oc->input = (ConnectionInputPtr) NULL;
627    oc->output = (ConnectionOutputPtr) NULL;
628    oc->auth_id = None;
629    oc->conn_time = conn_time;
630    oc->flags = 0;
631    if (!(client = NextAvailableClient((void *) oc))) {
632        free(oc);
633        return NullClient;
634    }
635    client->local = ComputeLocalClient(client);
636    ospoll_add(server_poll, fd,
637               ospoll_trigger_edge,
638               ClientReady,
639               client);
640    set_poll_client(client);
641
642#ifdef DEBUG
643    ErrorF("AllocNewConnection: client index = %d, socket fd = %d\n",
644           client->index, fd);
645#endif
646#ifdef XSERVER_DTRACE
647    XSERVER_CLIENT_CONNECT(client->index, fd);
648#endif
649
650    return client;
651}
652
653/*****************
654 * EstablishNewConnections
655 *    If anyone is waiting on listened sockets, accept them.
656 *    Returns a mask with indices of new clients.  Updates AllClients
657 *    and AllSockets.
658 *****************/
659
660static Bool
661EstablishNewConnections(ClientPtr clientUnused, void *closure)
662{
663    int curconn = (int) (intptr_t) closure;
664    int newconn;       /* fd of new client */
665    CARD32 connect_time;
666    int i;
667    ClientPtr client;
668    OsCommPtr oc;
669    XtransConnInfo trans_conn, new_trans_conn;
670    int status;
671
672    connect_time = GetTimeInMillis();
673    /* kill off stragglers */
674    for (i = 1; i < currentMaxClients; i++) {
675        if ((client = clients[i])) {
676            oc = (OsCommPtr) (client->osPrivate);
677            if ((oc && (oc->conn_time != 0) &&
678                 (connect_time - oc->conn_time) >= TimeOutValue) ||
679                (client->noClientException != Success && !client->clientGone))
680                CloseDownClient(client);
681        }
682    }
683
684    if ((trans_conn = lookup_trans_conn(curconn)) == NULL)
685        return TRUE;
686
687    if ((new_trans_conn = _XSERVTransAccept(trans_conn, &status)) == NULL)
688        return TRUE;
689
690    newconn = _XSERVTransGetConnectionNumber(new_trans_conn);
691
692    _XSERVTransSetOption(new_trans_conn, TRANS_NONBLOCKING, 1);
693
694    if (trans_conn->flags & TRANS_NOXAUTH)
695        new_trans_conn->flags = new_trans_conn->flags | TRANS_NOXAUTH;
696
697    if (!AllocNewConnection(new_trans_conn, newconn, connect_time)) {
698        ErrorConnMax(new_trans_conn);
699    }
700    return TRUE;
701}
702
703static void
704QueueNewConnections(int fd, int ready, void *data)
705{
706    QueueWorkProc(EstablishNewConnections, NULL, (void *) (intptr_t) fd);
707}
708
709#define NOROOM "Maximum number of clients reached"
710
711/************
712 *   ErrorConnMax
713 *     Fail a connection due to lack of client or file descriptor space
714 ************/
715
716static void
717ConnMaxNotify(int fd, int events, void *data)
718{
719    XtransConnInfo trans_conn = data;
720    char order = 0;
721
722    /* try to read the byte-order of the connection */
723    (void) _XSERVTransRead(trans_conn, &order, 1);
724    if (order == 'l' || order == 'B' || order == 'r' || order == 'R') {
725        xConnSetupPrefix csp;
726        char pad[3] = { 0, 0, 0 };
727        int whichbyte = 1;
728        struct iovec iov[3];
729
730        csp.success = xFalse;
731        csp.lengthReason = sizeof(NOROOM) - 1;
732        csp.length = (sizeof(NOROOM) + 2) >> 2;
733        csp.majorVersion = X_PROTOCOL;
734        csp.minorVersion = X_PROTOCOL_REVISION;
735        if (((*(char *) &whichbyte) && (order == 'B' || order == 'R')) ||
736            (!(*(char *) &whichbyte) && (order == 'l' || order == 'r'))) {
737            swaps(&csp.majorVersion);
738            swaps(&csp.minorVersion);
739            swaps(&csp.length);
740        }
741        iov[0].iov_len = sz_xConnSetupPrefix;
742        iov[0].iov_base = (char *) &csp;
743        iov[1].iov_len = csp.lengthReason;
744        iov[1].iov_base = (void *) NOROOM;
745        iov[2].iov_len = (4 - (csp.lengthReason & 3)) & 3;
746        iov[2].iov_base = pad;
747        (void) _XSERVTransWritev(trans_conn, iov, 3);
748    }
749    RemoveNotifyFd(trans_conn->fd);
750    _XSERVTransClose(trans_conn);
751}
752
753static void
754ErrorConnMax(XtransConnInfo trans_conn)
755{
756    if (!SetNotifyFd(trans_conn->fd, ConnMaxNotify, X_NOTIFY_READ, trans_conn))
757        _XSERVTransClose(trans_conn);
758}
759
760/************
761 *   CloseDownFileDescriptor:
762 *     Remove this file descriptor
763 ************/
764
765void
766CloseDownFileDescriptor(OsCommPtr oc)
767{
768    if (oc->trans_conn) {
769        int connection = oc->fd;
770#ifdef XDMCP
771        XdmcpCloseDisplay(connection);
772#endif
773        ospoll_remove(server_poll, connection);
774        _XSERVTransDisconnect(oc->trans_conn);
775        _XSERVTransClose(oc->trans_conn);
776        oc->trans_conn = NULL;
777        oc->fd = -1;
778    }
779}
780
781/*****************
782 * CloseDownConnection
783 *    Delete client from AllClients and free resources
784 *****************/
785
786void
787CloseDownConnection(ClientPtr client)
788{
789    OsCommPtr oc = (OsCommPtr) client->osPrivate;
790
791    if (FlushCallback)
792        CallCallbacks(&FlushCallback, client);
793
794    if (oc->output)
795	FlushClient(client, oc, (char *) NULL, 0);
796    CloseDownFileDescriptor(oc);
797    FreeOsBuffers(oc);
798    free(client->osPrivate);
799    client->osPrivate = (void *) NULL;
800    if (auditTrailLevel > 1)
801        AuditF("client %d disconnected\n", client->index);
802}
803
804struct notify_fd {
805    int mask;
806    NotifyFdProcPtr notify;
807    void *data;
808};
809
810/*****************
811 * HandleNotifyFd
812 *    A poll callback to be called when the registered
813 *    file descriptor is ready.
814 *****************/
815
816static void
817HandleNotifyFd(int fd, int xevents, void *data)
818{
819    struct notify_fd *n = data;
820    n->notify(fd, xevents, n->data);
821}
822
823/*****************
824 * SetNotifyFd
825 *    Registers a callback to be invoked when the specified
826 *    file descriptor becomes readable.
827 *****************/
828
829Bool
830SetNotifyFd(int fd, NotifyFdProcPtr notify, int mask, void *data)
831{
832    struct notify_fd *n;
833
834    n = ospoll_data(server_poll, fd);
835    if (!n) {
836        if (mask == 0)
837            return TRUE;
838
839        n = calloc(1, sizeof (struct notify_fd));
840        if (!n)
841            return FALSE;
842        ospoll_add(server_poll, fd,
843                   ospoll_trigger_level,
844                   HandleNotifyFd,
845                   n);
846    }
847
848    if (mask == 0) {
849        ospoll_remove(server_poll, fd);
850        free(n);
851    } else {
852        int listen = mask & ~n->mask;
853        int mute = n->mask & ~mask;
854
855        if (listen)
856            ospoll_listen(server_poll, fd, listen);
857        if (mute)
858            ospoll_mute(server_poll, fd, mute);
859        n->mask = mask;
860        n->data = data;
861        n->notify = notify;
862    }
863
864    return TRUE;
865}
866
867/*****************
868 * OnlyListenToOneClient:
869 *    Only accept requests from  one client.  Continue to handle new
870 *    connections, but don't take any protocol requests from the new
871 *    ones.  Note that if GrabInProgress is set, EstablishNewConnections
872 *    needs to put new clients into SavedAllSockets and SavedAllClients.
873 *    Note also that there is no timeout for this in the protocol.
874 *    This routine is "undone" by ListenToAllClients()
875 *****************/
876
877int
878OnlyListenToOneClient(ClientPtr client)
879{
880    int rc;
881
882    rc = XaceHook(XACE_SERVER_ACCESS, client, DixGrabAccess);
883    if (rc != Success)
884        return rc;
885
886    if (!GrabInProgress) {
887        GrabInProgress = client->index;
888        set_poll_clients();
889    }
890
891    return rc;
892}
893
894/****************
895 * ListenToAllClients:
896 *    Undoes OnlyListentToOneClient()
897 ****************/
898
899void
900ListenToAllClients(void)
901{
902    if (GrabInProgress) {
903        GrabInProgress = 0;
904        set_poll_clients();
905    }
906}
907
908/****************
909 * IgnoreClient
910 *    Removes one client from input masks.
911 *    Must have cooresponding call to AttendClient.
912 ****************/
913
914void
915IgnoreClient(ClientPtr client)
916{
917    OsCommPtr oc = (OsCommPtr) client->osPrivate;
918
919    client->ignoreCount++;
920    if (client->ignoreCount > 1)
921        return;
922
923    isItTimeToYield = TRUE;
924    mark_client_not_ready(client);
925
926    oc->flags |= OS_COMM_IGNORED;
927    set_poll_client(client);
928}
929
930/****************
931 * AttendClient
932 *    Adds one client back into the input masks.
933 ****************/
934
935void
936AttendClient(ClientPtr client)
937{
938    OsCommPtr oc = (OsCommPtr) client->osPrivate;
939
940    if (client->clientGone) {
941        /*
942         * client is gone, so any pending requests will be dropped and its
943         * ignore count doesn't matter.
944         */
945        return;
946    }
947
948    client->ignoreCount--;
949    if (client->ignoreCount)
950        return;
951
952    oc->flags &= ~OS_COMM_IGNORED;
953    set_poll_client(client);
954    if (listen_to_client(client))
955        mark_client_ready(client);
956    else {
957        /* grab active, mark ready when grab goes away */
958        mark_client_saved_ready(client);
959    }
960}
961
962/* make client impervious to grabs; assume only executing client calls this */
963
964void
965MakeClientGrabImpervious(ClientPtr client)
966{
967    OsCommPtr oc = (OsCommPtr) client->osPrivate;
968
969    oc->flags |= OS_COMM_GRAB_IMPERVIOUS;
970    set_poll_client(client);
971
972    if (ServerGrabCallback) {
973        ServerGrabInfoRec grabinfo;
974
975        grabinfo.client = client;
976        grabinfo.grabstate = CLIENT_IMPERVIOUS;
977        CallCallbacks(&ServerGrabCallback, &grabinfo);
978    }
979}
980
981/* make client pervious to grabs; assume only executing client calls this */
982
983void
984MakeClientGrabPervious(ClientPtr client)
985{
986    OsCommPtr oc = (OsCommPtr) client->osPrivate;
987
988    oc->flags &= ~OS_COMM_GRAB_IMPERVIOUS;
989    set_poll_client(client);
990    isItTimeToYield = TRUE;
991
992    if (ServerGrabCallback) {
993        ServerGrabInfoRec grabinfo;
994
995        grabinfo.client = client;
996        grabinfo.grabstate = CLIENT_PERVIOUS;
997        CallCallbacks(&ServerGrabCallback, &grabinfo);
998    }
999}
1000
1001/* Add a fd (from launchd or similar) to our listeners */
1002void
1003ListenOnOpenFD(int fd, int noxauth)
1004{
1005    char port[256];
1006    XtransConnInfo ciptr;
1007    const char *display_env = getenv("DISPLAY");
1008
1009    if (display_env && (strncmp(display_env, "/tmp/launch", 11) == 0)) {
1010        /* Make the path the launchd socket if our DISPLAY is set right */
1011        strcpy(port, display_env);
1012    }
1013    else {
1014        /* Just some default so things don't break and die. */
1015        snprintf(port, sizeof(port), ":%d", atoi(display));
1016    }
1017
1018    /* Make our XtransConnInfo
1019     * TRANS_SOCKET_LOCAL_INDEX = 5 from Xtrans.c
1020     */
1021    ciptr = _XSERVTransReopenCOTSServer(5, fd, port);
1022    if (ciptr == NULL) {
1023        ErrorF("Got NULL while trying to Reopen listen port.\n");
1024        return;
1025    }
1026
1027    if (noxauth)
1028        ciptr->flags = ciptr->flags | TRANS_NOXAUTH;
1029
1030    /* Allocate space to store it */
1031    ListenTransFds =
1032        xnfreallocarray(ListenTransFds, ListenTransCount + 1, sizeof(int));
1033    ListenTransConns =
1034        xnfreallocarray(ListenTransConns, ListenTransCount + 1,
1035                        sizeof(XtransConnInfo));
1036
1037    /* Store it */
1038    ListenTransConns[ListenTransCount] = ciptr;
1039    ListenTransFds[ListenTransCount] = fd;
1040
1041    SetNotifyFd(fd, QueueNewConnections, X_NOTIFY_READ, NULL);
1042
1043    /* Increment the count */
1044    ListenTransCount++;
1045}
1046
1047/* based on TRANS(SocketUNIXAccept) (XtransConnInfo ciptr, int *status) */
1048Bool
1049AddClientOnOpenFD(int fd)
1050{
1051    XtransConnInfo ciptr;
1052    CARD32 connect_time;
1053    char port[20];
1054
1055    snprintf(port, sizeof(port), ":%d", atoi(display));
1056    ciptr = _XSERVTransReopenCOTSServer(5, fd, port);
1057    if (ciptr == NULL)
1058        return FALSE;
1059
1060    _XSERVTransSetOption(ciptr, TRANS_NONBLOCKING, 1);
1061    ciptr->flags |= TRANS_NOXAUTH;
1062
1063    connect_time = GetTimeInMillis();
1064
1065    if (!AllocNewConnection(ciptr, fd, connect_time)) {
1066        ErrorConnMax(ciptr);
1067        return FALSE;
1068    }
1069
1070    return TRUE;
1071}
1072
1073Bool
1074listen_to_client(ClientPtr client)
1075{
1076    OsCommPtr oc = (OsCommPtr) client->osPrivate;
1077
1078    if (oc->flags & OS_COMM_IGNORED)
1079        return FALSE;
1080
1081    if (!GrabInProgress)
1082        return TRUE;
1083
1084    if (client->index == GrabInProgress)
1085        return TRUE;
1086
1087    if (oc->flags & OS_COMM_GRAB_IMPERVIOUS)
1088        return TRUE;
1089
1090    return FALSE;
1091}
1092
1093static void
1094set_poll_client(ClientPtr client)
1095{
1096    OsCommPtr oc = (OsCommPtr) client->osPrivate;
1097
1098    if (oc->trans_conn) {
1099        if (listen_to_client(client))
1100            ospoll_listen(server_poll, oc->trans_conn->fd, X_NOTIFY_READ);
1101        else
1102            ospoll_mute(server_poll, oc->trans_conn->fd, X_NOTIFY_READ);
1103    }
1104}
1105
1106static void
1107set_poll_clients(void)
1108{
1109    int i;
1110
1111    for (i = 1; i < currentMaxClients; i++) {
1112        ClientPtr client = clients[i];
1113        if (client && !client->clientGone)
1114            set_poll_client(client);
1115    }
1116}
1117