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