#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>
#include <pexpert/pexpert.h>
extern void logwakeup(void);
#define LOG_RDPRI (PZERO + 1)
#define LOG_NBIO 0x02
#define LOG_ASYNC 0x04
#define LOG_RDWAIT 0x08
#define MAX_UNREAD_CHARS (CONFIG_MSG_BSIZE/2)
struct logsoftc {
int sc_state;
struct selinfo sc_selp;
int sc_pgid;
} logsoftc;
int log_open;
char smsg_bufc[CONFIG_MSG_BSIZE];
struct msgbuf msgbuf = {MSG_MAGIC,sizeof(smsg_bufc),0,0,smsg_bufc};
struct msgbuf *msgbufp __attribute__((used)) = &msgbuf;
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()
#if DEBUG
#define LOG_SETSIZE_DEBUG(x...) kprintf(x)
#else
#define LOG_SETSIZE_DEBUG(x...) do { } while(0)
#endif
static int sysctl_kern_msgbuf(struct sysctl_oid *oidp,
void *arg1,
int arg2,
struct sysctl_req *req);
int
logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
{
LOG_LOCK();
if (log_open) {
LOG_UNLOCK();
return (EBUSY);
}
logsoftc.sc_pgid = p->p_pid;
log_open = 1;
LOG_UNLOCK();
return (0);
}
int
logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
{
LOG_LOCK();
selwakeup(&logsoftc.sc_selp);
selthreadclear(&logsoftc.sc_selp);
log_open = 0;
LOG_UNLOCK();
return (0);
}
int
logread(__unused dev_t dev, struct uio *uio, int flag)
{
int 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) {
int readpos;
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;
readpos = msgbufp->msg_bufr;
LOG_UNLOCK();
error = uiomove((caddr_t)&msgbufp->msg_bufc[readpos],
l, uio);
LOG_LOCK();
if (error)
break;
msgbufp->msg_bufr = readpos + l;
if (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();
}
int
logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
{
int 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)
{
}
void
log_putc_locked(char c)
{
struct msgbuf *mbp;
mbp = msgbufp;
mbp->msg_bufc[mbp->msg_bufx++] = c;
if (mbp->msg_bufx >= msgbufp->msg_size)
mbp->msg_bufx = 0;
}
void
log_putc(char c)
{
int unread_count = 0;
LOG_LOCK();
log_putc_locked(c);
unread_count = msgbufp->msg_bufx - msgbufp->msg_bufr;
LOG_UNLOCK();
if (unread_count < 0)
unread_count = 0 - unread_count;
if (c == '\n' || unread_count >= MAX_UNREAD_CHARS)
logwakeup();
}
int
log_setsize(int size) {
char *new_logdata;
int new_logsize, new_bufr, new_bufx;
char *old_logdata;
int old_logsize, old_bufr, old_bufx;
int i, count;
char *p, ch;
if (size > MAX_MSG_BSIZE)
return (EINVAL);
if (size <= 0)
return (EINVAL);
new_logsize = size;
if (!(new_logdata = (char*)kalloc(size))) {
printf("log_setsize: unable to allocate memory\n");
return (ENOMEM);
}
bzero(new_logdata, new_logsize);
LOG_LOCK();
old_logsize = msgbufp->msg_size;
old_logdata = msgbufp->msg_bufc;
old_bufr = msgbufp->msg_bufr;
old_bufx = msgbufp->msg_bufx;
LOG_SETSIZE_DEBUG("log_setsize(%d): old_logdata %p old_logsize %d old_bufr %d old_bufx %d\n",
size, old_logdata, old_logsize, old_bufr, old_bufx);
if (new_logsize <= old_bufx) {
count = new_logsize;
p = old_logdata + old_bufx - count;
} else {
count = MIN(new_logsize, old_logsize);
p = old_logdata + old_logsize - (count - old_bufx);
}
for (i = 0; i < count; i++) {
if (p >= old_logdata + old_logsize)
p = old_logdata;
ch = *p++;
new_logdata[i] = ch;
}
new_bufx = i;
if (new_bufx >= new_logsize)
new_bufx = 0;
msgbufp->msg_bufx = new_bufx;
new_bufr = old_bufx - old_bufr;
if (new_bufr < 0)
new_bufr += old_logsize;
new_bufr = new_bufx - new_bufr;
if (new_bufr < 0)
new_bufr += new_logsize;
msgbufp->msg_bufr = new_bufr;
msgbufp->msg_size = new_logsize;
msgbufp->msg_bufc = new_logdata;
LOG_SETSIZE_DEBUG("log_setsize(%d): new_logdata %p new_logsize %d new_bufr %d new_bufx %d\n",
size, new_logdata, new_logsize, new_bufr, new_bufx);
LOG_UNLOCK();
bzero(old_logdata, old_logsize);
if (old_logdata != smsg_bufc) {
kfree(old_logdata, old_logsize);
}
printf("set system log size to %d bytes\n", new_logsize);
return 0;
}
SYSCTL_PROC(_kern, OID_AUTO, msgbuf, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, sysctl_kern_msgbuf, "I", "");
static int sysctl_kern_msgbuf(struct sysctl_oid *oidp __unused,
void *arg1 __unused,
int arg2 __unused,
struct sysctl_req *req)
{
int old_bufsize, bufsize;
int error;
LOG_LOCK();
old_bufsize = bufsize = msgbufp->msg_size;
LOG_UNLOCK();
error = sysctl_io_number(req, bufsize, sizeof(bufsize), &bufsize, NULL);
if (error)
return (error);
if (bufsize != old_bufsize) {
error = log_setsize(bufsize);
}
return (error);
}
int
log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t * retval) {
uint32_t i;
uint32_t localbuff_size;
int error = 0, newl, skip;
char *localbuff, *p, *copystart, ch;
size_t copysize;
LOG_LOCK();
localbuff_size = (msgbufp->msg_size + 2);
LOG_UNLOCK();
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);
}