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