io.c revision ce6676db
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	"xfs-config.h"
50
51#include	<X11/Xtrans/Xtrans.h>
52#include	<stdio.h>
53#include	<errno.h>
54#include	<sys/types.h>
55#include	<sys/param.h>
56#ifndef __UNIXOS2__
57#include	<sys/uio.h>
58#endif
59
60#include	<X11/fonts/FSproto.h>
61#include	"clientstr.h"
62#include	"X11/Xpoll.h"
63#include	"osdep.h"
64#include	"globals.h"
65#include	"dispatch.h"
66
67
68/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
69 * systems are broken and return EWOULDBLOCK when they should return EAGAIN
70  */
71
72#if defined(EAGAIN) && defined(EWOULDBLOCK)
73#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
74#else
75
76#ifdef EAGAIN
77#define ETEST(err) (err == EAGAIN)
78#else
79#define ETEST(err) (err == EWOULDBLOCK)
80#endif
81
82#endif
83
84static int  timesThisConnection = 0;
85static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
86static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
87static OsCommPtr AvailableInput = (OsCommPtr) NULL;
88
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
158#ifdef WORD64
159    /* need 8-byte alignment */
160    if ((oci->bufptr - oci->buffer) & 7  &&  gotnow > 0)
161    {
162	memmove( oci->buffer, oci->bufptr, gotnow);
163	oci->bufptr = oci->buffer;
164	oci->bufcnt = gotnow;
165    }
166#endif
167
168    request = (fsReq *) oci->bufptr;
169
170    /* not enough for a request */
171    if ((gotnow < SIZEOF(fsReq)) ||
172	    (gotnow < (needed = request_length(request, client)))) {
173	oci->lenLastReq = 0;
174	if ((gotnow < SIZEOF(fsReq)) || needed == 0)
175	    needed = SIZEOF(fsReq);
176	else if (needed > MAXBUFSIZE) {
177	    yield_control_death();
178	    return -1;
179	}
180	/* see if we need to shift up a partial request so the rest can fit */
181	if ((gotnow == 0) ||
182	    ((oci->bufptr - oci->buffer + needed) > oci->size))
183	{
184	    if ((gotnow > 0) && (oci->bufptr != oci->buffer))
185		memmove( oci->buffer, oci->bufptr, gotnow);
186	    /* grow buffer if necessary */
187	    if (needed > oci->size) {
188		char       *ibuf;
189
190		ibuf = (char *) fsrealloc(oci->buffer, needed);
191		if (!ibuf) {
192		    yield_control_death();
193		    return -1;
194		}
195		oci->size = needed;
196		oci->buffer = ibuf;
197	    }
198	    oci->bufptr = oci->buffer;
199	    oci->bufcnt = gotnow;
200	}
201	/* fill 'er up */
202	if (oc->trans_conn == NULL) {
203	    yield_control_death();
204	    return -1;
205	}
206	result = _FontTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
207		      oci->size - oci->bufcnt);
208	if (result <= 0) {
209#if !(defined(SVR4) && defined(i386) && !defined(sun))
210	    if ((result < 0) && ETEST(errno)) {
211		yield_control_no_input();
212		return 0;
213	    } else
214#endif
215	    {
216
217		yield_control_death();
218		return -1;
219	    }
220	}
221	oci->bufcnt += result;
222	gotnow += result;
223
224	/* free up space after huge requests */
225	if ((oci->size > BUFWATERMARK) &&
226		(oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
227	    char       *ibuf;
228
229	    ibuf = (char *) fsrealloc(oci->buffer, BUFSIZE);
230	    if (ibuf) {
231		oci->size = BUFSIZE;
232		oci->buffer = ibuf;
233		oci->bufptr = ibuf + oci->bufcnt - gotnow;
234	    }
235	}
236	request = (fsReq *) oci->bufptr;
237	if ((gotnow < SIZEOF(fsReq)) ||
238	    (gotnow < (needed = request_length(request, client)))) {
239	    yield_control_no_input();
240	    return 0;
241	}
242    }
243    if (needed == 0)
244	needed = SIZEOF(fsReq);
245    oci->lenLastReq = needed;
246    /*
247     * Check to see if client has at least one whole request in the buffer. If
248     * there is only a partial request, treat like buffer is empty so that
249     * select() will be called again and other clients can get into the queue.
250     */
251
252    if (gotnow >= needed + SIZEOF(fsReq)) {
253	request = (fsReq *) (oci->bufptr + needed);
254	if (gotnow >= needed + request_length(request, client))
255	    FD_SET(fd, &ClientsWithInput);
256	else
257	    yield_control_no_input();
258    } else {
259	if (gotnow == needed)
260	    AvailableInput = oc;
261	yield_control_no_input();
262    }
263
264    if (++timesThisConnection >= MAX_TIMES_PER)
265	yield_control();
266
267    client->requestBuffer = (pointer) oci->bufptr;
268    return needed;
269}
270
271Bool
272InsertFakeRequest(ClientPtr client, char *data, int count)
273{
274    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
275    ConnectionInputPtr oci = oc->input;
276    int         fd = oc->fd;
277    fsReq      *request;
278    int         gotnow,
279                moveup;
280
281    if (AvailableInput) {
282	if (AvailableInput != oc) {
283	    register ConnectionInputPtr aci = AvailableInput->input;
284
285	    if (aci->size > BUFWATERMARK) {
286		fsfree(aci->buffer);
287		fsfree(aci);
288	    } else {
289		aci->next = FreeInputs;
290		FreeInputs = aci;
291	    }
292	    AvailableInput->input = (ConnectionInputPtr) NULL;
293	}
294	AvailableInput = (OsCommPtr) NULL;
295    }
296    if (!oci) {
297	if ((oci = FreeInputs) != (ConnectionInputPtr) 0)
298	    FreeInputs = oci->next;
299	else if (!(oci = AllocateInputBuffer()))
300	    return FALSE;
301	oc->input = oci;
302
303    }
304    oci->bufptr += oci->lenLastReq;
305    oci->lenLastReq = 0;
306    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
307    if ((gotnow + count) > oci->size) {
308	char       *ibuf;
309
310	ibuf = (char *) fsrealloc(oci->buffer, gotnow + count);
311	if (!ibuf)
312	    return FALSE;
313	oci->size = gotnow + count;
314	oci->buffer = ibuf;
315	oci->bufptr = ibuf + oci->bufcnt - gotnow;
316    }
317    moveup = count - (oci->bufptr - oci->buffer);
318    if (moveup > 0) {
319	if (gotnow > 0)
320	    memmove( oci->bufptr + moveup, oci->bufptr, gotnow);
321	oci->bufptr += moveup;
322	oci->bufcnt += moveup;
323    }
324    memmove( oci->bufptr - count, data, count);
325    oci->bufptr -= count;
326    request = (fsReq *) oci->bufptr;
327    gotnow += count;
328    if ((gotnow >= SIZEOF(fsReq)) &&
329	    (gotnow >= request_length(request, client)))
330	FD_SET(fd, &ClientsWithInput);
331    else
332	yield_control_no_input();
333    return TRUE;
334}
335
336void
337ResetCurrentRequest(ClientPtr client)
338{
339    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
340    ConnectionInputPtr oci = oc->input;
341    int         fd = oc->fd;
342    fsReq      *request;
343    int         gotnow;
344
345    if (AvailableInput == oc)
346	AvailableInput = (OsCommPtr) NULL;
347    oci->lenLastReq = 0;
348    request = (fsReq *) oci->bufptr;
349    gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
350    if ((gotnow >= SIZEOF(fsReq)) &&
351	    (gotnow >= request_length(request, client))) {
352	FD_SET(fd, &ClientsWithInput);
353	yield_control();
354    } else {
355	yield_control_no_input();
356    }
357}
358
359int
360FlushClient(
361    ClientPtr   client,
362    OsCommPtr   oc,
363    char       *extraBuf,
364    int         extraCount,
365    int         padsize)
366{
367    ConnectionOutputPtr oco = oc->output;
368    int         fd = oc->fd;
369    struct iovec iov[3];
370    char        padBuffer[3];
371    long        written;
372    long        notWritten;
373    long        todo;
374
375    if (!oco)
376	return 0;
377    written = 0;
378    notWritten = oco->count + extraCount + padsize;
379    todo = notWritten;
380    while (notWritten) {
381	long        before = written;
382	long        remain = todo;
383	int         i = 0;
384	long        len;
385
386	/*-
387	 * You could be very general here and have "in" and "out" iovecs and
388	 * write a loop without using a macro, but what the heck.  This
389	 * translates to:
390	 *
391	 * 	how much of this piece is new?
392	 *	if more new then we are trying this time, clamp
393	 *	if nothing new
394	 *	    then bump down amount already written, for next piece
395	 *	    else put new stuff in iovec, will need all of next piece
396	 *
397	 * Note that todo had better be at least 1 or else we'll end up
398	 * writing 0 iovecs.
399	 */
400
401#define	InsertIOV(pointer, length)	\
402	len = (length) - before;	\
403	if (len > remain)		\
404	    len = remain;		\
405	if (len <= 0) {			\
406	    before = (-len);		\
407	} else {			\
408	    iov[i].iov_len = len;	\
409	    iov[i].iov_base = (pointer) + before; \
410	    i++;			\
411	    remain -= len;		\
412	    before = 0;			\
413	}
414
415	InsertIOV((char *) oco->buf, oco->count);
416	InsertIOV(extraBuf, extraCount);
417	InsertIOV(padBuffer, padsize);
418
419	errno = 0;
420	if (oc->trans_conn && (len = _FontTransWritev(oc->trans_conn, iov, i)) >= 0) {
421	    written += len;
422	    notWritten -= len;
423	    todo = notWritten;
424	} else if (ETEST(errno)
425#ifdef SUNSYSV /* check for another brain-damaged OS bug */
426		 || (errno == 0)
427#endif
428#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
429		 || ((errno == EMSGSIZE) && (todo == 1))
430#endif
431		)
432	{
433	    FD_SET(fd, &ClientsWriteBlocked);
434	    AnyClientsWriteBlocked = TRUE;
435
436	    if (written < oco->count) {
437		if (written > 0) {
438		    oco->count -= written;
439		    memmove( (char *) oco->buf, (char *) oco->buf + written,
440			    oco->count);
441		    written = 0;
442		}
443	    } else {
444		written -= oco->count;
445		oco->count = 0;
446	    }
447
448	    /* grow buffer if necessary */
449	    if (notWritten > oco->size) {
450		unsigned char *obuf;
451
452		obuf = (unsigned char *) fsrealloc(oco->buf,
453					      notWritten + OutputBufferSize);
454		if (!obuf) {
455		    if (oc->trans_conn)
456			_FontTransClose(oc->trans_conn);
457		    oc->trans_conn = NULL;
458		    MarkClientException(client);
459		    oco->count = 0;
460		    return -1;
461		}
462		oco->size = notWritten + OutputBufferSize;
463		oco->buf = obuf;
464	    }
465	    if ((len = extraCount - written) > 0) {
466		memmove( (char *) oco->buf + oco->count,
467			extraBuf + written, len);
468	    }
469	    oco->count = notWritten;
470	    return extraCount;
471	}
472#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
473	else if (errno == EMSGSIZE)
474	{
475	    todo >>= 1;
476	}
477#endif
478	else
479	{
480	    if (oc->trans_conn)
481	        _FontTransClose(oc->trans_conn);
482	    oc->trans_conn = NULL;
483	    MarkClientException(client);
484	    oco->count = 0;
485	    return -1;
486	}
487    }
488
489    /* everything was flushed */
490    oco->count = 0;
491
492    /* clear the write block if it was set */
493    if (AnyClientsWriteBlocked) {
494	FD_CLR(fd, &ClientsWriteBlocked);
495	if (!XFD_ANYSET(&ClientsWriteBlocked))
496	    AnyClientsWriteBlocked = FALSE;
497    }
498    if (oco->size > BUFWATERMARK) {
499	fsfree(oco->buf);
500	fsfree(oco);
501    } else {
502	oco->next = FreeOutputs;
503	FreeOutputs = oco;
504    }
505    oc->output = (ConnectionOutputPtr) NULL;
506
507    return extraCount;
508}
509
510void
511FlushAllOutput(void)
512{
513    int         index, base;
514    fd_mask	mask;
515    OsCommPtr   oc;
516    ClientPtr   client;
517
518    if (!NewOutputPending)
519	return;
520
521    NewOutputPending = FALSE;
522
523    for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) {
524	mask = OutputPending.fds_bits[base];
525	OutputPending.fds_bits[base] = 0;
526	while (mask) {
527	    index = ffs(mask) - 1;
528	    mask &= ~lowbit(mask);
529	    if ((index = ConnectionTranslation[(base << 5) + index]) == 0)
530		continue;
531	    client = clients[index];
532	    if (client->clientGone == CLIENT_GONE)
533		continue;
534	    oc = (OsCommPtr) client->osPrivate;
535	    if (FD_ISSET(oc->fd, &ClientsWithInput)) {
536		FD_SET(oc->fd, &OutputPending);
537		NewOutputPending = TRUE;
538	    } else {
539		(void) FlushClient(client, oc, (char *) NULL, 0, 0);
540	    }
541	}
542    }
543}
544
545/*
546 * returns number of bytes written
547 */
548static int
549write_to_client_internal(ClientPtr client, int count, char *buf, int padBytes)
550{
551    OsCommPtr   oc = (OsCommPtr) client->osPrivate;
552    ConnectionOutputPtr oco = oc->output;
553
554    if (!count)
555	return 0;
556
557    if (!oco) {
558	if ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
559	    FreeOutputs = oco->next;
560	} else if (!(oco = AllocateOutputBuffer())) {
561	    _FontTransClose(oc->trans_conn);
562	    oc->trans_conn = NULL;
563	    MarkClientException(client);
564	    return -1;
565	}
566	oc->output = oco;
567    }
568    if (oco->count + count + padBytes > oco->size) {
569	FD_CLR(oc->fd, &OutputPending);
570	NewOutputPending = FALSE;
571	return FlushClient(client, oc, buf, count, padBytes);
572    }
573    NewOutputPending = TRUE;
574    FD_SET(oc->fd, &OutputPending);
575    memmove( (char *) oco->buf + oco->count, buf, count);
576    oco->count += count + padBytes;
577
578    return count;
579}
580
581void
582WriteToClientUnpadded(ClientPtr client, int count, char *buf)
583{
584    write_to_client_internal(client, count, buf, 0);
585}
586
587static int  padlength[4] = {0, 3, 2, 1};
588
589void
590WriteToClient(ClientPtr client, int count, char *buf)
591{
592    int flag = 0;
593    if (NULL == buf) {
594	flag = -1;
595	buf = (char *)fsalloc(count); memset(buf, 0, count);
596    }
597     write_to_client_internal(client, count, buf, padlength[count & 3]);
598    if (flag)
599	fsfree(buf);
600}
601
602static ConnectionInputPtr
603AllocateInputBuffer(void)
604{
605    register ConnectionInputPtr oci;
606
607    oci = (ConnectionInputPtr) fsalloc(sizeof(ConnectionInput));
608    if (!oci)
609	return (ConnectionInputPtr) NULL;
610    oci->buffer = (char *) fsalloc(BUFSIZE);
611    if (!oci->buffer) {
612	fsfree(oci);
613	return (ConnectionInputPtr) NULL;
614    }
615    oci->next = NULL;
616    oci->size = BUFSIZE;
617    oci->bufptr = oci->buffer;
618    oci->bufcnt = 0;
619    oci->lenLastReq = 0;
620    return oci;
621}
622
623static ConnectionOutputPtr
624AllocateOutputBuffer(void)
625{
626    register ConnectionOutputPtr oco;
627
628    oco = (ConnectionOutputPtr) fsalloc(sizeof(ConnectionOutput));
629    if (!oco)
630	return (ConnectionOutputPtr) NULL;
631    oco->buf = (unsigned char *) fsalloc(BUFSIZE);
632    if (!oco->buf) {
633	fsfree(oco);
634	return (ConnectionOutputPtr) NULL;
635    }
636    oco->size = BUFSIZE;
637    oco->count = 0;
638    return oco;
639}
640
641
642void
643FreeOsBuffers(OsCommPtr oc)
644{
645    register ConnectionInputPtr oci;
646    register ConnectionOutputPtr oco;
647
648    if (AvailableInput == oc)
649	AvailableInput = (OsCommPtr) NULL;
650    if ((oci = oc->input) != (ConnectionInputPtr) 0) {
651	if (FreeInputs) {
652	    fsfree(oci->buffer);
653	    fsfree(oci);
654	} else {
655	    FreeInputs = oci;
656	    oci->next = (ConnectionInputPtr) NULL;
657	    oci->bufptr = oci->buffer;
658	    oci->bufcnt = 0;
659	    oci->lenLastReq = 0;
660	}
661    }
662    if ((oco = oc->output) != (ConnectionOutputPtr) 0) {
663	if (FreeOutputs) {
664	    fsfree(oco->buf);
665	    fsfree(oco);
666	} else {
667	    FreeOutputs = oco;
668	    oco->next = (ConnectionOutputPtr) NULL;
669	    oco->count = 0;
670	}
671    }
672}
673
674void
675ResetOsBuffers(void)
676{
677    register ConnectionInputPtr oci;
678    register ConnectionOutputPtr oco;
679
680    while ((oci = FreeInputs) != (ConnectionInputPtr) 0) {
681	FreeInputs = oci->next;
682	fsfree(oci->buffer);
683	fsfree(oci);
684    }
685    while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
686	FreeOutputs = oco->next;
687	fsfree(oco->buf);
688	fsfree(oco);
689    }
690}
691