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