static char rcsid[] = "$Header: log.c,v 820.1 86/12/04 19:55:56 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * New message logging pseudo-device.  Accepts
 * characters to be logged, packages them into
 * mbufs and allows them to be read by a user
 * program via the conventional character device
 * interface.
 *
 * The scheme is to create a chain of mbufs,
 * allocating new links dynamically.  The head
 * of the chain points to where the next data
 * to read can be found; the tail points to where
 * the next data is to be written.
 *
 * jam 840227-28-0426
 */

#include "log.h"
#if NLOG > 0
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/file.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/mbuf.h"
#include "../h/uio.h"
#include "../h/errno.h"

#define LOG_MAXMBUFS	40		/* Maximum number of mbufs to use */
#define LOG_MAXDATA	MLEN		/* Maximum data in any mbuf */
#define LOG_PRIREAD	(PZERO + 1)	/* Sleep at interruptible priority */

char	log_isopen;	/* Set if the logging device is open */
char	log_waiting;	/* Some process is waiting on log_head */
int	log_mbufcount;	/* Number of mbufs currently allocated */
struct mbuf *log_head;	/* Head of mbuf chain.  Reads done here */
struct mbuf *log_tail;	/* Tail of mbuf chain.  Writes done here */
char	*log_ptr;	/* Pointer to next free character in the buffer */
int	log_left;	/* Number of bytes left in the log_tail mbuf */
int	log_type;	/* Types of data being logged */
struct proc *log_rsel;	/* Process waiting on select for reading */
int	log_rcoll;	/* Collision on read select */

int	log_maxmbufs = LOG_MAXMBUFS;

struct mbuf *log_alloc();

/*
 * Copy an array of characters into the
 * logging mbuf.  If the mbuf fills up then
 * add a new one to the chain.
 */
log_data(data, length, type)
   register char *data;
   register int length;
   int type;
{
	/*
	 * Don't do any logging if no one is going to
	 * read it.
	 */
	if (!log_isopen)
		return;

	/*
	 * Make sure that we are logging this type
	 * of data.
	 */
	if ((type & log_type) == 0)
		return;

	/*
	 * If there are no mbufs currently allocated
	 * get the first one here.
	 */
	if (log_head == NULL)
		log_head = log_tail = log_alloc();

	/*
	 * Copy all of the data into the logging mbufs
	 * allocating new ones as old ones become full.
	 */
	while (length--)
	{
		*log_ptr++ = *data++;
		++log_tail->m_len;
		if (--log_left == 0)
		{
			log_tail->m_next = log_alloc();
			log_tail = log_tail->m_next;
		}
	}

	/*
	 * Wake up anyone who was waiting for some
	 * data to be logged.
	 */
	logwakeup();
}
/*
 * Allocate an mbuf for the purpose of logging.
 * Initialize log_ptr and log_left.
 */
struct mbuf *
log_alloc()
{
	register struct mbuf *m;

	/*
	 * If we have used our quota of mbufs then
	 * recycle the mbuf immediately after the head
	 * of the mbuf chain and add a string to the
	 * next one indicating that data was lost.
	 * There must be at least 3 mbufs in the chain
	 * for this scheme to work.
	 */
	if (log_mbufcount >= log_maxmbufs)
	{
recycle:
		m = log_head->m_next;
		log_head->m_next = m->m_next;
		bcopy("**LOG LOST 128**", mtod(m->m_next, char *), 16);
	}
	else
	{
		MGET(m, M_DONTWAIT, MT_LOG);
		if (m == NULL)
		{
			if (log_mbufcount >= 3)	/* Last attempt to not panic */
				goto recycle;
			log_isopen = 0;		/* So we don't loop */
			panic("log_alloc");
		}
		++log_mbufcount;
	}
	m->m_next = NULL;
	m->m_off = MMINOFF;
	m->m_len = 0;
	log_ptr = mtod(m, char *);
	log_left = LOG_MAXDATA;
	return(m);
}
/*
 * Open the logging device.  This will both
 * enable logging and allow access to the log
 * from a user program.
 */
logopen(dev, flag)
   dev_t dev;
{
	if (log_isopen)
		return(EBUSY);
	log_isopen = 1;
	log_type = minor(dev);
	if (log_type == 0)
		log_type = ~0;
	return(0);		/* Successfull return code */
}
/*
 * On last close of the logging device mark the
 * device as inactive and free all of the mbufs
 * on the input queue.
 */
logclose(dev, flag)
   dev_t dev;
{
	log_isopen = 0;
	m_freem(log_head);
	log_head = NULL;
	log_mbufcount = 0;
}
/*
 * Read from the log device.  If there is any
 * data on the logging queue then pass as much
 * as possible to the user.
 */
logread(dev, uio)
   dev_t dev;
   register struct uio *uio;
{
	register struct mbuf *m;
	register int error = 0;
	register x;

	x = spl7();
	while (log_head == NULL || log_head->m_len == 0)
	{
		log_waiting = 1;
		/* BUG there is probably a race since LOG_PRIREAD > PZERO */
		sleep((caddr_t)&log_head, LOG_PRIREAD);
	}
	splx(x);
	while (uio->uio_resid && (m = log_head) && m->m_len)
	{
		register int length = MIN(uio->uio_resid, m->m_len);

		error = uiomove(mtod(m, char *), length, UIO_READ, uio);
		if (error)
			break;
		m->m_off += length;
		x = spl7();
		m->m_len -= length;
		if (m->m_len == 0 && m != log_tail)
		{
			log_head = m_free(m);
			--log_mbufcount;
		}
		splx(x);
	}
	return(error);
}
/*
 * Return 1 if there are characters to be read
 * from the log device, else return 0.
 */
logselect(dev, rw)
	dev_t dev;
	int rw;
{
	int s = spl7();

	if (rw == FREAD)
	{
		if (log_head && log_head->m_len)
		{
			splx(s);
			return(1);
		}
		if (log_rsel && log_rsel->p_wchan == (caddr_t)&selwait)
			log_rcoll = 1;
		else
			log_rsel = u.u_procp;
	}
	splx(s);
	return (0);
}
/*
 * Wakeup anyone waiting on log input either
 * from a read or from a select.
 */
logwakeup()
{
	/*
	 * If anyone is waiting on a select then
	 * wake him up.
	 */
	if (log_rsel)
	{
		selwakeup(log_rsel, log_rcoll);
		log_rcoll = 0;
		log_rsel = 0;
	}

	/*
	 * If anyone is waiting on a read wake
	 * him up.
	 */
	if (log_waiting)
	{
		log_waiting = 0;
		wakeup((caddr_t)&log_head);
	}
}
#endif NLOG > 0
