1/*
2 * i/o functions
3 */
4/*
5
6Copyright 1990, 1991, 1998  The Open Group
7
8Permission to use, copy, modify, distribute, and sell this software and its
9documentation for any purpose is hereby granted without fee, provided that
10the above copyright notice appear in all copies and that both that
11copyright notice and this permission notice appear in supporting
12documentation.
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of The Open Group shall not be
25used in advertising or otherwise to promote the sale, use or other dealings
26in this Software without prior written authorization from The Open Group.
27
28 * Copyright 1990, 1991 Network Computing Devices;
29 * Portions Copyright 1987 by Digital Equipment Corporation
30 *
31 * Permission to use, copy, modify, distribute, and sell this software and
32 * its documentation for any purpose is hereby granted without fee, provided
33 * that the above copyright notice appear in all copies and that both that
34 * copyright notice and this permission notice appear in supporting
35 * documentation, and that the names of Network Computing Devices, or Digital
36 * not be used in advertising or publicity pertaining to distribution
37 * of the software without specific, written prior permission.
38 *
39 * NETWORK COMPUTING DEVICES, AND DIGITAL DISCLAIM ALL WARRANTIES WITH
40 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
41 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES,
42 * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
43 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
44 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
45 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
46 * THIS SOFTWARE.
47 */
48
49#include	"X11/Xpoll.h"
50
51#include	"config.h"
52
53/* include Xpoll.h early for possible FD_SETSIZE re-definition */
54#include	"X11/Xpoll.h"
55
56#include	<X11/Xtrans/Xtrans.h>
57#include	<stdio.h>
58#include	<errno.h>
59#include	<sys/types.h>
60#include	<sys/param.h>
61#include	<sys/uio.h>
62
63#include	<X11/fonts/FSproto.h>
64#include	"clientstr.h"
65#include	"osdep.h"
66#include	"globals.h"
67#include	"dispatch.h"
68
69
70/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
71 * systems are broken and return EWOULDBLOCK when they should return EAGAIN
72  */
73
74#if defined(EAGAIN) && defined(EWOULDBLOCK)
75#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
76#else
77
78#ifdef EAGAIN
79#define ETEST(err) (err == EAGAIN)
80#else
81#define ETEST(err) (err == EWOULDBLOCK)
82#endif
83
84#endif
85
86static int  timesThisConnection = 0;
87static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
88static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
89static OsCommPtr AvailableInput = (OsCommPtr) NULL;
90
91extern int xfd_ffs(fd_mask);
92static ConnectionInputPtr AllocateInputBuffer(void);
93static ConnectionOutputPtr AllocateOutputBuffer(void);
94
95
96#define		MAX_TIMES_PER	10
97
98#define	yield_control()				\
99	{ isItTimeToYield = TRUE;		\
100	  timesThisConnection = 0; }
101
102#define	yield_control_no_input()		\
103	{ yield_control();			\
104	  FD_CLR(fd, &ClientsWithInput); }
105
106#define	yield_control_death()			\
107	{ timesThisConnection = 0; }
108
109#define	request_length(req, client)		\
110	((int)((client)->swapped ? lswaps((req)->length) : (req)->length) << 2)
111
112int
113ReadRequest(ClientPtr client)
114{
115    OsCommPtr   oc;
116    ConnectionInputPtr oci;
117    fsReq      *request;
118    int         fd,
119                result,
120                gotnow,
121                needed = 0;
122
123    if (client == NULL)
124	return -1;
125    oc = (OsCommPtr) client->osPrivate;
126    if (oc == NULL)
127	return -1;
128    oci = oc->input;
129    fd = oc->fd;
130    if (oci != NULL && fd < 0)
131	return -1;
132
133    if (AvailableInput) {
134	if (AvailableInput != oc) {
135	    ConnectionInputPtr aci = AvailableInput->input;
136
137	    if (aci->size > BUFWATERMARK) {
138		FSfree(aci->buffer);
139		FSfree(aci);
140	    } else {
141		aci->next = FreeInputs;
142		FreeInputs = aci;
143	    }
144	    AvailableInput->input = (ConnectionInputPtr) NULL;
145	}
146	AvailableInput = (OsCommPtr) NULL;
147    }
148    if (!oci) {
149	if ((oci = FreeInputs ) != (ConnectionInputPtr) 0) {
150	    FreeInputs = oci->next;
151	} else if (!(oci = AllocateInputBuffer())) {
152	    yield_control_death();
153	    return -1;
154	}
155	oc->input = oci;
156    }
157    oci->bufptr += oci->lenLastReq;
158
159    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
160    request = (fsReq *) oci->bufptr;
161
162    /* not enough for a request */
163    if ((gotnow < SIZEOF(fsReq)) ||
164	    (gotnow < (needed = request_length(request, client)))) {
165	oci->lenLastReq = 0;
166	if ((gotnow < SIZEOF(fsReq)) || needed == 0)
167	    needed = SIZEOF(fsReq);
168	else if (needed > MAXBUFSIZE) {
169	    yield_control_death();
170	    return -1;
171	}
172	/* see if we need to shift up a partial request so the rest can fit */
173	if ((gotnow == 0) ||
174	    ((oci->bufptr - oci->buffer + needed) > oci->size))
175	{
176	    if ((gotnow > 0) && (oci->bufptr != oci->buffer))
177		memmove( oci->buffer, oci->bufptr, gotnow);
178	    /* grow buffer if necessary */
179	    if (needed > oci->size) {
180		char       *ibuf;
181
182		ibuf = (char *) FSrealloc(oci->buffer, needed);
183		if (!ibuf) {
184		    yield_control_death();
185		    return -1;
186		}
187		oci->size = needed;
188		oci->buffer = ibuf;
189	    }
190	    oci->bufptr = oci->buffer;
191	    oci->bufcnt = gotnow;
192	}
193	/* fill 'er up */
194	if (oc->trans_conn == NULL) {
195	    yield_control_death();
196	    return -1;
197	}
198	result = _FontTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
199		      oci->size - oci->bufcnt);
200	if (result <= 0) {
201#if !(defined(SVR4) && defined(i386) && !defined(sun))
202	    if ((result < 0) && ETEST(errno)) {
203		yield_control_no_input();
204		return 0;
205	    } else
206#endif
207	    {
208
209		yield_control_death();
210		return -1;
211	    }
212	}
213	oci->bufcnt += result;
214	gotnow += result;
215
216	/* free up space after huge requests */
217	if ((oci->size > BUFWATERMARK) &&
218		(oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
219	    char       *ibuf;
220
221	    ibuf = (char *) FSrealloc(oci->buffer, BUFSIZE);
222	    if (ibuf) {
223		oci->size = BUFSIZE;
224		oci->buffer = ibuf;
225		oci->bufptr = ibuf + oci->bufcnt - gotnow;
226	    }
227	}
228	request = (fsReq *) oci->bufptr;
229	if ((gotnow < SIZEOF(fsReq)) ||
230	    (gotnow < (needed = request_length(request, client)))) {
231	    yield_control_no_input();
232	    return 0;
233	}
234    }
235    if (needed == 0)
236	needed = SIZEOF(fsReq);
237    oci->lenLastReq = needed;
238    /*
239     * Check to see if client has at least one whole request in the buffer. If
240     * there is only a partial request, treat like buffer is empty so that
241     * select() will be called again and other clients can get into the queue.
242     */
243
244    if (gotnow >= needed + SIZEOF(fsReq)) {
245	request = (fsReq *) (oci->bufptr + needed);
246	if (gotnow >= needed + request_length(request, client))
247	    FD_SET(fd, &ClientsWithInput);
248	else
249	    yield_control_no_input();
250    } else {
251	if (gotnow == needed)
252	    AvailableInput = oc;
253	yield_control_no_input();
254    }
255
256    if (++timesThisConnection >= MAX_TIMES_PER)
257	yield_control();
258
259    client->requestBuffer = (pointer) oci->bufptr;
260    return needed;
261}
262
263Bool
264InsertFakeRequest(ClientPtr client, char *data, int count)
265{
266    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
267    ConnectionInputPtr oci = oc->input;
268    int         fd = oc->fd;
269    fsReq      *request;
270    int         gotnow,
271                moveup;
272
273    if (AvailableInput) {
274	if (AvailableInput != oc) {
275	    register ConnectionInputPtr aci = AvailableInput->input;
276
277	    if (aci->size > BUFWATERMARK) {
278		FSfree(aci->buffer);
279		FSfree(aci);
280	    } else {
281		aci->next = FreeInputs;
282		FreeInputs = aci;
283	    }
284	    AvailableInput->input = (ConnectionInputPtr) NULL;
285	}
286	AvailableInput = (OsCommPtr) NULL;
287    }
288    if (!oci) {
289	if ((oci = FreeInputs) != (ConnectionInputPtr) 0)
290	    FreeInputs = oci->next;
291	else if (!(oci = AllocateInputBuffer()))
292	    return FALSE;
293	oc->input = oci;
294
295    }
296    oci->bufptr += oci->lenLastReq;
297    oci->lenLastReq = 0;
298    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
299    if ((gotnow + count) > oci->size) {
300	char       *ibuf;
301
302	ibuf = (char *) FSrealloc(oci->buffer, gotnow + count);
303	if (!ibuf)
304	    return FALSE;
305	oci->size = gotnow + count;
306	oci->buffer = ibuf;
307	oci->bufptr = ibuf + oci->bufcnt - gotnow;
308    }
309    moveup = count - (oci->bufptr - oci->buffer);
310    if (moveup > 0) {
311	if (gotnow > 0)
312	    memmove( oci->bufptr + moveup, oci->bufptr, gotnow);
313	oci->bufptr += moveup;
314	oci->bufcnt += moveup;
315    }
316    memmove( oci->bufptr - count, data, count);
317    oci->bufptr -= count;
318    request = (fsReq *) oci->bufptr;
319    gotnow += count;
320    if ((gotnow >= SIZEOF(fsReq)) &&
321	    (gotnow >= request_length(request, client)))
322	FD_SET(fd, &ClientsWithInput);
323    else
324	yield_control_no_input();
325    return TRUE;
326}
327
328void
329ResetCurrentRequest(ClientPtr client)
330{
331    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
332    ConnectionInputPtr oci = oc->input;
333    int         fd = oc->fd;
334    fsReq      *request;
335    int         gotnow;
336
337    if (AvailableInput == oc)
338	AvailableInput = (OsCommPtr) NULL;
339    oci->lenLastReq = 0;
340    request = (fsReq *) oci->bufptr;
341    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
342    if ((gotnow >= SIZEOF(fsReq)) &&
343	    (gotnow >= request_length(request, client))) {
344	FD_SET(fd, &ClientsWithInput);
345	yield_control();
346    } else {
347	yield_control_no_input();
348    }
349}
350
351int
352FlushClient(
353    ClientPtr   client,
354    OsCommPtr   oc,
355    char       *extraBuf,
356    int         extraCount,
357    int         padsize)
358{
359    ConnectionOutputPtr oco = oc->output;
360    int         fd = oc->fd;
361    struct iovec iov[3];
362    char        padBuffer[3];
363    long        written;
364    long        notWritten;
365    long        todo;
366
367    if (!oco)
368	return 0;
369    written = 0;
370    notWritten = oco->count + extraCount + padsize;
371    todo = notWritten;
372    while (notWritten) {
373	long        before = written;
374	long        remain = todo;
375	int         i = 0;
376	long        len;
377
378	/*-
379	 * You could be very general here and have "in" and "out" iovecs and
380	 * write a loop without using a macro, but what the heck.  This
381	 * translates to:
382	 *
383	 * 	how much of this piece is new?
384	 *	if more new then we are trying this time, clamp
385	 *	if nothing new
386	 *	    then bump down amount already written, for next piece
387	 *	    else put new stuff in iovec, will need all of next piece
388	 *
389	 * Note that todo had better be at least 1 or else we'll end up
390	 * writing 0 iovecs.
391	 */
392
393#define	InsertIOV(pointer, length)	\
394	len = (length) - before;	\
395	if (len > remain)		\
396	    len = remain;		\
397	if (len <= 0) {			\
398	    before = (-len);		\
399	} else {			\
400	    iov[i].iov_len = len;	\
401	    iov[i].iov_base = (pointer) + before; \
402	    i++;			\
403	    remain -= len;		\
404	    before = 0;			\
405	}
406
407	InsertIOV((char *) oco->buf, oco->count);
408	InsertIOV(extraBuf, extraCount);
409	InsertIOV(padBuffer, padsize);
410
411	errno = 0;
412	if (oc->trans_conn && (len = _FontTransWritev(oc->trans_conn, iov, i)) >= 0) {
413	    written += len;
414	    notWritten -= len;
415	    todo = notWritten;
416	} else if (ETEST(errno)
417#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
418		 || ((errno == EMSGSIZE) && (todo == 1))
419#endif
420		)
421	{
422	    FD_SET(fd, &ClientsWriteBlocked);
423	    AnyClientsWriteBlocked = TRUE;
424
425	    if (written < oco->count) {
426		if (written > 0) {
427		    oco->count -= written;
428		    memmove( (char *) oco->buf, (char *) oco->buf + written,
429			    oco->count);
430		    written = 0;
431		}
432	    } else {
433		written -= oco->count;
434		oco->count = 0;
435	    }
436
437	    /* grow buffer if necessary */
438	    if (notWritten > oco->size) {
439		unsigned char *obuf;
440
441		obuf = (unsigned char *) FSrealloc(oco->buf,
442					      notWritten + OutputBufferSize);
443		if (!obuf) {
444		    if (oc->trans_conn)
445			_FontTransClose(oc->trans_conn);
446		    oc->trans_conn = NULL;
447		    MarkClientException(client);
448		    oco->count = 0;
449		    return -1;
450		}
451		oco->size = notWritten + OutputBufferSize;
452		oco->buf = obuf;
453	    }
454	    if ((len = extraCount - written) > 0) {
455		memmove( (char *) oco->buf + oco->count,
456			extraBuf + written, len);
457	    }
458	    oco->count = notWritten;
459	    return extraCount;
460	}
461#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
462	else if (errno == EMSGSIZE)
463	{
464	    todo >>= 1;
465	}
466#endif
467	else
468	{
469	    if (oc->trans_conn)
470	        _FontTransClose(oc->trans_conn);
471	    oc->trans_conn = NULL;
472	    MarkClientException(client);
473	    oco->count = 0;
474	    return -1;
475	}
476    }
477
478    /* everything was flushed */
479    oco->count = 0;
480
481    /* clear the write block if it was set */
482    if (AnyClientsWriteBlocked) {
483	FD_CLR(fd, &ClientsWriteBlocked);
484	if (!XFD_ANYSET(&ClientsWriteBlocked))
485	    AnyClientsWriteBlocked = FALSE;
486    }
487    if (oco->size > BUFWATERMARK) {
488	FSfree(oco->buf);
489	FSfree(oco);
490    } else {
491	oco->next = FreeOutputs;
492	FreeOutputs = oco;
493    }
494    oc->output = (ConnectionOutputPtr) NULL;
495
496    return extraCount;
497}
498
499void
500FlushAllOutput(void)
501{
502    int         index, base;
503    fd_mask	mask;
504    OsCommPtr   oc;
505    ClientPtr   client;
506
507    if (!NewOutputPending)
508	return;
509
510    NewOutputPending = FALSE;
511
512    for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) {
513	mask = OutputPending.fds_bits[base];
514	OutputPending.fds_bits[base] = 0;
515	while (mask) {
516	    index = xfd_ffs(mask) - 1;
517	    mask &= ~lowbit(mask);
518	    if ((index = ConnectionTranslation[(base * (sizeof(fd_mask) * 8)) + index]) == 0)
519		continue;
520	    client = clients[index];
521	    if (client->clientGone == CLIENT_GONE)
522		continue;
523	    oc = (OsCommPtr) client->osPrivate;
524	    if (FD_ISSET(oc->fd, &ClientsWithInput)) {
525		FD_SET(oc->fd, &OutputPending);
526		NewOutputPending = TRUE;
527	    } else {
528		(void) FlushClient(client, oc, (char *) NULL, 0, 0);
529	    }
530	}
531    }
532}
533
534/*
535 * returns number of bytes written
536 */
537static int
538write_to_client_internal(ClientPtr client, int count, char *buf, int padBytes)
539{
540    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
541    ConnectionOutputPtr oco = oc->output;
542
543    if (!count)
544	return 0;
545
546    if (!oco) {
547	if ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
548	    FreeOutputs = oco->next;
549	} else if (!(oco = AllocateOutputBuffer())) {
550	    _FontTransClose(oc->trans_conn);
551	    oc->trans_conn = NULL;
552	    MarkClientException(client);
553	    return -1;
554	}
555	oc->output = oco;
556    }
557    if (oco->count + count + padBytes > oco->size) {
558	FD_CLR(oc->fd, &OutputPending);
559	NewOutputPending = FALSE;
560	return FlushClient(client, oc, buf, count, padBytes);
561    }
562    NewOutputPending = TRUE;
563    FD_SET(oc->fd, &OutputPending);
564    memmove( (char *) oco->buf + oco->count, buf, count);
565    oco->count += count + padBytes;
566
567    return count;
568}
569
570void
571WriteToClientUnpadded(ClientPtr client, int count, char *buf)
572{
573    write_to_client_internal(client, count, buf, 0);
574}
575
576static int  padlength[4] = {0, 3, 2, 1};
577
578void
579WriteToClient(ClientPtr client, int count, char *buf)
580{
581    int flag = 0;
582    if (NULL == buf) {
583	flag = -1;
584	buf = (char *) FScalloc(1, count);
585	if (buf == NULL)
586	    FatalError("WriteToClient couldn't create client buffer\n");
587    }
588    write_to_client_internal(client, count, buf, padlength[count & 3]);
589    if (flag)
590	FSfree(buf);
591}
592
593static ConnectionInputPtr
594AllocateInputBuffer(void)
595{
596    register ConnectionInputPtr oci;
597
598    oci = (ConnectionInputPtr) FSalloc(sizeof(ConnectionInput));
599    if (!oci)
600	return (ConnectionInputPtr) NULL;
601    oci->buffer = (char *) FSalloc(BUFSIZE);
602    if (!oci->buffer) {
603	FSfree(oci);
604	return (ConnectionInputPtr) NULL;
605    }
606    oci->next = NULL;
607    oci->size = BUFSIZE;
608    oci->bufptr = oci->buffer;
609    oci->bufcnt = 0;
610    oci->lenLastReq = 0;
611    return oci;
612}
613
614static ConnectionOutputPtr
615AllocateOutputBuffer(void)
616{
617    register ConnectionOutputPtr oco;
618
619    oco = (ConnectionOutputPtr) FSalloc(sizeof(ConnectionOutput));
620    if (!oco)
621	return (ConnectionOutputPtr) NULL;
622    oco->buf = (unsigned char *) FSalloc(BUFSIZE);
623    if (!oco->buf) {
624	FSfree(oco);
625	return (ConnectionOutputPtr) NULL;
626    }
627    oco->size = BUFSIZE;
628    oco->count = 0;
629    return oco;
630}
631
632
633void
634FreeOsBuffers(OsCommPtr oc)
635{
636    register ConnectionInputPtr oci;
637    register ConnectionOutputPtr oco;
638
639    if (AvailableInput == oc)
640	AvailableInput = (OsCommPtr) NULL;
641    if ((oci = oc->input) != (ConnectionInputPtr) 0) {
642	if (FreeInputs) {
643	    FSfree(oci->buffer);
644	    FSfree(oci);
645	} else {
646	    FreeInputs = oci;
647	    oci->next = (ConnectionInputPtr) NULL;
648	    oci->bufptr = oci->buffer;
649	    oci->bufcnt = 0;
650	    oci->lenLastReq = 0;
651	}
652    }
653    if ((oco = oc->output) != (ConnectionOutputPtr) 0) {
654	if (FreeOutputs) {
655	    FSfree(oco->buf);
656	    FSfree(oco);
657	} else {
658	    FreeOutputs = oco;
659	    oco->next = (ConnectionOutputPtr) NULL;
660	    oco->count = 0;
661	}
662    }
663}
664
665void
666ResetOsBuffers(void)
667{
668    register ConnectionInputPtr oci;
669    register ConnectionOutputPtr oco;
670
671    while ((oci = FreeInputs) != (ConnectionInputPtr) 0) {
672	FreeInputs = oci->next;
673	FSfree(oci->buffer);
674	FSfree(oci);
675    }
676    while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
677	FreeOutputs = oco->next;
678	FSfree(oco->buf);
679	FSfree(oco);
680    }
681}
682