xref: /onnv-gate/usr/src/cmd/lp/lib/msgs/read_fifo.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.11	*/
32*0Sstevel@tonic-gate /* LINTLIBRARY */
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate # include	<errno.h>
36*0Sstevel@tonic-gate # include	<string.h>
37*0Sstevel@tonic-gate #include <syslog.h>
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate # include	"lp.h"
40*0Sstevel@tonic-gate # include	"msgs.h"
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate extern char	Resync[];
43*0Sstevel@tonic-gate extern char	Endsync[];
44*0Sstevel@tonic-gate static int	Had_Full_Buffer = 1;
45*0Sstevel@tonic-gate int		Garbage_Bytes	= 0;
46*0Sstevel@tonic-gate int		Garbage_Messages= 0;
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate /*
49*0Sstevel@tonic-gate ** A real message is written in one piece, and the write
50*0Sstevel@tonic-gate ** is atomic. Thus, even if the O_NDELAY flag is set,
51*0Sstevel@tonic-gate ** if we read part of the real message, we can continue
52*0Sstevel@tonic-gate ** to read the rest of it in as many steps as we want
53*0Sstevel@tonic-gate ** (up to the size of the message, of course!) without
54*0Sstevel@tonic-gate ** UNIX returning 0 because no data is available.
55*0Sstevel@tonic-gate ** So, a real message doesn't have to be read in one piece,
56*0Sstevel@tonic-gate ** which is good since we don't know how much to read!
57*0Sstevel@tonic-gate **
58*0Sstevel@tonic-gate ** Fake messages, or improperly written messages, don't
59*0Sstevel@tonic-gate ** have this nice property.
60*0Sstevel@tonic-gate **
61*0Sstevel@tonic-gate ** INTERRUPTED READS:
62*0Sstevel@tonic-gate **
63*0Sstevel@tonic-gate ** If a signal occurs during an attempted read, we can exit.
64*0Sstevel@tonic-gate ** The caller can retry the read and we will correctly restart
65*0Sstevel@tonic-gate ** it. The correctness of this assertion can be seen by noticing
66*0Sstevel@tonic-gate ** that at the beginning of each READ below, we can go back
67*0Sstevel@tonic-gate ** to the first statement executed (the first READ below)
68*0Sstevel@tonic-gate ** and correctly reexecute the code.
69*0Sstevel@tonic-gate **
70*0Sstevel@tonic-gate ** If the last writer closed the fifo, we'll read 0 bytes
71*0Sstevel@tonic-gate ** (at least on the subsequent read). If we were in the
72*0Sstevel@tonic-gate ** middle of reading a message, we were reading a bogus
73*0Sstevel@tonic-gate ** message (but see below).
74*0Sstevel@tonic-gate **
75*0Sstevel@tonic-gate ** If we read less than we expect, it's because we were
76*0Sstevel@tonic-gate ** reading a fake message (but see below).
77*0Sstevel@tonic-gate **
78*0Sstevel@tonic-gate ** HOWEVER: In the last two cases, we may have ONE OR MORE
79*0Sstevel@tonic-gate ** REAL MESSAGES snuggled in amongst the trash!
80*0Sstevel@tonic-gate **
81*0Sstevel@tonic-gate ** All this verbal rambling is preface to let you understand why we
82*0Sstevel@tonic-gate ** buffer the data (which is a shame, but necessary).
83*0Sstevel@tonic-gate */
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate /*
86*0Sstevel@tonic-gate ** As long as we get real messages, we can avoid needless function calls.
87*0Sstevel@tonic-gate ** The SYNC argument in this macro should be set if the resynch. bytes
88*0Sstevel@tonic-gate ** have been read--i.e. if the rest of the message is trying to be read.
89*0Sstevel@tonic-gate ** In this case, if we had not read a full buffer last time, then we
90*0Sstevel@tonic-gate ** must be in the middle of a bogus message.
91*0Sstevel@tonic-gate */
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate #define UNSYNCHED_READ(N) \
94*0Sstevel@tonic-gate     if (fbp->psave_end - fbp->psave < N || fbp->psave >= fbp->psave_end) \
95*0Sstevel@tonic-gate     { \
96*0Sstevel@tonic-gate 	switch (_buffer(fifo)) \
97*0Sstevel@tonic-gate 	{ \
98*0Sstevel@tonic-gate 	    case -1: \
99*0Sstevel@tonic-gate 		return (-1); \
100*0Sstevel@tonic-gate 	    case 0: \
101*0Sstevel@tonic-gate 		if (fbp->psave_end > fbp->psave) \
102*0Sstevel@tonic-gate 		    goto SyncUp; \
103*0Sstevel@tonic-gate 		return (0); \
104*0Sstevel@tonic-gate 	} \
105*0Sstevel@tonic-gate     }
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate #define SYNCHED_READ(N) \
108*0Sstevel@tonic-gate     if (fbp->psave_end - fbp->psave < N || fbp->psave >= fbp->psave_end) \
109*0Sstevel@tonic-gate     { \
110*0Sstevel@tonic-gate 	switch (_buffer(fifo)) \
111*0Sstevel@tonic-gate 	{ \
112*0Sstevel@tonic-gate 	    case -1: \
113*0Sstevel@tonic-gate 		return (-1); \
114*0Sstevel@tonic-gate 	    case 0: \
115*0Sstevel@tonic-gate 		if (fbp->psave_end > fbp->psave) \
116*0Sstevel@tonic-gate 		    goto SyncUp; \
117*0Sstevel@tonic-gate 		return (0); \
118*0Sstevel@tonic-gate 	} \
119*0Sstevel@tonic-gate 	if (!Had_Full_Buffer) \
120*0Sstevel@tonic-gate 	    goto SyncUp; \
121*0Sstevel@tonic-gate     }
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate /*
124*0Sstevel@tonic-gate ** read_fifo() - READ A BUFFER WITH HEADER AND CHECKSUM
125*0Sstevel@tonic-gate */
126*0Sstevel@tonic-gate int
127*0Sstevel@tonic-gate read_fifo (fifo, buf, size)
128*0Sstevel@tonic-gate int		fifo;
129*0Sstevel@tonic-gate char		*buf;
130*0Sstevel@tonic-gate unsigned int	size;
131*0Sstevel@tonic-gate {
132*0Sstevel@tonic-gate     register fifobuffer_t *fbp;
133*0Sstevel@tonic-gate     register unsigned int real_chksum,
134*0Sstevel@tonic-gate 			  chksum,
135*0Sstevel@tonic-gate 			  real_size;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate     /*
138*0Sstevel@tonic-gate     ** Make sure we start on a message boundary. The first
139*0Sstevel@tonic-gate     ** line of defense is to look for the resync. bytes.
140*0Sstevel@tonic-gate     **
141*0Sstevel@tonic-gate     ** The "SyncUp" label is global to this routine (below this point)
142*0Sstevel@tonic-gate     ** and is called whenever we determine that we're out
143*0Sstevel@tonic-gate     ** of sync. with the incoming bytes.
144*0Sstevel@tonic-gate     */
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate     if (!(fbp=GetFifoBuffer (fifo)))
147*0Sstevel@tonic-gate 	return	-1;
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate     UNSYNCHED_READ (HEAD_RESYNC_LEN);
150*0Sstevel@tonic-gate     while (*fbp->psave != Resync[0] || *(fbp->psave + 1) != Resync[1])
151*0Sstevel@tonic-gate     {
152*0Sstevel@tonic-gate SyncUp:
153*0Sstevel@tonic-gate #if	defined(TRACE_MESSAGES)
154*0Sstevel@tonic-gate 	if (trace_messages)
155*0Sstevel@tonic-gate 		syslog(LOG_DEBUG, "DISCARD %c\n", *fbp->psave);
156*0Sstevel@tonic-gate #endif
157*0Sstevel@tonic-gate 	fbp->psave++;
158*0Sstevel@tonic-gate 	Garbage_Bytes++;
159*0Sstevel@tonic-gate 	UNSYNCHED_READ (HEAD_RESYNC_LEN);
160*0Sstevel@tonic-gate     }
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate     /*
164*0Sstevel@tonic-gate     ** We're sync'd, so read the full header.
165*0Sstevel@tonic-gate     */
166*0Sstevel@tonic-gate 
167*0Sstevel@tonic-gate     SYNCHED_READ (HEAD_LEN);
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate     /*
171*0Sstevel@tonic-gate     ** If the header size is smaller than the minimum size for a header,
172*0Sstevel@tonic-gate     ** or larger than allowed, we must assume that we really aren't
173*0Sstevel@tonic-gate     ** synchronized.
174*0Sstevel@tonic-gate     */
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate     real_size = stoh(fbp->psave + HEAD_SIZE);
177*0Sstevel@tonic-gate     if (real_size < CONTROL_LEN || MSGMAX < real_size)
178*0Sstevel@tonic-gate     {
179*0Sstevel@tonic-gate #if	defined(TRACE_MESSAGES)
180*0Sstevel@tonic-gate 	if (trace_messages)
181*0Sstevel@tonic-gate 		syslog(LOG_DEBUG, "BAD SIZE\n");
182*0Sstevel@tonic-gate #endif
183*0Sstevel@tonic-gate 	goto SyncUp;
184*0Sstevel@tonic-gate     }
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate     /*
187*0Sstevel@tonic-gate     ** We have the header. Now we can finally read the rest of the
188*0Sstevel@tonic-gate     ** message...
189*0Sstevel@tonic-gate     */
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate     SYNCHED_READ (real_size);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate     /*
195*0Sstevel@tonic-gate     ** ...but did we read a real message?...
196*0Sstevel@tonic-gate     */
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate     if
199*0Sstevel@tonic-gate     (
200*0Sstevel@tonic-gate 	   *(fbp->psave + TAIL_ENDSYNC(real_size)) != Endsync[0]
201*0Sstevel@tonic-gate 	|| *(fbp->psave + TAIL_ENDSYNC(real_size) + 1) != Endsync[1]
202*0Sstevel@tonic-gate     )
203*0Sstevel@tonic-gate     {
204*0Sstevel@tonic-gate #if	defined(TRACE_MESSAGES)
205*0Sstevel@tonic-gate 	if (trace_messages)
206*0Sstevel@tonic-gate 		syslog(LOG_DEBUG, "BAD ENDSYNC\n");
207*0Sstevel@tonic-gate #endif
208*0Sstevel@tonic-gate 	Garbage_Messages++;
209*0Sstevel@tonic-gate 	goto SyncUp;
210*0Sstevel@tonic-gate     }
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate     chksum = stoh(fbp->psave + TAIL_CHKSUM(real_size));
213*0Sstevel@tonic-gate     CALC_CHKSUM (fbp->psave, real_size, real_chksum);
214*0Sstevel@tonic-gate     if (real_chksum != chksum)
215*0Sstevel@tonic-gate     {
216*0Sstevel@tonic-gate #if	defined(TRACE_MESSAGES)
217*0Sstevel@tonic-gate 	if (trace_messages)
218*0Sstevel@tonic-gate 		syslog(LOG_DEBUG, "BAD CHKSUM\n");
219*0Sstevel@tonic-gate #endif
220*0Sstevel@tonic-gate 	Garbage_Messages++;
221*0Sstevel@tonic-gate 	goto SyncUp;
222*0Sstevel@tonic-gate     }
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate     /*
225*0Sstevel@tonic-gate     ** ...yes!...but can the caller handle the message?
226*0Sstevel@tonic-gate     */
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate     if (size < real_size)
229*0Sstevel@tonic-gate     {
230*0Sstevel@tonic-gate 	errno = E2BIG;
231*0Sstevel@tonic-gate 	return (-1);
232*0Sstevel@tonic-gate     }
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate     /*
236*0Sstevel@tonic-gate     ** Yes!! We can finally copy the message into the caller's buffer
237*0Sstevel@tonic-gate     ** and remove it from our buffer. That wasn't so bad, was it?
238*0Sstevel@tonic-gate     */
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate #if	defined(TRACE_MESSAGES)
241*0Sstevel@tonic-gate     if (trace_messages)
242*0Sstevel@tonic-gate 	syslog(LOG_DEBUG, "MESSAGE: %-.*s", real_size, fbp->psave);
243*0Sstevel@tonic-gate #endif
244*0Sstevel@tonic-gate     (void)memcpy (buf, fbp->psave, real_size);
245*0Sstevel@tonic-gate     fbp->psave += real_size;
246*0Sstevel@tonic-gate     return (real_size);
247*0Sstevel@tonic-gate }
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate int
250*0Sstevel@tonic-gate peek3_2 (fifo)
251*0Sstevel@tonic-gate int		fifo;
252*0Sstevel@tonic-gate {
253*0Sstevel@tonic-gate     register fifobuffer_t	*fbp;
254*0Sstevel@tonic-gate     register unsigned int	real_size;
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate     /*
257*0Sstevel@tonic-gate     ** Make sure we start on a message boundary. The first
258*0Sstevel@tonic-gate     ** line of defense is to look for the resync. bytes.
259*0Sstevel@tonic-gate     **
260*0Sstevel@tonic-gate     ** The "SyncUp" label is global to this routine (below this point)
261*0Sstevel@tonic-gate     ** and is called whenever we determine that we're out
262*0Sstevel@tonic-gate     ** of sync. with the incoming bytes.
263*0Sstevel@tonic-gate     */
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate     if (!(fbp=GetFifoBuffer (fifo)))
266*0Sstevel@tonic-gate 	return	-1;
267*0Sstevel@tonic-gate     UNSYNCHED_READ (HEAD_RESYNC_LEN);
268*0Sstevel@tonic-gate     while (*fbp->psave != Resync[0] || *(fbp->psave + 1) != Resync[1])
269*0Sstevel@tonic-gate     {
270*0Sstevel@tonic-gate SyncUp:
271*0Sstevel@tonic-gate 	fbp->psave++;
272*0Sstevel@tonic-gate 	Garbage_Bytes++;
273*0Sstevel@tonic-gate 	UNSYNCHED_READ (HEAD_RESYNC_LEN);
274*0Sstevel@tonic-gate     }
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate     /*
278*0Sstevel@tonic-gate     ** We're sync'd, so read the full header.
279*0Sstevel@tonic-gate     */
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate     SYNCHED_READ (HEAD_LEN);
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate     /*
285*0Sstevel@tonic-gate     ** If the header size is smaller than the minimum size for a header,
286*0Sstevel@tonic-gate     ** or larger than allowed, we must assume that we really aren't
287*0Sstevel@tonic-gate     ** synchronized.
288*0Sstevel@tonic-gate     */
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate     real_size = stoh(fbp->psave + HEAD_SIZE);
291*0Sstevel@tonic-gate     if (real_size < CONTROL_LEN || MSGMAX < real_size)
292*0Sstevel@tonic-gate     {
293*0Sstevel@tonic-gate 	goto SyncUp;
294*0Sstevel@tonic-gate     }
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate     return(real_size);
297*0Sstevel@tonic-gate }
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate static int
300*0Sstevel@tonic-gate _buffer (fifo)
301*0Sstevel@tonic-gate int	fifo;
302*0Sstevel@tonic-gate {
303*0Sstevel@tonic-gate 	     int	   n, nbytes, count = 0;
304*0Sstevel@tonic-gate     register fifobuffer_t  *fbp;
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate     /*
307*0Sstevel@tonic-gate     ** As long as we get real messages, and if we chose
308*0Sstevel@tonic-gate     ** SAVE_SIZE well, we shouldn't have to move the data
309*0Sstevel@tonic-gate     ** in the "else" branch below: Each time we call "read"
310*0Sstevel@tonic-gate     ** we aren't likely to get as many bytes as we ask for,
311*0Sstevel@tonic-gate     ** just as many as are in the fifo, AND THIS SHOULD
312*0Sstevel@tonic-gate     ** REPRESENT AN INTEGRAL NUMBER OF MESSAGES. Since
313*0Sstevel@tonic-gate     ** the "read_fifo" routine reads complete messages,
314*0Sstevel@tonic-gate     ** it will end its read at the end of the message,
315*0Sstevel@tonic-gate     ** which (eventually) will make "psave_end" == "psave".
316*0Sstevel@tonic-gate     */
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate     /*
319*0Sstevel@tonic-gate     ** If the buffer is empty, there's nothing to move.
320*0Sstevel@tonic-gate     */
321*0Sstevel@tonic-gate     if (!(fbp = GetFifoBuffer (fifo)))
322*0Sstevel@tonic-gate 	return	-1;
323*0Sstevel@tonic-gate     if (fbp->psave_end == fbp->psave)
324*0Sstevel@tonic-gate 	fbp->psave = fbp->psave_end = fbp->save;	/* sane pointers! */
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate     /*
327*0Sstevel@tonic-gate     ** If the buffer has data at the high end, move it down.
328*0Sstevel@tonic-gate     */
329*0Sstevel@tonic-gate     else
330*0Sstevel@tonic-gate     if (fbp->psave != fbp->save)		/* sane pointers! */
331*0Sstevel@tonic-gate     {
332*0Sstevel@tonic-gate 	/*
333*0Sstevel@tonic-gate 	** Move the data still left in the buffer to the
334*0Sstevel@tonic-gate 	** front, so we can read as much as possible into
335*0Sstevel@tonic-gate 	** buffer after it.
336*0Sstevel@tonic-gate 	*/
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate 	memmove(fbp->save, fbp->psave, fbp->psave_end - fbp->psave);
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	fbp->psave_end = fbp->save + (fbp->psave_end - fbp->psave);
341*0Sstevel@tonic-gate 	fbp->psave = fbp->save;	/* sane	pointers! */
342*0Sstevel@tonic-gate     }
343*0Sstevel@tonic-gate 
344*0Sstevel@tonic-gate     /*
345*0Sstevel@tonic-gate     ** The "fbp->psave" and "fbp->psave_end" pointers must be in a sane
346*0Sstevel@tonic-gate     ** state when we get here, in case the "read()" gets interrupted.
347*0Sstevel@tonic-gate     ** When that happens, we return to the caller who may try
348*0Sstevel@tonic-gate     ** to restart us! Sane: fbp->psave == fbp->save (HERE!)
349*0Sstevel@tonic-gate     */
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate     nbytes = MSGMAX - (fbp->psave_end - fbp->save);
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate     while ((n = read(fifo, fbp->psave_end, nbytes)) == 0 && count < 60)
354*0Sstevel@tonic-gate     {
355*0Sstevel@tonic-gate 	(void)	sleep ((unsigned) 1);
356*0Sstevel@tonic-gate 	count++;
357*0Sstevel@tonic-gate     }
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate     if (n > 0)
360*0Sstevel@tonic-gate 	fbp->psave_end += n;
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate     Had_Full_Buffer = fbp->full;
363*0Sstevel@tonic-gate     fbp->full = (nbytes == n);
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate     return (n);
366*0Sstevel@tonic-gate }
367