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