#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#undef DEBUG_COMMUNICATION
#ifdef WIN32
#include <X11/Xwinsock.h>
#endif
#include <stdio.h>
#define XSERV_t
#define TRANS_SERVER
#define TRANS_REOPEN
#include <X11/Xtrans/Xtrans.h>
#include <X11/Xmd.h>
#include <errno.h>
#if !defined(WIN32)
#include <sys/uio.h>
#endif
#include <X11/X.h>
#define NEED_REPLIES
#include <X11/Xproto.h>
#include "os.h"
#include "osdep.h"
#include <X11/Xpoll.h>
#include "opaque.h"
#include "dixstruct.h"
#include "misc.h"
_X_EXPORT CallbackListPtr ReplyCallback;
_X_EXPORT CallbackListPtr FlushCallback;
static ConnectionInputPtr AllocateInputBuffer(void);
static ConnectionOutputPtr AllocateOutputBuffer(void);
#ifndef WIN32
#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
#else
#define ETEST(err) (err == EAGAIN || err == WSAEWOULDBLOCK)
#endif
static Bool CriticalOutputPending;
static int timesThisConnection = 0;
static ConnectionInputPtr FreeInputs = (ConnectionInputPtr)NULL;
static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr)NULL;
static OsCommPtr AvailableInput = (OsCommPtr)NULL;
#define get_req_len(req,cli) ((cli)->swapped ? \
lswaps((req)->length) : (req)->length)
#include <X11/extensions/bigreqstr.h>
#define get_big_req_len(req,cli) ((cli)->swapped ? \
lswapl(((xBigReq *)(req))->length) : \
((xBigReq *)(req))->length)
#define MAX_TIMES_PER 10
static void
YieldControl(void)
{
isItTimeToYield = TRUE;
timesThisConnection = 0;
}
static void
YieldControlNoInput(int fd)
{
YieldControl();
FD_CLR(fd, &ClientsWithInput);
}
static void
YieldControlDeath(void)
{
timesThisConnection = 0;
}
int
ReadRequestFromClient(ClientPtr client)
{
OsCommPtr oc = (OsCommPtr)client->osPrivate;
ConnectionInputPtr oci = oc->input;
int fd = oc->fd;
unsigned int gotnow, needed;
int result;
register xReq *request;
Bool need_header;
Bool move_header;
if (AvailableInput)
{
if (AvailableInput != oc)
{
register ConnectionInputPtr aci = AvailableInput->input;
if (aci->size > BUFWATERMARK)
{
xfree(aci->buffer);
xfree(aci);
}
else
{
aci->next = FreeInputs;
FreeInputs = aci;
}
AvailableInput->input = (ConnectionInputPtr)NULL;
}
AvailableInput = (OsCommPtr)NULL;
}
if (!oci)
{
if ((oci = FreeInputs))
{
FreeInputs = oci->next;
}
else if (!(oci = AllocateInputBuffer()))
{
YieldControlDeath();
return -1;
}
oc->input = oci;
}
oci->bufptr += oci->lenLastReq;
need_header = FALSE;
move_header = FALSE;
gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
if (gotnow < sizeof(xReq))
{
needed = sizeof(xReq);
need_header = TRUE;
}
else
{
request = (xReq *)oci->bufptr;
needed = get_req_len(request, client);
if (!needed && client->big_requests)
{
move_header = TRUE;
if (gotnow < sizeof(xBigReq))
{
needed = sizeof(xBigReq) >> 2;
need_header = TRUE;
}
else
needed = get_big_req_len(request, client);
}
client->req_len = needed;
needed <<= 2;
}
if (gotnow < needed)
{
oci->lenLastReq = 0;
if (needed > maxBigRequestSize << 2)
{
YieldControlDeath();
return -1;
}
if ((gotnow == 0) ||
((oci->bufptr - oci->buffer + needed) > oci->size))
{
if ((gotnow > 0) && (oci->bufptr != oci->buffer))
memmove(oci->buffer, oci->bufptr, gotnow);
if (needed > oci->size)
{
char *ibuf;
ibuf = (char *)xrealloc(oci->buffer, needed);
if (!ibuf)
{
YieldControlDeath();
return -1;
}
oci->size = needed;
oci->buffer = ibuf;
}
oci->bufptr = oci->buffer;
oci->bufcnt = gotnow;
}
if (!oc->trans_conn)
{
YieldControlDeath();
return -1;
}
result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
oci->size - oci->bufcnt);
if (result <= 0)
{
if ((result < 0) && ETEST(errno))
{
#if defined(SVR4) && defined(__i386__) && !defined(sun)
if (0)
#endif
{
YieldControlNoInput(fd);
return 0;
}
}
YieldControlDeath();
return -1;
}
oci->bufcnt += result;
gotnow += result;
if ((oci->size > BUFWATERMARK) &&
(oci->bufcnt < BUFSIZE) && (needed < BUFSIZE))
{
char *ibuf;
ibuf = (char *)xrealloc(oci->buffer, BUFSIZE);
if (ibuf)
{
oci->size = BUFSIZE;
oci->buffer = ibuf;
oci->bufptr = ibuf + oci->bufcnt - gotnow;
}
}
if (need_header && gotnow >= needed)
{
request = (xReq *)oci->bufptr;
needed = get_req_len(request, client);
if (!needed && client->big_requests)
{
move_header = TRUE;
if (gotnow < sizeof(xBigReq))
needed = sizeof(xBigReq) >> 2;
else
needed = get_big_req_len(request, client);
}
client->req_len = needed;
needed <<= 2;
}
if (gotnow < needed)
{
YieldControlNoInput(fd);
return 0;
}
}
if (needed == 0)
{
if (client->big_requests)
needed = sizeof(xBigReq);
else
needed = sizeof(xReq);
}
oci->lenLastReq = needed;
gotnow -= needed;
if (gotnow >= sizeof(xReq))
{
request = (xReq *)(oci->bufptr + needed);
if (gotnow >= (result = (get_req_len(request, client) << 2))
&& (result ||
(client->big_requests &&
(gotnow >= sizeof(xBigReq) &&
gotnow >= (get_big_req_len(request, client) << 2))))
)
FD_SET(fd, &ClientsWithInput);
else
{
if (!SmartScheduleDisable)
FD_CLR(fd, &ClientsWithInput);
else
YieldControlNoInput(fd);
}
}
else
{
if (!gotnow)
AvailableInput = oc;
if (!SmartScheduleDisable)
FD_CLR(fd, &ClientsWithInput);
else
YieldControlNoInput(fd);
}
if (SmartScheduleDisable)
if (++timesThisConnection >= MAX_TIMES_PER)
YieldControl();
if (move_header)
{
request = (xReq *)oci->bufptr;
oci->bufptr += (sizeof(xBigReq) - sizeof(xReq));
*(xReq *)oci->bufptr = *request;
oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq));
client->req_len -= (sizeof(xBigReq) - sizeof(xReq)) >> 2;
}
client->requestBuffer = (pointer)oci->bufptr;
#ifdef DEBUG_COMMUNICATION
{
xReq *req = client->requestBuffer;
ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n",
client->index,req->reqType,req->data,req->length);
}
#endif
return needed;
}
Bool
InsertFakeRequest(ClientPtr client, char *data, int count)
{
OsCommPtr oc = (OsCommPtr)client->osPrivate;
ConnectionInputPtr oci = oc->input;
int fd = oc->fd;
int gotnow, moveup;
if (AvailableInput)
{
if (AvailableInput != oc)
{
ConnectionInputPtr aci = AvailableInput->input;
if (aci->size > BUFWATERMARK)
{
xfree(aci->buffer);
xfree(aci);
}
else
{
aci->next = FreeInputs;
FreeInputs = aci;
}
AvailableInput->input = (ConnectionInputPtr)NULL;
}
AvailableInput = (OsCommPtr)NULL;
}
if (!oci)
{
if ((oci = FreeInputs))
FreeInputs = oci->next;
else if (!(oci = AllocateInputBuffer()))
return FALSE;
oc->input = oci;
}
oci->bufptr += oci->lenLastReq;
oci->lenLastReq = 0;
gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
if ((gotnow + count) > oci->size)
{
char *ibuf;
ibuf = (char *)xrealloc(oci->buffer, gotnow + count);
if (!ibuf)
return(FALSE);
oci->size = gotnow + count;
oci->buffer = ibuf;
oci->bufptr = ibuf + oci->bufcnt - gotnow;
}
moveup = count - (oci->bufptr - oci->buffer);
if (moveup > 0)
{
if (gotnow > 0)
memmove(oci->bufptr + moveup, oci->bufptr, gotnow);
oci->bufptr += moveup;
oci->bufcnt += moveup;
}
memmove(oci->bufptr - count, data, count);
oci->bufptr -= count;
gotnow += count;
if ((gotnow >= sizeof(xReq)) &&
(gotnow >= (int)(get_req_len((xReq *)oci->bufptr, client) << 2)))
FD_SET(fd, &ClientsWithInput);
else
YieldControlNoInput(fd);
return(TRUE);
}
_X_EXPORT void
ResetCurrentRequest(ClientPtr client)
{
OsCommPtr oc = (OsCommPtr)client->osPrivate;
register ConnectionInputPtr oci = oc->input;
int fd = oc->fd;
register xReq *request;
int gotnow, needed;
if (AvailableInput == oc)
AvailableInput = (OsCommPtr)NULL;
oci->lenLastReq = 0;
gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
if (gotnow < sizeof(xReq))
{
YieldControlNoInput(fd);
}
else
{
request = (xReq *)oci->bufptr;
needed = get_req_len(request, client);
if (!needed && client->big_requests)
{
oci->bufptr -= sizeof(xBigReq) - sizeof(xReq);
*(xReq *)oci->bufptr = *request;
((xBigReq *)oci->bufptr)->length = client->req_len;
if (client->swapped)
{
char n;
swapl(&((xBigReq *)oci->bufptr)->length, n);
}
}
if (gotnow >= (needed << 2))
{
if (FD_ISSET(fd, &AllClients))
{
FD_SET(fd, &ClientsWithInput);
}
else
{
FD_SET(fd, &IgnoredClientsWithInput);
}
YieldControl();
}
else
YieldControlNoInput(fd);
}
}
static const int padlength[4] = {0, 3, 2, 1};
void
FlushAllOutput(void)
{
register int index, base;
register fd_mask mask;
OsCommPtr oc;
register ClientPtr client;
Bool newoutput = NewOutputPending;
#if defined(WIN32)
fd_set newOutputPending;
#endif
if (FlushCallback)
CallCallbacks(&FlushCallback, NULL);
if (!newoutput)
return;
CriticalOutputPending = FALSE;
NewOutputPending = FALSE;
#ifndef WIN32
for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++)
{
mask = OutputPending.fds_bits[ base ];
OutputPending.fds_bits[ base ] = 0;
while (mask)
{
index = ffs(mask) - 1;
mask &= ~lowbit(mask);
if ((index = ConnectionTranslation[(base * (sizeof(fd_mask)*8)) + index]) == 0)
continue;
client = clients[index];
if (client->clientGone)
continue;
oc = (OsCommPtr)client->osPrivate;
if (FD_ISSET(oc->fd, &ClientsWithInput))
{
FD_SET(oc->fd, &OutputPending);
NewOutputPending = TRUE;
}
else
(void)FlushClient(client, oc, (char *)NULL, 0);
}
}
#else
FD_ZERO(&newOutputPending);
for (base = 0; base < XFD_SETCOUNT(&OutputPending); base++)
{
index = XFD_FD(&OutputPending, base);
if ((index = GetConnectionTranslation(index)) == 0)
continue;
client = clients[index];
if (client->clientGone)
continue;
oc = (OsCommPtr)client->osPrivate;
if (FD_ISSET(oc->fd, &ClientsWithInput))
{
FD_SET(oc->fd, &newOutputPending);
NewOutputPending = TRUE;
}
else
(void)FlushClient(client, oc, (char *)NULL, 0);
}
XFD_COPYSET(&newOutputPending, &OutputPending);
#endif
}
void
FlushIfCriticalOutputPending(void)
{
if (CriticalOutputPending)
FlushAllOutput();
}
_X_EXPORT void
SetCriticalOutputPending(void)
{
CriticalOutputPending = TRUE;
}
_X_EXPORT int
WriteToClient (ClientPtr who, int count, const void *__buf)
{
OsCommPtr oc = (OsCommPtr)who->osPrivate;
ConnectionOutputPtr oco = oc->output;
int padBytes;
const char *buf = __buf;
#ifdef DEBUG_COMMUNICATION
Bool multicount = FALSE;
#endif
if (!count)
return(0);
#ifdef DEBUG_COMMUNICATION
{
char info[128];
xError *err;
xGenericReply *rep;
xEvent *ev;
if (!who->replyBytesRemaining) {
switch(buf[0]) {
case X_Reply:
rep = (xGenericReply*)buf;
if (rep->sequenceNumber == who->sequence) {
snprintf(info,127,"Xreply: type: 0x%x data: 0x%x "
"len: %i seq#: 0x%x", rep->type, rep->data1,
rep->length, rep->sequenceNumber);
multicount = TRUE;
}
break;
case X_Error:
err = (xError*)buf;
snprintf(info,127,"Xerror: Code: 0x%x resID: 0x%x maj: 0x%x "
"min: %x", err->errorCode,err->resourceID,
err->minorCode,err->majorCode);
break;
default:
if ((buf[0] & 0x7f) == KeymapNotify)
snprintf(info,127,"KeymapNotifyEvent: %i",buf[0]);
else {
ev = (xEvent*)buf;
snprintf(info,127,"XEvent: type: 0x%x detail: 0x%x "
"seq#: 0x%x", ev->u.u.type, ev->u.u.detail,
ev->u.u.sequenceNumber);
}
}
ErrorF("REPLY: ClientIDX: %i %s\n",who->index, info);
} else
multicount = TRUE;
}
#endif
if (!oco)
{
if ((oco = FreeOutputs))
{
FreeOutputs = oco->next;
}
else if (!(oco = AllocateOutputBuffer()))
{
if (oc->trans_conn) {
_XSERVTransDisconnect(oc->trans_conn);
_XSERVTransClose(oc->trans_conn);
oc->trans_conn = NULL;
}
MarkClientException(who);
return -1;
}
oc->output = oco;
}
padBytes = padlength[count & 3];
if(ReplyCallback)
{
ReplyInfoRec replyinfo;
replyinfo.client = who;
replyinfo.replyData = buf;
replyinfo.dataLenBytes = count + padBytes;
if (who->replyBytesRemaining)
{
who->replyBytesRemaining -= count + padBytes;
replyinfo.startOfReply = FALSE;
replyinfo.bytesRemaining = who->replyBytesRemaining;
CallCallbacks((&ReplyCallback), (pointer)&replyinfo);
}
else if (who->clientState == ClientStateRunning
&& buf[0] == X_Reply)
{
CARD32 replylen;
unsigned long bytesleft;
char n;
replylen = ((xGenericReply *)buf)->length;
if (who->swapped)
swapl(&replylen, n);
bytesleft = (replylen * 4) + SIZEOF(xReply) - count - padBytes;
replyinfo.startOfReply = TRUE;
replyinfo.bytesRemaining = who->replyBytesRemaining = bytesleft;
CallCallbacks((&ReplyCallback), (pointer)&replyinfo);
}
}
#ifdef DEBUG_COMMUNICATION
else if (multicount) {
if (who->replyBytesRemaining) {
who->replyBytesRemaining -= (count + padBytes);
} else {
CARD32 replylen;
replylen = ((xGenericReply *)buf)->length;
who->replyBytesRemaining =
(replylen * 4) + SIZEOF(xReply) - count - padBytes;
}
}
#endif
if (oco->count + count + padBytes > oco->size)
{
FD_CLR(oc->fd, &OutputPending);
if(!XFD_ANYSET(&OutputPending)) {
CriticalOutputPending = FALSE;
NewOutputPending = FALSE;
}
return FlushClient(who, oc, buf, count);
}
NewOutputPending = TRUE;
FD_SET(oc->fd, &OutputPending);
memmove((char *)oco->buf + oco->count, buf, count);
oco->count += count + padBytes;
return(count);
}
int
FlushClient(ClientPtr who, OsCommPtr oc, const void *__extraBuf, int extraCount)
{
ConnectionOutputPtr oco = oc->output;
int connection = oc->fd;
XtransConnInfo trans_conn = oc->trans_conn;
struct iovec iov[3];
static char padBuffer[3];
const char *extraBuf = __extraBuf;
long written;
long padsize;
long notWritten;
long todo;
if (!oco)
return 0;
written = 0;
padsize = padlength[extraCount & 3];
notWritten = oco->count + extraCount + padsize;
todo = notWritten;
while (notWritten) {
long before = written;
long remain = todo;
int i = 0;
long len;
#define InsertIOV(pointer, length) \
len = (length) - before; \
if (len > remain) \
len = remain; \
if (len <= 0) { \
before = (-len); \
} else { \
iov[i].iov_len = len; \
iov[i].iov_base = (pointer) + before; \
i++; \
remain -= len; \
before = 0; \
}
InsertIOV ((char *)oco->buf, oco->count)
InsertIOV ((char *)extraBuf, extraCount)
InsertIOV (padBuffer, padsize)
errno = 0;
if (trans_conn && (len = _XSERVTransWritev(trans_conn, iov, i)) >= 0)
{
written += len;
notWritten -= len;
todo = notWritten;
}
else if (ETEST(errno)
#ifdef SUNSYSV
|| (errno == 0)
#endif
#ifdef EMSGSIZE
|| ((errno == EMSGSIZE) && (todo == 1))
#endif
)
{
FD_SET(connection, &ClientsWriteBlocked);
AnyClientsWriteBlocked = TRUE;
if (written < oco->count)
{
if (written > 0)
{
oco->count -= written;
memmove((char *)oco->buf,
(char *)oco->buf + written,
oco->count);
written = 0;
}
}
else
{
written -= oco->count;
oco->count = 0;
}
if (notWritten > oco->size)
{
unsigned char *obuf;
obuf = (unsigned char *)xrealloc(oco->buf,
notWritten + BUFSIZE);
if (!obuf)
{
_XSERVTransDisconnect(oc->trans_conn);
_XSERVTransClose(oc->trans_conn);
oc->trans_conn = NULL;
MarkClientException(who);
oco->count = 0;
return(-1);
}
oco->size = notWritten + BUFSIZE;
oco->buf = obuf;
}
if ((len = extraCount - written) > 0)
memmove ((char *)oco->buf + oco->count,
extraBuf + written,
len);
oco->count = notWritten;
return extraCount;
}
#ifdef EMSGSIZE
else if (errno == EMSGSIZE)
{
todo >>= 1;
}
#endif
else
{
if (oc->trans_conn)
{
_XSERVTransDisconnect(oc->trans_conn);
_XSERVTransClose(oc->trans_conn);
oc->trans_conn = NULL;
}
MarkClientException(who);
oco->count = 0;
return(-1);
}
}
oco->count = 0;
if (AnyClientsWriteBlocked)
{
FD_CLR(oc->fd, &ClientsWriteBlocked);
if (! XFD_ANYSET(&ClientsWriteBlocked))
AnyClientsWriteBlocked = FALSE;
}
if (oco->size > BUFWATERMARK)
{
xfree(oco->buf);
xfree(oco);
}
else
{
oco->next = FreeOutputs;
FreeOutputs = oco;
}
oc->output = (ConnectionOutputPtr)NULL;
return extraCount;
}
static ConnectionInputPtr
AllocateInputBuffer(void)
{
ConnectionInputPtr oci;
oci = (ConnectionInputPtr)xalloc(sizeof(ConnectionInput));
if (!oci)
return (ConnectionInputPtr)NULL;
oci->buffer = (char *)xalloc(BUFSIZE);
if (!oci->buffer)
{
xfree(oci);
return (ConnectionInputPtr)NULL;
}
oci->size = BUFSIZE;
oci->bufptr = oci->buffer;
oci->bufcnt = 0;
oci->lenLastReq = 0;
return oci;
}
static ConnectionOutputPtr
AllocateOutputBuffer(void)
{
ConnectionOutputPtr oco;
oco = (ConnectionOutputPtr)xalloc(sizeof(ConnectionOutput));
if (!oco)
return (ConnectionOutputPtr)NULL;
oco->buf = (unsigned char *) xcalloc(1, BUFSIZE);
if (!oco->buf)
{
xfree(oco);
return (ConnectionOutputPtr)NULL;
}
oco->size = BUFSIZE;
oco->count = 0;
return oco;
}
void
FreeOsBuffers(OsCommPtr oc)
{
ConnectionInputPtr oci;
ConnectionOutputPtr oco;
if (AvailableInput == oc)
AvailableInput = (OsCommPtr)NULL;
if ((oci = oc->input))
{
if (FreeInputs)
{
xfree(oci->buffer);
xfree(oci);
}
else
{
FreeInputs = oci;
oci->next = (ConnectionInputPtr)NULL;
oci->bufptr = oci->buffer;
oci->bufcnt = 0;
oci->lenLastReq = 0;
}
}
if ((oco = oc->output))
{
if (FreeOutputs)
{
xfree(oco->buf);
xfree(oco);
}
else
{
FreeOutputs = oco;
oco->next = (ConnectionOutputPtr)NULL;
oco->count = 0;
}
}
}
void
ResetOsBuffers(void)
{
ConnectionInputPtr oci;
ConnectionOutputPtr oco;
while ((oci = FreeInputs))
{
FreeInputs = oci->next;
xfree(oci->buffer);
xfree(oci);
}
while ((oco = FreeOutputs))
{
FreeOutputs = oco->next;
xfree(oco->buf);
xfree(oco);
}
}