io.c revision 30e1ba2c
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 SUNSYSV /* check for another brain-damaged OS bug */
418		 || (errno == 0)
419#endif
420#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
421		 || ((errno == EMSGSIZE) && (todo == 1))
422#endif
423		)
424	{
425	    FD_SET(fd, &ClientsWriteBlocked);
426	    AnyClientsWriteBlocked = TRUE;
427
428	    if (written < oco->count) {
429		if (written > 0) {
430		    oco->count -= written;
431		    memmove( (char *) oco->buf, (char *) oco->buf + written,
432			    oco->count);
433		    written = 0;
434		}
435	    } else {
436		written -= oco->count;
437		oco->count = 0;
438	    }
439
440	    /* grow buffer if necessary */
441	    if (notWritten > oco->size) {
442		unsigned char *obuf;
443
444		obuf = (unsigned char *) fsrealloc(oco->buf,
445					      notWritten + OutputBufferSize);
446		if (!obuf) {
447		    if (oc->trans_conn)
448			_FontTransClose(oc->trans_conn);
449		    oc->trans_conn = NULL;
450		    MarkClientException(client);
451		    oco->count = 0;
452		    return -1;
453		}
454		oco->size = notWritten + OutputBufferSize;
455		oco->buf = obuf;
456	    }
457	    if ((len = extraCount - written) > 0) {
458		memmove( (char *) oco->buf + oco->count,
459			extraBuf + written, len);
460	    }
461	    oco->count = notWritten;
462	    return extraCount;
463	}
464#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
465	else if (errno == EMSGSIZE)
466	{
467	    todo >>= 1;
468	}
469#endif
470	else
471	{
472	    if (oc->trans_conn)
473	        _FontTransClose(oc->trans_conn);
474	    oc->trans_conn = NULL;
475	    MarkClientException(client);
476	    oco->count = 0;
477	    return -1;
478	}
479    }
480
481    /* everything was flushed */
482    oco->count = 0;
483
484    /* clear the write block if it was set */
485    if (AnyClientsWriteBlocked) {
486	FD_CLR(fd, &ClientsWriteBlocked);
487	if (!XFD_ANYSET(&ClientsWriteBlocked))
488	    AnyClientsWriteBlocked = FALSE;
489    }
490    if (oco->size > BUFWATERMARK) {
491	fsfree(oco->buf);
492	fsfree(oco);
493    } else {
494	oco->next = FreeOutputs;
495	FreeOutputs = oco;
496    }
497    oc->output = (ConnectionOutputPtr) NULL;
498
499    return extraCount;
500}
501
502void
503FlushAllOutput(void)
504{
505    int         index, base;
506    fd_mask	mask;
507    OsCommPtr   oc;
508    ClientPtr   client;
509
510    if (!NewOutputPending)
511	return;
512
513    NewOutputPending = FALSE;
514
515    for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) {
516	mask = OutputPending.fds_bits[base];
517	OutputPending.fds_bits[base] = 0;
518	while (mask) {
519	    index = xfd_ffs(mask) - 1;
520	    mask &= ~lowbit(mask);
521	    if ((index = ConnectionTranslation[(base * (sizeof(fd_mask) * 8)) + index]) == 0)
522		continue;
523	    client = clients[index];
524	    if (client->clientGone == CLIENT_GONE)
525		continue;
526	    oc = (OsCommPtr) client->osPrivate;
527	    if (FD_ISSET(oc->fd, &ClientsWithInput)) {
528		FD_SET(oc->fd, &OutputPending);
529		NewOutputPending = TRUE;
530	    } else {
531		(void) FlushClient(client, oc, (char *) NULL, 0, 0);
532	    }
533	}
534    }
535}
536
537/*
538 * returns number of bytes written
539 */
540static int
541write_to_client_internal(ClientPtr client, int count, char *buf, int padBytes)
542{
543    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
544    ConnectionOutputPtr oco = oc->output;
545
546    if (!count)
547	return 0;
548
549    if (!oco) {
550	if ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
551	    FreeOutputs = oco->next;
552	} else if (!(oco = AllocateOutputBuffer())) {
553	    _FontTransClose(oc->trans_conn);
554	    oc->trans_conn = NULL;
555	    MarkClientException(client);
556	    return -1;
557	}
558	oc->output = oco;
559    }
560    if (oco->count + count + padBytes > oco->size) {
561	FD_CLR(oc->fd, &OutputPending);
562	NewOutputPending = FALSE;
563	return FlushClient(client, oc, buf, count, padBytes);
564    }
565    NewOutputPending = TRUE;
566    FD_SET(oc->fd, &OutputPending);
567    memmove( (char *) oco->buf + oco->count, buf, count);
568    oco->count += count + padBytes;
569
570    return count;
571}
572
573void
574WriteToClientUnpadded(ClientPtr client, int count, char *buf)
575{
576    write_to_client_internal(client, count, buf, 0);
577}
578
579static int  padlength[4] = {0, 3, 2, 1};
580
581void
582WriteToClient(ClientPtr client, int count, char *buf)
583{
584    int flag = 0;
585    if (NULL == buf) {
586	flag = -1;
587	buf = (char *)fsalloc(count); memset(buf, 0, count);
588    }
589     write_to_client_internal(client, count, buf, padlength[count & 3]);
590    if (flag)
591	fsfree(buf);
592}
593
594static ConnectionInputPtr
595AllocateInputBuffer(void)
596{
597    register ConnectionInputPtr oci;
598
599    oci = (ConnectionInputPtr) fsalloc(sizeof(ConnectionInput));
600    if (!oci)
601	return (ConnectionInputPtr) NULL;
602    oci->buffer = (char *) fsalloc(BUFSIZE);
603    if (!oci->buffer) {
604	fsfree(oci);
605	return (ConnectionInputPtr) NULL;
606    }
607    oci->next = NULL;
608    oci->size = BUFSIZE;
609    oci->bufptr = oci->buffer;
610    oci->bufcnt = 0;
611    oci->lenLastReq = 0;
612    return oci;
613}
614
615static ConnectionOutputPtr
616AllocateOutputBuffer(void)
617{
618    register ConnectionOutputPtr oco;
619
620    oco = (ConnectionOutputPtr) fsalloc(sizeof(ConnectionOutput));
621    if (!oco)
622	return (ConnectionOutputPtr) NULL;
623    oco->buf = (unsigned char *) fsalloc(BUFSIZE);
624    if (!oco->buf) {
625	fsfree(oco);
626	return (ConnectionOutputPtr) NULL;
627    }
628    oco->size = BUFSIZE;
629    oco->count = 0;
630    return oco;
631}
632
633
634void
635FreeOsBuffers(OsCommPtr oc)
636{
637    register ConnectionInputPtr oci;
638    register ConnectionOutputPtr oco;
639
640    if (AvailableInput == oc)
641	AvailableInput = (OsCommPtr) NULL;
642    if ((oci = oc->input) != (ConnectionInputPtr) 0) {
643	if (FreeInputs) {
644	    fsfree(oci->buffer);
645	    fsfree(oci);
646	} else {
647	    FreeInputs = oci;
648	    oci->next = (ConnectionInputPtr) NULL;
649	    oci->bufptr = oci->buffer;
650	    oci->bufcnt = 0;
651	    oci->lenLastReq = 0;
652	}
653    }
654    if ((oco = oc->output) != (ConnectionOutputPtr) 0) {
655	if (FreeOutputs) {
656	    fsfree(oco->buf);
657	    fsfree(oco);
658	} else {
659	    FreeOutputs = oco;
660	    oco->next = (ConnectionOutputPtr) NULL;
661	    oco->count = 0;
662	}
663    }
664}
665
666void
667ResetOsBuffers(void)
668{
669    register ConnectionInputPtr oci;
670    register ConnectionOutputPtr oco;
671
672    while ((oci = FreeInputs) != (ConnectionInputPtr) 0) {
673	FreeInputs = oci->next;
674	fsfree(oci->buffer);
675	fsfree(oci);
676    }
677    while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
678	FreeOutputs = oco->next;
679	fsfree(oco->buf);
680	fsfree(oco);
681    }
682}
683