#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc_internal.h>
#include <sys/vnode.h>
#include <sys/ioctl.h>
#include <sys/msgbuf.h>
#include <sys/file_internal.h>
#include <sys/errno.h>
#include <sys/select.h>
#include <sys/kernel.h>
#include <kern/thread.h>
#include <sys/lock.h>
#include <sys/signalvar.h>
#include <sys/conf.h>
#include <sys/sysctl.h>
#include <kern/kalloc.h>
extern void klogwakeup(void);
extern void logwakeup(void);
#define LOG_RDPRI (PZERO + 1)
#define LOG_NBIO 0x02
#define LOG_ASYNC 0x04
#define LOG_RDWAIT 0x08
struct logsoftc {
int sc_state;
struct selinfo sc_selp;
int sc_pgid;
} logsoftc;
int log_open;
char smsg_bufc[MSG_BSIZE];
struct msgbuf temp_msgbuf = {0,MSG_BSIZE,0,0,smsg_bufc};
struct msgbuf *msgbufp;
static int _logentrypend = 0;
static int log_inited = 0;
extern void bsd_log_lock(void);
extern void bsd_log_unlock(void);
extern void bsd_log_init(void);
extern d_open_t logopen;
extern d_close_t logclose;
extern d_read_t logread;
extern d_ioctl_t logioctl;
extern d_select_t logselect;
#define LOG_LOCK() bsd_log_lock()
#define LOG_UNLOCK() bsd_log_unlock()
int
logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
{
LOG_LOCK();
if (log_open) {
LOG_UNLOCK();
return (EBUSY);
}
log_open = 1;
logsoftc.sc_pgid = p->p_pid;
if (msgbufp->msg_magic != MSG_MAGIC) {
register int i;
msgbufp->msg_magic = MSG_MAGIC;
msgbufp->msg_bufx = msgbufp->msg_bufr = 0;
for (i=0; i < MSG_BSIZE; i++)
msgbufp->msg_bufc[i] = 0;
}
LOG_UNLOCK();
return (0);
}
int
logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
{
LOG_LOCK();
log_open = 0;
selwakeup(&logsoftc.sc_selp);
selthreadclear(&logsoftc.sc_selp);
LOG_UNLOCK();
return (0);
}
int
logread(__unused dev_t dev, struct uio *uio, int flag)
{
register long l;
int error = 0;
LOG_LOCK();
while (msgbufp->msg_bufr == msgbufp->msg_bufx) {
if (flag & IO_NDELAY) {
error = EWOULDBLOCK;
goto out;
}
if (logsoftc.sc_state & LOG_NBIO) {
error = EWOULDBLOCK;
goto out;
}
logsoftc.sc_state |= LOG_RDWAIT;
LOG_UNLOCK();
if ((error = tsleep((caddr_t)msgbufp, LOG_RDPRI | PCATCH,
"klog", 5 * hz)) != 0) {
if (error != EWOULDBLOCK)
return (error);
}
LOG_LOCK();
}
logsoftc.sc_state &= ~LOG_RDWAIT;
while (uio_resid(uio) > 0) {
l = msgbufp->msg_bufx - msgbufp->msg_bufr;
if (l < 0)
l = msgbufp->msg_size - msgbufp->msg_bufr;
l = min(l, uio_resid(uio));
if (l == 0)
break;
LOG_UNLOCK();
error = uiomove((caddr_t)&msgbufp->msg_bufc[msgbufp->msg_bufr],
(int)l, uio);
LOG_LOCK();
if (error)
break;
msgbufp->msg_bufr += l;
if (msgbufp->msg_bufr < 0 || msgbufp->msg_bufr >= msgbufp->msg_size)
msgbufp->msg_bufr = 0;
}
out:
LOG_UNLOCK();
return (error);
}
int
logselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
{
switch (rw) {
case FREAD:
LOG_LOCK();
if (msgbufp->msg_bufr != msgbufp->msg_bufx) {
LOG_UNLOCK();
return (1);
}
selrecord(p, &logsoftc.sc_selp, wql);
LOG_UNLOCK();
break;
}
return (0);
}
void
logwakeup(void)
{
int pgid;
LOG_LOCK();
if (!log_open) {
LOG_UNLOCK();
return;
}
selwakeup(&logsoftc.sc_selp);
if (logsoftc.sc_state & LOG_ASYNC) {
pgid = logsoftc.sc_pgid;
LOG_UNLOCK();
if (pgid < 0)
gsignal(-pgid, SIGIO);
else
proc_signal(pgid, SIGIO);
LOG_LOCK();
}
if (logsoftc.sc_state & LOG_RDWAIT) {
wakeup((caddr_t)msgbufp);
logsoftc.sc_state &= ~LOG_RDWAIT;
}
LOG_UNLOCK();
}
void
klogwakeup(void)
{
if (_logentrypend) {
_logentrypend = 0;
logwakeup();
}
}
int
logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
{
long l;
LOG_LOCK();
switch (com) {
case FIONREAD:
l = msgbufp->msg_bufx - msgbufp->msg_bufr;
if (l < 0)
l += msgbufp->msg_size;
*(off_t *)data = l;
break;
case FIONBIO:
if (*(int *)data)
logsoftc.sc_state |= LOG_NBIO;
else
logsoftc.sc_state &= ~LOG_NBIO;
break;
case FIOASYNC:
if (*(int *)data)
logsoftc.sc_state |= LOG_ASYNC;
else
logsoftc.sc_state &= ~LOG_ASYNC;
break;
case TIOCSPGRP:
logsoftc.sc_pgid = *(int *)data;
break;
case TIOCGPGRP:
*(int *)data = logsoftc.sc_pgid;
break;
default:
LOG_UNLOCK();
return (-1);
}
LOG_UNLOCK();
return (0);
}
void
bsd_log_init(void)
{
if (!log_inited) {
msgbufp = &temp_msgbuf;
log_inited = 1;
}
}
void
log_putc_locked(char c)
{
register struct msgbuf *mbp;
if (!log_inited) {
panic("bsd log is not inited");
}
mbp = msgbufp;
if (mbp-> msg_magic != MSG_MAGIC) {
register int i;
mbp->msg_magic = MSG_MAGIC;
mbp->msg_bufx = mbp->msg_bufr = 0;
for (i=0; i < MSG_BSIZE; i++)
mbp->msg_bufc[i] = 0;
}
mbp->msg_bufc[mbp->msg_bufx++] = c;
_logentrypend = 1;
if (mbp->msg_bufx < 0 || mbp->msg_bufx >= msgbufp->msg_size)
mbp->msg_bufx = 0;
}
void
log_putc(char c)
{
if (!log_inited) {
panic("bsd log is not inited");
}
LOG_LOCK();
log_putc_locked(c);
LOG_UNLOCK();
}
void
log_setsize(long size) {
char *new_logdata;
if (msgbufp->msg_size!=MSG_BSIZE) {
printf("log_setsize: attempt to change size more than once\n");
return;
}
if (size==MSG_BSIZE)
return;
if (size<MSG_BSIZE) {
printf("log_setsize: can't decrease log size\n");
return;
}
if (!(new_logdata = (char*)kalloc(size))) {
printf("log_setsize: unable to allocate memory\n");
return;
}
LOG_LOCK();
bcopy(smsg_bufc, new_logdata, MSG_BSIZE);
bzero(new_logdata+MSG_BSIZE, size - MSG_BSIZE);
bzero(smsg_bufc, MSG_BSIZE);
msgbufp->msg_size = size;
msgbufp->msg_bufc = new_logdata;
LOG_UNLOCK();
printf("set system log size to %ld bytes\n", msgbufp->msg_size);
}
SYSCTL_LONG(_kern, OID_AUTO, msgbuf, CTLFLAG_RD, &temp_msgbuf.msg_size, "");
int
log_dmesg(user_addr_t buffer, uint32_t buffersize, register_t * retval) {
unsigned long i;
int error = 0, newl, skip;
char *localbuff, *p, *copystart, ch;
long localbuff_size = msgbufp->msg_size+2, copysize;
if (!(localbuff = (char *)kalloc(localbuff_size))) {
printf("log_dmesg: unable to allocate memory\n");
return (ENOMEM);
}
LOG_LOCK();
p = msgbufp->msg_bufc + msgbufp->msg_bufx;
for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
if (p >= msgbufp->msg_bufc + msgbufp->msg_size)
p = msgbufp->msg_bufc;
ch = *p;
if (skip) {
if (ch == '>')
newl = skip = 0;
continue;
}
if (newl && ch == '<') {
skip = 1;
continue;
}
if (ch == '\0')
continue;
newl = ch == '\n';
localbuff[i++] = ch;
if (i == (localbuff_size - 2))
break;
}
if (!newl)
localbuff[i++] = '\n';
localbuff[i++] = 0;
if (buffersize >= i) {
copystart = localbuff;
copysize = i;
} else {
copystart = localbuff + i - buffersize;
copysize = buffersize;
}
LOG_UNLOCK();
error = copyout(copystart, buffer, copysize);
if (!error)
*retval = copysize;
kfree(localbuff, localbuff_size);
return (error);
}