read_fifo.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1997 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.11 */
/* LINTLIBRARY */
# include <errno.h>
# include <string.h>
#include <syslog.h>
# include "lp.h"
# include "msgs.h"
extern char Resync[];
extern char Endsync[];
static int Had_Full_Buffer = 1;
int Garbage_Bytes = 0;
int Garbage_Messages= 0;
/*
** A real message is written in one piece, and the write
** is atomic. Thus, even if the O_NDELAY flag is set,
** if we read part of the real message, we can continue
** to read the rest of it in as many steps as we want
** (up to the size of the message, of course!) without
** UNIX returning 0 because no data is available.
** So, a real message doesn't have to be read in one piece,
** which is good since we don't know how much to read!
**
** Fake messages, or improperly written messages, don't
** have this nice property.
**
** INTERRUPTED READS:
**
** If a signal occurs during an attempted read, we can exit.
** The caller can retry the read and we will correctly restart
** it. The correctness of this assertion can be seen by noticing
** that at the beginning of each READ below, we can go back
** to the first statement executed (the first READ below)
** and correctly reexecute the code.
**
** If the last writer closed the fifo, we'll read 0 bytes
** (at least on the subsequent read). If we were in the
** middle of reading a message, we were reading a bogus
** message (but see below).
**
** If we read less than we expect, it's because we were
** reading a fake message (but see below).
**
** HOWEVER: In the last two cases, we may have ONE OR MORE
** REAL MESSAGES snuggled in amongst the trash!
**
** All this verbal rambling is preface to let you understand why we
** buffer the data (which is a shame, but necessary).
*/
/*
** As long as we get real messages, we can avoid needless function calls.
** The SYNC argument in this macro should be set if the resynch. bytes
** have been read--i.e. if the rest of the message is trying to be read.
** In this case, if we had not read a full buffer last time, then we
** must be in the middle of a bogus message.
*/
#define UNSYNCHED_READ(N) \
if (fbp->psave_end - fbp->psave < N || fbp->psave >= fbp->psave_end) \
{ \
switch (_buffer(fifo)) \
{ \
case -1: \
return (-1); \
case 0: \
if (fbp->psave_end > fbp->psave) \
goto SyncUp; \
return (0); \
} \
}
#define SYNCHED_READ(N) \
if (fbp->psave_end - fbp->psave < N || fbp->psave >= fbp->psave_end) \
{ \
switch (_buffer(fifo)) \
{ \
case -1: \
return (-1); \
case 0: \
if (fbp->psave_end > fbp->psave) \
goto SyncUp; \
return (0); \
} \
if (!Had_Full_Buffer) \
goto SyncUp; \
}
/*
** read_fifo() - READ A BUFFER WITH HEADER AND CHECKSUM
*/
int
read_fifo (fifo, buf, size)
int fifo;
char *buf;
unsigned int size;
{
register fifobuffer_t *fbp;
register unsigned int real_chksum,
chksum,
real_size;
/*
** Make sure we start on a message boundary. The first
** line of defense is to look for the resync. bytes.
**
** The "SyncUp" label is global to this routine (below this point)
** and is called whenever we determine that we're out
** of sync. with the incoming bytes.
*/
if (!(fbp=GetFifoBuffer (fifo)))
return -1;
UNSYNCHED_READ (HEAD_RESYNC_LEN);
while (*fbp->psave != Resync[0] || *(fbp->psave + 1) != Resync[1])
{
SyncUp:
#if defined(TRACE_MESSAGES)
if (trace_messages)
syslog(LOG_DEBUG, "DISCARD %c\n", *fbp->psave);
#endif
fbp->psave++;
Garbage_Bytes++;
UNSYNCHED_READ (HEAD_RESYNC_LEN);
}
/*
** We're sync'd, so read the full header.
*/
SYNCHED_READ (HEAD_LEN);
/*
** If the header size is smaller than the minimum size for a header,
** or larger than allowed, we must assume that we really aren't
** synchronized.
*/
real_size = stoh(fbp->psave + HEAD_SIZE);
if (real_size < CONTROL_LEN || MSGMAX < real_size)
{
#if defined(TRACE_MESSAGES)
if (trace_messages)
syslog(LOG_DEBUG, "BAD SIZE\n");
#endif
goto SyncUp;
}
/*
** We have the header. Now we can finally read the rest of the
** message...
*/
SYNCHED_READ (real_size);
/*
** ...but did we read a real message?...
*/
if
(
*(fbp->psave + TAIL_ENDSYNC(real_size)) != Endsync[0]
|| *(fbp->psave + TAIL_ENDSYNC(real_size) + 1) != Endsync[1]
)
{
#if defined(TRACE_MESSAGES)
if (trace_messages)
syslog(LOG_DEBUG, "BAD ENDSYNC\n");
#endif
Garbage_Messages++;
goto SyncUp;
}
chksum = stoh(fbp->psave + TAIL_CHKSUM(real_size));
CALC_CHKSUM (fbp->psave, real_size, real_chksum);
if (real_chksum != chksum)
{
#if defined(TRACE_MESSAGES)
if (trace_messages)
syslog(LOG_DEBUG, "BAD CHKSUM\n");
#endif
Garbage_Messages++;
goto SyncUp;
}
/*
** ...yes!...but can the caller handle the message?
*/
if (size < real_size)
{
errno = E2BIG;
return (-1);
}
/*
** Yes!! We can finally copy the message into the caller's buffer
** and remove it from our buffer. That wasn't so bad, was it?
*/
#if defined(TRACE_MESSAGES)
if (trace_messages)
syslog(LOG_DEBUG, "MESSAGE: %-.*s", real_size, fbp->psave);
#endif
(void)memcpy (buf, fbp->psave, real_size);
fbp->psave += real_size;
return (real_size);
}
int
peek3_2 (fifo)
int fifo;
{
register fifobuffer_t *fbp;
register unsigned int real_size;
/*
** Make sure we start on a message boundary. The first
** line of defense is to look for the resync. bytes.
**
** The "SyncUp" label is global to this routine (below this point)
** and is called whenever we determine that we're out
** of sync. with the incoming bytes.
*/
if (!(fbp=GetFifoBuffer (fifo)))
return -1;
UNSYNCHED_READ (HEAD_RESYNC_LEN);
while (*fbp->psave != Resync[0] || *(fbp->psave + 1) != Resync[1])
{
SyncUp:
fbp->psave++;
Garbage_Bytes++;
UNSYNCHED_READ (HEAD_RESYNC_LEN);
}
/*
** We're sync'd, so read the full header.
*/
SYNCHED_READ (HEAD_LEN);
/*
** If the header size is smaller than the minimum size for a header,
** or larger than allowed, we must assume that we really aren't
** synchronized.
*/
real_size = stoh(fbp->psave + HEAD_SIZE);
if (real_size < CONTROL_LEN || MSGMAX < real_size)
{
goto SyncUp;
}
return(real_size);
}
static int
_buffer (fifo)
int fifo;
{
int n, nbytes, count = 0;
register fifobuffer_t *fbp;
/*
** As long as we get real messages, and if we chose
** SAVE_SIZE well, we shouldn't have to move the data
** in the "else" branch below: Each time we call "read"
** we aren't likely to get as many bytes as we ask for,
** just as many as are in the fifo, AND THIS SHOULD
** REPRESENT AN INTEGRAL NUMBER OF MESSAGES. Since
** the "read_fifo" routine reads complete messages,
** it will end its read at the end of the message,
** which (eventually) will make "psave_end" == "psave".
*/
/*
** If the buffer is empty, there's nothing to move.
*/
if (!(fbp = GetFifoBuffer (fifo)))
return -1;
if (fbp->psave_end == fbp->psave)
fbp->psave = fbp->psave_end = fbp->save; /* sane pointers! */
/*
** If the buffer has data at the high end, move it down.
*/
else
if (fbp->psave != fbp->save) /* sane pointers! */
{
/*
** Move the data still left in the buffer to the
** front, so we can read as much as possible into
** buffer after it.
*/
memmove(fbp->save, fbp->psave, fbp->psave_end - fbp->psave);
fbp->psave_end = fbp->save + (fbp->psave_end - fbp->psave);
fbp->psave = fbp->save; /* sane pointers! */
}
/*
** The "fbp->psave" and "fbp->psave_end" pointers must be in a sane
** state when we get here, in case the "read()" gets interrupted.
** When that happens, we return to the caller who may try
** to restart us! Sane: fbp->psave == fbp->save (HERE!)
*/
nbytes = MSGMAX - (fbp->psave_end - fbp->save);
while ((n = read(fifo, fbp->psave_end, nbytes)) == 0 && count < 60)
{
(void) sleep ((unsigned) 1);
count++;
}
if (n > 0)
fbp->psave_end += n;
Had_Full_Buffer = fbp->full;
fbp->full = (nbytes == n);
return (n);
}