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