/* * 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 # include #include # 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); }