Files
retrobsd/sys/kernel/subr_log.c
Serge Vakulenko 585773955b Fix include paths in the kernel sources.
Max32 kernel successfully compiled with kconfig utility.
2015-08-31 00:21:41 -07:00

337 lines
8.0 KiB
C

/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
/*
* logioctl() had the wrong number of arguments. Argh! Apparently this
* driver was overlooked when 'dev' was added to ioctl entry points.
*
* logclose() returned garbage. this went unnoticed because most programs
* don't check status when doing a close.
* Add support for multiple log devices. Minor device 0 is the traditional
* kernel logger (/dev/klog), minor device 1 is reserved for the future device
* error logging daemon.
*/
#define NLOG 1
int nlog = 1;
#include <sys/param.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/msgbuf.h>
#include <sys/file.h>
#include <sys/inode.h>
#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/map.h>
#include <sys/systm.h>
#include <sys/conf.h>
const struct devspec logdevs[] = {
{ 0, "klog" },
{ 0, 0 }
};
#define LOG_RDPRI (PZERO + 1)
#define LOG_OPEN 0x01
#define LOG_ASYNC 0x04
#define LOG_RDWAIT 0x08
struct msgbuf msgbuf[NLOG];
static struct logsoftc {
int sc_state; /* see above for possibilities */
struct proc *sc_selp; /* process waiting on select call */
int sc_pgid; /* process/group for async I/O */
int sc_overrun; /* full buffer count */
} logsoftc[NLOG];
/*ARGSUSED*/
int
logopen(dev, mode, unused)
dev_t dev;
int mode;
{
register int unit = minor(dev);
if (unit >= NLOG)
return(ENODEV);
if (logisopen(unit))
return(EBUSY);
if (msgbuf[unit].msg_bufc == 0) /* no buffer allocated */
return(ENOMEM);
logsoftc[unit].sc_state |= LOG_OPEN;
logsoftc[unit].sc_pgid = u.u_procp->p_pid; /* signal process only */
logsoftc[unit].sc_overrun = 0;
return(0);
}
/*ARGSUSED*/
int
logclose(dev, flag, unused)
dev_t dev;
int flag;
{
register int unit = minor(dev);
logsoftc[unit].sc_state = 0;
return(0);
}
/*
* This is a helper function to keep knowledge of this driver's data
* structures away from the rest of the kernel.
*/
int
logisopen(unit)
int unit;
{
if (logsoftc[unit].sc_state & LOG_OPEN)
return(1);
return(0);
}
/*ARGSUSED*/
int
logread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
register int l;
register struct logsoftc *lp;
register struct msgbuf *mp;
int s, error = 0;
char buf [128];
l = minor(dev);
lp = &logsoftc[l];
mp = &msgbuf[l];
s = splhigh();
while (mp->msg_bufr == mp->msg_bufx) {
if (flag & IO_NDELAY) {
splx(s);
return(EWOULDBLOCK);
}
lp->sc_state |= LOG_RDWAIT;
sleep((caddr_t)mp, LOG_RDPRI);
}
lp->sc_state &= ~LOG_RDWAIT;
while (uio->uio_resid) {
l = mp->msg_bufx - mp->msg_bufr;
/*
* If the reader and writer are equal then we have caught up and there
* is nothing more to transfer.
*/
if (l == 0)
break;
/*
* If the write pointer is behind the reader then only consider as
* available for now the bytes from the read pointer thru the end of
* the buffer.
*/
if (l < 0) {
l = MSG_BSIZE - mp->msg_bufr;
/*
* If the reader is exactly at the end of the buffer it is
* time to wrap it around to the beginning and recalculate the
* amount of data to transfer.
*/
if (l == 0) {
mp->msg_bufr = 0;
continue;
}
}
l = MIN (l, uio->uio_resid);
l = MIN (l, sizeof buf);
bcopy (&mp->msg_bufc[mp->msg_bufr], buf, l);
error = uiomove (buf, l, uio);
if (error)
break;
mp->msg_bufr += l;
}
splx(s);
return(error);
}
/*ARGSUSED*/
int
logselect(dev, rw)
dev_t dev;
int rw;
{
register int s = splhigh();
int unit = minor(dev);
switch (rw) {
case FREAD:
if (msgbuf[unit].msg_bufr != msgbuf[unit].msg_bufx) {
splx(s);
return(1);
}
logsoftc[unit].sc_selp = u.u_procp;
break;
}
splx(s);
return(0);
}
void
logwakeup(unit)
int unit;
{
register struct proc *p;
register struct logsoftc *lp;
register struct msgbuf *mp;
if (! logisopen(unit))
return;
lp = &logsoftc[unit];
mp = &msgbuf[unit];
if (lp->sc_selp) {
selwakeup(lp->sc_selp, (long) 0);
lp->sc_selp = 0;
}
if (lp->sc_state & LOG_ASYNC && (mp->msg_bufx != mp->msg_bufr)) {
if (lp->sc_pgid < 0)
gsignal(-lp->sc_pgid, SIGIO);
else if ((p = pfind(lp->sc_pgid)))
psignal(p, SIGIO);
}
if (lp->sc_state & LOG_RDWAIT) {
wakeup((caddr_t)mp);
lp->sc_state &= ~LOG_RDWAIT;
}
}
/*ARGSUSED*/
int
logioctl(dev, com, data, flag)
dev_t dev;
u_int com;
caddr_t data;
int flag;
{
long l;
register int s;
int unit;
register struct logsoftc *lp;
register struct msgbuf *mp;
unit = minor(dev);
lp = &logsoftc[unit];
mp = &msgbuf[unit];
switch (com) {
case FIONREAD:
s = splhigh();
l = mp->msg_bufx - mp->msg_bufr;
splx(s);
if (l < 0)
l += MSG_BSIZE;
*(off_t *)data = l;
break;
case FIONBIO:
break;
case FIOASYNC:
if (*(int *)data)
lp->sc_state |= LOG_ASYNC;
else
lp->sc_state &= ~LOG_ASYNC;
break;
case TIOCSPGRP:
lp->sc_pgid = *(int *)data;
break;
case TIOCGPGRP:
*(int *)data = lp->sc_pgid;
break;
default:
return(-1);
}
return(0);
}
/*
* This is inefficient for single character writes. Alas, changing this
* to be buffered would affect the networking code's use of printf.
*/
int
logwrt (buf, len, log)
char *buf;
int len;
int log;
{
register struct msgbuf *mp = &msgbuf[log];
struct logsoftc *lp = &logsoftc[log];
register int infront;
int s, n, writer, err = 0;
if (mp->msg_magic != MSG_MAGIC || (len > MSG_BSIZE))
return(-1);
/*
* Hate to do this but since this can be called from anywhere in the kernel
* we have to hold off any interrupt service routines so they don't change
* things. This looks like a lot of code but it isn't really.
*/
s = splhigh();
while (len) {
again: infront = MSG_BSIZE - mp->msg_bufx;
if (infront <= 0) {
mp->msg_bufx = 0;
infront = MSG_BSIZE - mp->msg_bufr;
}
n = mp->msg_bufr - mp->msg_bufx;
if (n < 0) /* bufr < bufx */
writer = (MSG_BSIZE - mp->msg_bufx) + mp->msg_bufr;
else if (n == 0)
writer = MSG_BSIZE;
else {
writer = n;
infront = n;
}
if (len > writer) {
/*
* won't fit. the total number of bytes to be written is
* greater than the number available. the buffer is full.
* throw away the old data and keep the current data by resetting
* the 'writer' pointer to the current 'reader' position. Bump the
* overrun counter in case anyone wants to look at it for debugging.
*/
lp->sc_overrun++;
mp->msg_bufx = mp->msg_bufr;
goto again;
}
if (infront > len)
infront = len;
bcopy(buf, &mp->msg_bufc[mp->msg_bufx], infront);
mp->msg_bufx += infront;
len -= infront;
buf += infront;
}
splx(s);
return(err);
}
/*
* Initialize the log driver. Called from the system startup code (machdep2.c).
* All buffers are the same (MSG_BSIZE) size.
*/
int
loginit()
{
register struct msgbuf *mp;
for (mp = &msgbuf[0]; mp < &msgbuf[NLOG]; mp++) {
mp->msg_magic = MSG_MAGIC;
mp->msg_bufx = mp->msg_bufr = 0;
}
return(0);
}