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