+
+
+
+/*
+ * 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 single byte output to the log. It
+ * primarily exists to maintain binary backward compatibility.
+ */
+void
+log_putc(char c)
+{
+ int unread_count = 0;
+ LOG_LOCK();
+ log_putc_locked(msgbufp, 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 >= (msgbufp->msg_size / 2)) {
+ logwakeup(msgbufp);
+ }
+}
+
+
+/*
+ * 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();
+
+ 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);
+
+ /* 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);
+ }
+ 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;
+}
+
+void
+oslog_setsize(int size)
+{
+ uint16_t scale = 0;
+ // If the size is less than the default stream buffer
+ // do nothing
+ if (size <= OSLOG_STREAM_BUF_SIZE) {
+ return;
+ }
+
+ scale = (uint16_t) (size / OSLOG_STREAM_BUF_SIZE);
+
+ oslog_stream_buf_size = size;
+ oslog_stream_num_entries = scale * OSLOG_NUM_STREAM_ENTRIES;
+ printf("oslog_setsize: new buffer size = %d, new num entries= %d\n", oslog_stream_buf_size, oslog_stream_num_entries);
+}
+
+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;
+}
+
+#ifdef CONFIG_XNUPOST
+
+uint32_t find_pattern_in_buffer(char * pattern, uint32_t len, int expected_count);
+
+/*
+ * returns count of pattern found in systemlog buffer.
+ * stops searching further if count reaches expected_count.
+ */
+uint32_t
+find_pattern_in_buffer(char * pattern, uint32_t len, int expected_count)
+{
+ int match_count = 0;
+ int i = 0;
+ int j = 0;
+ int no_match = 0;
+ int pos = 0;
+ char ch = 0;
+
+ if (pattern == NULL || len == 0 || expected_count == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < msgbufp->msg_size; i++) {
+ no_match = 0;
+ for (j = 0; j < (int)len; j++) {
+ pos = (msgbufp->msg_bufx + i + j) % msgbufp->msg_size;
+ ch = msgbufp->msg_bufc[pos];
+ if (ch != pattern[j]) {
+ no_match = 1;
+ break;
+ }
+ }
+ if (no_match == 0) {
+ match_count++;
+ if (match_count >= expected_count) {
+ break;
+ }
+ }
+ }
+ return match_count;
+}
+
+#endif