io.c revision f30dc278
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	<X11/Xtrans/Xtrans.h>
54#include	<stdio.h>
55#include	<errno.h>
56#include	<sys/types.h>
57#include	<sys/param.h>
58#include	<sys/uio.h>
59
60#include	<X11/fonts/FSproto.h>
61#include	"clientstr.h"
62#include	"osdep.h"
63#include	"globals.h"
64#include	"dispatch.h"
65
66
67/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
68 * systems are broken and return EWOULDBLOCK when they should return EAGAIN
69  */
70
71#if defined(EAGAIN) && defined(EWOULDBLOCK)
72#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
73#else
74
75#ifdef EAGAIN
76#define ETEST(err) (err == EAGAIN)
77#else
78#define ETEST(err) (err == EWOULDBLOCK)
79#endif
80
81#endif
82
83static int  timesThisConnection = 0;
84static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
85static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
86static OsCommPtr AvailableInput = (OsCommPtr) NULL;
87
88extern int xfd_ffs(fd_mask);
89static ConnectionInputPtr AllocateInputBuffer(void);
90static ConnectionOutputPtr AllocateOutputBuffer(void);
91
92
93#define		MAX_TIMES_PER	10
94
95#define	yield_control()				\
96	{ isItTimeToYield = TRUE;		\
97	  timesThisConnection = 0; }
98
99#define	yield_control_no_input()		\
100	{ yield_control();			\
101	  FD_CLR(fd, &ClientsWithInput); }
102
103#define	yield_control_death()			\
104	{ timesThisConnection = 0; }
105
106#define	request_length(req, client)		\
107	((int)((client)->swapped ? lswaps((req)->length) : (req)->length) << 2)
108
109int
110ReadRequest(ClientPtr client)
111{
112    OsCommPtr   oc;
113    ConnectionInputPtr oci;
114    fsReq      *request;
115    int         fd,
116                result,
117                gotnow,
118                needed = 0;
119
120    if (client == NULL)
121	return -1;
122    oc = (OsCommPtr) client->osPrivate;
123    if (oc == NULL)
124	return -1;
125    oci = oc->input;
126    fd = oc->fd;
127    if (oci != NULL && fd < 0)
128	return -1;
129
130    if (AvailableInput) {
131	if (AvailableInput != oc) {
132	    ConnectionInputPtr aci = AvailableInput->input;
133
134	    if (aci->size > BUFWATERMARK) {
135		fsfree(aci->buffer);
136		fsfree(aci);
137	    } else {
138		aci->next = FreeInputs;
139		FreeInputs = aci;
140	    }
141	    AvailableInput->input = (ConnectionInputPtr) NULL;
142	}
143	AvailableInput = (OsCommPtr) NULL;
144    }
145    if (!oci) {
146	if ((oci = FreeInputs ) != (ConnectionInputPtr) 0) {
147	    FreeInputs = oci->next;
148	} else if (!(oci = AllocateInputBuffer())) {
149	    yield_control_death();
150	    return -1;
151	}
152	oc->input = oci;
153    }
154    oci->bufptr += oci->lenLastReq;
155
156    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
157    request = (fsReq *) oci->bufptr;
158
159    /* not enough for a request */
160    if ((gotnow < SIZEOF(fsReq)) ||
161	    (gotnow < (needed = request_length(request, client)))) {
162	oci->lenLastReq = 0;
163	if ((gotnow < SIZEOF(fsReq)) || needed == 0)
164	    needed = SIZEOF(fsReq);
165	else if (needed > MAXBUFSIZE) {
166	    yield_control_death();
167	    return -1;
168	}
169	/* see if we need to shift up a partial request so the rest can fit */
170	if ((gotnow == 0) ||
171	    ((oci->bufptr - oci->buffer + needed) > oci->size))
172	{
173	    if ((gotnow > 0) && (oci->bufptr != oci->buffer))
174		memmove( oci->buffer, oci->bufptr, gotnow);
175	    /* grow buffer if necessary */
176	    if (needed > oci->size) {
177		char       *ibuf;
178
179		ibuf = (char *) fsrealloc(oci->buffer, needed);
180		if (!ibuf) {
181		    yield_control_death();
182		    return -1;
183		}
184		oci->size = needed;
185		oci->buffer = ibuf;
186	    }
187	    oci->bufptr = oci->buffer;
188	    oci->bufcnt = gotnow;
189	}
190	/* fill 'er up */
191	if (oc->trans_conn == NULL) {
192	    yield_control_death();
193	    return -1;
194	}
195	result = _FontTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
196		      oci->size - oci->bufcnt);
197	if (result <= 0) {
198#if !(defined(SVR4) && defined(i386) && !defined(sun))
199	    if ((result < 0) && ETEST(errno)) {
200		yield_control_no_input();
201		return 0;
202	    } else
203#endif
204	    {
205
206		yield_control_death();
207		return -1;
208	    }
209	}
210	oci->bufcnt += result;
211	gotnow += result;
212
213	/* free up space after huge requests */
214	if ((oci->size > BUFWATERMARK) &&
215		(oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
216	    char       *ibuf;
217
218	    ibuf = (char *) fsrealloc(oci->buffer, BUFSIZE);
219	    if (ibuf) {
220		oci->size = BUFSIZE;
221		oci->buffer = ibuf;
222		oci->bufptr = ibuf + oci->bufcnt - gotnow;
223	    }
224	}
225	request = (fsReq *) oci->bufptr;
226	if ((gotnow < SIZEOF(fsReq)) ||
227	    (gotnow < (needed = request_length(request, client)))) {
228	    yield_control_no_input();
229	    return 0;
230	}
231    }
232    if (needed == 0)
233	needed = SIZEOF(fsReq);
234    oci->lenLastReq = needed;
235    /*
236     * Check to see if client has at least one whole request in the buffer. If
237     * there is only a partial request, treat like buffer is empty so that
238     * select() will be called again and other clients can get into the queue.
239     */
240
241    if (gotnow >= needed + SIZEOF(fsReq)) {
242	request = (fsReq *) (oci->bufptr + needed);
243	if (gotnow >= needed + request_length(request, client))
244	    FD_SET(fd, &ClientsWithInput);
245	else
246	    yield_control_no_input();
247    } else {
248	if (gotnow == needed)
249	    AvailableInput = oc;
250	yield_control_no_input();
251    }
252
253    if (++timesThisConnection >= MAX_TIMES_PER)
254	yield_control();
255
256    client->requestBuffer = (pointer) oci->bufptr;
257    return needed;
258}
259
260Bool
261InsertFakeRequest(ClientPtr client, char *data, int count)
262{
263    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
264    ConnectionInputPtr oci = oc->input;
265    int         fd = oc->fd;
266    fsReq      *request;
267    int         gotnow,
268                moveup;
269
270    if (AvailableInput) {
271	if (AvailableInput != oc) {
272	    register ConnectionInputPtr aci = AvailableInput->input;
273
274	    if (aci->size > BUFWATERMARK) {
275		fsfree(aci->buffer);
276		fsfree(aci);
277	    } else {
278		aci->next = FreeInputs;
279		FreeInputs = aci;
280	    }
281	    AvailableInput->input = (ConnectionInputPtr) NULL;
282	}
283	AvailableInput = (OsCommPtr) NULL;
284    }
285    if (!oci) {
286	if ((oci = FreeInputs) != (ConnectionInputPtr) 0)
287	    FreeInputs = oci->next;
288	else if (!(oci = AllocateInputBuffer()))
289	    return FALSE;
290	oc->input = oci;
291
292    }
293    oci->bufptr += oci->lenLastReq;
294    oci->lenLastReq = 0;
295    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
296    if ((gotnow + count) > oci->size) {
297	char       *ibuf;
298
299	ibuf = (char *) fsrealloc(oci->buffer, gotnow + count);
300	if (!ibuf)
301	    return FALSE;
302	oci->size = gotnow + count;
303	oci->buffer = ibuf;
304	oci->bufptr = ibuf + oci->bufcnt - gotnow;
305    }
306    moveup = count - (oci->bufptr - oci->buffer);
307    if (moveup > 0) {
308	if (gotnow > 0)
309	    memmove( oci->bufptr + moveup, oci->bufptr, gotnow);
310	oci->bufptr += moveup;
311	oci->bufcnt += moveup;
312    }
313    memmove( oci->bufptr - count, data, count);
314    oci->bufptr -= count;
315    request = (fsReq *) oci->bufptr;
316    gotnow += count;
317    if ((gotnow >= SIZEOF(fsReq)) &&
318	    (gotnow >= request_length(request, client)))
319	FD_SET(fd, &ClientsWithInput);
320    else
321	yield_control_no_input();
322    return TRUE;
323}
324
325void
326ResetCurrentRequest(ClientPtr client)
327{
328    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
329    ConnectionInputPtr oci = oc->input;
330    int         fd = oc->fd;
331    fsReq      *request;
332    int         gotnow;
333
334    if (AvailableInput == oc)
335	AvailableInput = (OsCommPtr) NULL;
336    oci->lenLastReq = 0;
337    request = (fsReq *) oci->bufptr;
338    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
339    if ((gotnow >= SIZEOF(fsReq)) &&
340	    (gotnow >= request_length(request, client))) {
341	FD_SET(fd, &ClientsWithInput);
342	yield_control();
343    } else {
344	yield_control_no_input();
345    }
346}
347
348int
349FlushClient(
350    ClientPtr   client,
351    OsCommPtr   oc,
352    char       *extraBuf,
353    int         extraCount,
354    int         padsize)
355{
356    ConnectionOutputPtr oco = oc->output;
357    int         fd = oc->fd;
358    struct iovec iov[3];
359    char        padBuffer[3];
360    long        written;
361    long        notWritten;
362    long        todo;
363
364    if (!oco)
365	return 0;
366    written = 0;
367    notWritten = oco->count + extraCount + padsize;
368    todo = notWritten;
369    while (notWritten) {
370	long        before = written;
371	long        remain = todo;
372	int         i = 0;
373	long        len;
374
375	/*-
376	 * You could be very general here and have "in" and "out" iovecs and
377	 * write a loop without using a macro, but what the heck.  This
378	 * translates to:
379	 *
380	 * 	how much of this piece is new?
381	 *	if more new then we are trying this time, clamp
382	 *	if nothing new
383	 *	    then bump down amount already written, for next piece
384	 *	    else put new stuff in iovec, will need all of next piece
385	 *
386	 * Note that todo had better be at least 1 or else we'll end up
387	 * writing 0 iovecs.
388	 */
389
390#define	InsertIOV(pointer, length)	\
391	len = (length) - before;	\
392	if (len > remain)		\
393	    len = remain;		\
394	if (len <= 0) {			\
395	    before = (-len);		\
396	} else {			\
397	    iov[i].iov_len = len;	\
398	    iov[i].iov_base = (pointer) + before; \
399	    i++;			\
400	    remain -= len;		\
401	    before = 0;			\
402	}
403
404	InsertIOV((char *) oco->buf, oco->count);
405	InsertIOV(extraBuf, extraCount);
406	InsertIOV(padBuffer, padsize);
407
408	errno = 0;
409	if (oc->trans_conn && (len = _FontTransWritev(oc->trans_conn, iov, i)) >= 0) {
410	    written += len;
411	    notWritten -= len;
412	    todo = notWritten;
413	} else if (ETEST(errno)
414#ifdef SUNSYSV /* check for another brain-damaged OS bug */
415		 || (errno == 0)
416#endif
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 *)fsalloc(count); memset(buf, 0, count);
585    }
586     write_to_client_internal(client, count, buf, padlength[count & 3]);
587    if (flag)
588	fsfree(buf);
589}
590
591static ConnectionInputPtr
592AllocateInputBuffer(void)
593{
594    register ConnectionInputPtr oci;
595
596    oci = (ConnectionInputPtr) fsalloc(sizeof(ConnectionInput));
597    if (!oci)
598	return (ConnectionInputPtr) NULL;
599    oci->buffer = (char *) fsalloc(BUFSIZE);
600    if (!oci->buffer) {
601	fsfree(oci);
602	return (ConnectionInputPtr) NULL;
603    }
604    oci->next = NULL;
605    oci->size = BUFSIZE;
606    oci->bufptr = oci->buffer;
607    oci->bufcnt = 0;
608    oci->lenLastReq = 0;
609    return oci;
610}
611
612static ConnectionOutputPtr
613AllocateOutputBuffer(void)
614{
615    register ConnectionOutputPtr oco;
616
617    oco = (ConnectionOutputPtr) fsalloc(sizeof(ConnectionOutput));
618    if (!oco)
619	return (ConnectionOutputPtr) NULL;
620    oco->buf = (unsigned char *) fsalloc(BUFSIZE);
621    if (!oco->buf) {
622	fsfree(oco);
623	return (ConnectionOutputPtr) NULL;
624    }
625    oco->size = BUFSIZE;
626    oco->count = 0;
627    return oco;
628}
629
630
631void
632FreeOsBuffers(OsCommPtr oc)
633{
634    register ConnectionInputPtr oci;
635    register ConnectionOutputPtr oco;
636
637    if (AvailableInput == oc)
638	AvailableInput = (OsCommPtr) NULL;
639    if ((oci = oc->input) != (ConnectionInputPtr) 0) {
640	if (FreeInputs) {
641	    fsfree(oci->buffer);
642	    fsfree(oci);
643	} else {
644	    FreeInputs = oci;
645	    oci->next = (ConnectionInputPtr) NULL;
646	    oci->bufptr = oci->buffer;
647	    oci->bufcnt = 0;
648	    oci->lenLastReq = 0;
649	}
650    }
651    if ((oco = oc->output) != (ConnectionOutputPtr) 0) {
652	if (FreeOutputs) {
653	    fsfree(oco->buf);
654	    fsfree(oco);
655	} else {
656	    FreeOutputs = oco;
657	    oco->next = (ConnectionOutputPtr) NULL;
658	    oco->count = 0;
659	}
660    }
661}
662
663void
664ResetOsBuffers(void)
665{
666    register ConnectionInputPtr oci;
667    register ConnectionOutputPtr oco;
668
669    while ((oci = FreeInputs) != (ConnectionInputPtr) 0) {
670	FreeInputs = oci->next;
671	fsfree(oci->buffer);
672	fsfree(oci);
673    }
674    while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
675	FreeOutputs = oco->next;
676	fsfree(oco->buf);
677	fsfree(oco);
678    }
679}
680