X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/91447636331957f3d9b5ca5b508f07c526b0074d..3e170ce000f1506b7b5d2c5c7faec85ceabb573d:/bsd/kern/subr_log.c?ds=sidebyside

diff --git a/bsd/kern/subr_log.c b/bsd/kern/subr_log.c
index 73dc5cc51..0f8c00b36 100644
--- a/bsd/kern/subr_log.c
+++ b/bsd/kern/subr_log.c
@@ -1,23 +1,29 @@
 /*
- * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2010 Apple, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
 /*
@@ -71,6 +77,14 @@
 #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>
+
+/* XXX should be in a common header somewhere */
+extern void logwakeup(void);
 
 #define LOG_RDPRI	(PZERO + 1)
 
@@ -78,6 +92,10 @@
 #define LOG_ASYNC	0x04
 #define LOG_RDWAIT	0x08
 
+#define MAX_UNREAD_CHARS (CONFIG_MSG_BSIZE/2)
+/* All globals should be accessed under LOG_LOCK() */
+
+/* logsoftc only valid while log_open=1 */
 struct logsoftc {
 	int	sc_state;		/* see above for possibilities */
 	struct	selinfo sc_selp;	/* thread waiting for select */
@@ -85,15 +103,22 @@ struct logsoftc {
 } logsoftc;
 
 int	log_open;			/* also used in log() */
-struct msgbuf temp_msgbuf;
-struct msgbuf *msgbufp;
-static int _logentrypend = 0;
-static int log_inited = 0;
-void bsd_log_lock(void);
-/* the following two are implemented in osfmk/kern/printf.c  */
+char smsg_bufc[CONFIG_MSG_BSIZE]; /* static buffer */
+struct msgbuf msgbuf = {MSG_MAGIC,sizeof(smsg_bufc),0,0,smsg_bufc};
+struct msgbuf *msgbufp __attribute__((used)) = &msgbuf;
+
+/* the following are implemented in osfmk/kern/printf.c  */
+extern void bsd_log_lock(void);
 extern void bsd_log_unlock(void);
 extern void bsd_log_init(void);
 
+/* XXX wants a linker set so these can be static */
+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;
+
 /*
  * Serialize log access.  Note that the log can be written at interrupt level,
  * so any log manipulations that can be done from, or affect, another processor
@@ -103,33 +128,29 @@ extern void bsd_log_init(void);
 #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);
 
 /*ARGSUSED*/
-logopen(dev, flags, mode, p)
-	dev_t dev;
-	int flags, mode;
-	struct proc *p;
+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;		/* signal process only */
-	/*
-	 * Potential race here with putchar() but since putchar should be
-	 * called by autoconf, msg_magic should be initialized by the time
-	 * we get here.
-	 */
-	if (msgbufp->msg_magic != MSG_MAGIC) {
-		register int i;
+	log_open = 1;
 
-		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);
@@ -137,30 +158,22 @@ logopen(dev, flags, mode, p)
 
 /*ARGSUSED*/
 int
-logclose(dev, flag)
-	dev_t dev;
+logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
 {
-	int oldpri;
 	LOG_LOCK();
-	log_open = 0;
 	selwakeup(&logsoftc.sc_selp);
 	selthreadclear(&logsoftc.sc_selp);
+	log_open = 0;
 	LOG_UNLOCK();
 	return (0);
 }
 
 /*ARGSUSED*/
 int
-logread(dev, uio, flag)
-	dev_t dev;
-	struct uio *uio;
-	int flag;
+logread(__unused dev_t dev, struct uio *uio, int flag)
 {
-	register long l;
-	register int s;
+	int l;
 	int error = 0;
-	char localbuff[MSG_BSIZE];
-	int copybytes;
 
 	LOG_LOCK();
 	while (msgbufp->msg_bufr == msgbufp->msg_bufx) {
@@ -175,12 +188,11 @@ logread(dev, uio, flag)
 		logsoftc.sc_state |= LOG_RDWAIT;
 		LOG_UNLOCK();
 		/*
-		 * If the wakeup is missed the ligtening bolt will wake this up 
-		 * if there are any new characters. If that doesn't do it
+		 * If the wakeup is missed 
 		 * then wait for 5 sec and reevaluate 
 		 */
-		if (error = tsleep((caddr_t)msgbufp, LOG_RDPRI | PCATCH,
-				"klog", 5 * hz)) {
+		if ((error = tsleep((caddr_t)msgbufp, LOG_RDPRI | PCATCH,
+				"klog", 5 * hz)) != 0) {
 			/* if it times out; ignore */
 			if (error != EWOULDBLOCK)
 				return (error);
@@ -189,23 +201,25 @@ logread(dev, uio, flag)
 	}
 	logsoftc.sc_state &= ~LOG_RDWAIT;
 
-
 	while (uio_resid(uio) > 0) {
+		int readpos;
+
 		l = msgbufp->msg_bufx - msgbufp->msg_bufr;
 		if (l < 0)
-			l = MSG_BSIZE - msgbufp->msg_bufr;
+			l = msgbufp->msg_size - msgbufp->msg_bufr;
 		l = min(l, uio_resid(uio));
 		if (l == 0)
 			break;
-		bcopy(&msgbufp->msg_bufc[msgbufp->msg_bufr], &localbuff[0], l);
+
+		readpos = msgbufp->msg_bufr;
 		LOG_UNLOCK();
-		error = uiomove((caddr_t)&localbuff[0],
-			(int)l, uio);
+		error = uiomove((caddr_t)&msgbufp->msg_bufc[readpos],
+			l, uio);
 		LOG_LOCK();
 		if (error)
 			break;
-		msgbufp->msg_bufr += l;
-		if (msgbufp->msg_bufr < 0 || msgbufp->msg_bufr >= MSG_BSIZE)
+		msgbufp->msg_bufr = readpos + l;
+		if (msgbufp->msg_bufr >= msgbufp->msg_size)
 			msgbufp->msg_bufr = 0;
 	}
 out:
@@ -215,13 +229,8 @@ out:
 
 /*ARGSUSED*/
 int
-logselect(dev, rw, wql, p)
-	dev_t dev;
-	int rw;
-	void * wql;
-	struct proc *p;
+logselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
 {
-
 	switch (rw) {
 
 	case FREAD:
@@ -238,11 +247,9 @@ logselect(dev, rw, wql, p)
 }
 
 void
-logwakeup()
+logwakeup(void)
 {
-	struct proc *p;
 	int pgid;
-	boolean_t funnel_state;
 
 	LOG_LOCK();	
 	if (!log_open) {
@@ -255,8 +262,8 @@ logwakeup()
 		LOG_UNLOCK();
 		if (pgid < 0)
 			gsignal(-pgid, SIGIO); 
-		else if (p = pfind(pgid))
-			psignal(p, SIGIO);
+		else 
+			proc_signal(pgid, SIGIO);
 		LOG_LOCK();
 	}
 	if (logsoftc.sc_state & LOG_RDWAIT) {
@@ -266,32 +273,21 @@ logwakeup()
 	LOG_UNLOCK();
 }
 
-void
-klogwakeup()
-{
-
-	if (_logentrypend) {
-		_logentrypend = 0;
-		logwakeup();
-	}
-}
 
 /*ARGSUSED*/
 int
-logioctl(dev, com, data, flag)
-	caddr_t data;
+logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
 {
-	long l;
-	int s;
+	int l;
 
-	LOG_LOCK();	
+	LOG_LOCK();
 	switch (com) {
 
 	/* return number of characters immediately available */
 	case FIONREAD:
 		l = msgbufp->msg_bufx - msgbufp->msg_bufr;
 		if (l < 0)
-			l += MSG_BSIZE;
+			l += msgbufp->msg_size;
 		*(off_t *)data = l;
 		break;
 
@@ -326,37 +322,263 @@ logioctl(dev, com, data, flag)
 }
 
 void
-bsd_log_init()
+bsd_log_init(void)
 {
-	if (!log_inited) { 
-		msgbufp = &temp_msgbuf;
-		log_inited = 1;
-	}
+	/* After this point, we must be ready to accept characters */
 }
 
+
+/*
+ * log_putc_locked
+ *
+ * Decription:	Output a character to the log; assumes the LOG_LOCK() is held
+ *		by the caller.
+ *
+ * Parameters:	c				Character to output
+ *
+ * Returns:	(void)
+ *
+ * Notes:	This functions is used for multibyte output to the log; it
+ *		should be used preferrentially where possible to ensure that
+ *		log entries do not end up interspersed due to preemption or
+ *		SMP reentrancy.
+ */
+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;
+}
+
+
+/*
+ * log_putc
+ *
+ * Decription:	Output a character to the log; assumes the LOG_LOCK() is NOT
+ *		held by the caller.
+ *
+ * Parameters:	c				Character to output
+ *
+ * Returns:	(void)
+ *
+ * Notes:	This function is used for syingle byte output to the log.  It
+ *		primarily exists to maintain binary backward compatibility.
+ */
 void
 log_putc(char c)
 {
-	register struct msgbuf *mbp;
+	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();
+}
 
-	if (!log_inited) {
-		panic("bsd log is not inited");
+
+/*
+ * it is possible to increase the kernel log buffer size by adding
+ *   msgbuf=n
+ * to the kernel command line, and to read the current size using
+ *   sysctl kern.msgbuf
+ * If there is no parameter on the kernel command line, the buffer is
+ * allocated statically and is CONFIG_MSG_BSIZE characters in size, otherwise
+ * memory is dynamically allocated. Memory management must already be up.
+ */
+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();
 
-	mbp = msgbufp; 
-	if (mbp-> msg_magic != MSG_MAGIC) { 
-		register int i;
+	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);
 
-		mbp->msg_magic = MSG_MAGIC;
-		mbp->msg_bufx = mbp->msg_bufr = 0;
-		for (i=0; i < MSG_BSIZE; i++)
-			mbp->msg_bufc[i] = 0;
+	/* start "new_logsize" bytes before the write pointer */
+	if (new_logsize <= old_bufx) {
+		count = new_logsize;
+		p = old_logdata + old_bufx - count;
+	} else {
+		/*
+		 * if new buffer is bigger, copy what we have and let the
+		 * bzero above handle the difference
+		 */
+		count = MIN(new_logsize, old_logsize);
+		p = old_logdata + old_logsize - (count - old_bufx);
 	}
-	mbp->msg_bufc[mbp->msg_bufx++] = c;
-	_logentrypend = 1;
-	if (mbp->msg_bufx < 0 || mbp->msg_bufx >= MSG_BSIZE)
-		mbp->msg_bufx = 0;
+	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; /* how much were we trailing bufx by? */
+	if (new_bufr < 0)
+		new_bufr += old_logsize;
+	new_bufr = new_bufx - new_bufr; /* now relative to oldest data in new buffer */
+	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();
+
+	/* this memory is now dead - clear it so that it compresses better
+	   in case of suspend to disk etc. */
+	bzero(old_logdata, old_logsize);
+	if (old_logdata != smsg_bufc) {
+		/* dynamic memory that must be freed */
+		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);
+}
+
+
+/*
+ * This should be called by /sbin/dmesg only via libproc.
+ * It returns as much data still in the buffer as possible.
+ */
+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); /* + '\n' + '\0' */
+	LOG_UNLOCK();
+
+	/* Allocate a temporary non-circular buffer for copyout */
+	if (!(localbuff = (char *)kalloc(localbuff_size))) {
+		printf("log_dmesg: unable to allocate memory\n");
+		return (ENOMEM);
+	}
+
+	/* in between here, the log could become bigger, but that's fine */
+	LOG_LOCK();
+
+	/*
+	 * The message buffer is circular; start at the write pointer, and
+	 * make one loop up to write pointer - 1.
+	 */
+	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;
+		/* Skip "\n<.*>" syslog sequences. */
+		if (skip) {
+			if (ch == '>')
+				newl = skip = 0;
+			continue;
+		}
+		if (newl && ch == '<') {
+			skip = 1;
+			continue;
+		}
+		if (ch == '\0')
+			continue;
+		newl = (ch == '\n');
+		localbuff[i++] = ch;
+		/* The original version of this routine contained a buffer
+		 * overflow. At the time, a "small" targeted fix was desired
+		 * so the change below to check the buffer bounds was made.
+		 * TODO: rewrite this needlessly convoluted routine.
+		 */
+		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);
+}