1/* 2 * Copyright (c) 2000-2010 Apple, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 29/* 30 * Copyright (c) 1982, 1986, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. All advertising materials mentioning features or use of this software 42 * must display the following acknowledgement: 43 * This product includes software developed by the University of 44 * California, Berkeley and its contributors. 45 * 4. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 * @(#)subr_log.c 8.3 (Berkeley) 2/14/95 62 */ 63 64/* 65 * Error log buffer for kernel printf's. 66 */ 67 68#include <sys/param.h> 69#include <sys/systm.h> 70#include <sys/proc_internal.h> 71#include <sys/vnode.h> 72#include <sys/ioctl.h> 73#include <sys/msgbuf.h> 74#include <sys/file_internal.h> 75#include <sys/errno.h> 76#include <sys/select.h> 77#include <sys/kernel.h> 78#include <kern/thread.h> 79#include <sys/lock.h> 80#include <sys/signalvar.h> 81#include <sys/conf.h> 82#include <sys/sysctl.h> 83#include <kern/kalloc.h> 84#include <pexpert/pexpert.h> 85 86/* XXX should be in a common header somewhere */ 87extern void logwakeup(void); 88 89#define LOG_RDPRI (PZERO + 1) 90 91#define LOG_NBIO 0x02 92#define LOG_ASYNC 0x04 93#define LOG_RDWAIT 0x08 94 95#define MAX_UNREAD_CHARS (CONFIG_MSG_BSIZE/2) 96/* All globals should be accessed under LOG_LOCK() */ 97 98/* logsoftc only valid while log_open=1 */ 99struct logsoftc { 100 int sc_state; /* see above for possibilities */ 101 struct selinfo sc_selp; /* thread waiting for select */ 102 int sc_pgid; /* process/group for async I/O */ 103} logsoftc; 104 105int log_open; /* also used in log() */ 106char smsg_bufc[CONFIG_MSG_BSIZE]; /* static buffer */ 107struct msgbuf msgbuf = {MSG_MAGIC,sizeof(smsg_bufc),0,0,smsg_bufc}; 108struct msgbuf *msgbufp __attribute__((used)) = &msgbuf; 109 110/* the following are implemented in osfmk/kern/printf.c */ 111extern void bsd_log_lock(void); 112extern void bsd_log_unlock(void); 113extern void bsd_log_init(void); 114 115/* XXX wants a linker set so these can be static */ 116extern d_open_t logopen; 117extern d_close_t logclose; 118extern d_read_t logread; 119extern d_ioctl_t logioctl; 120extern d_select_t logselect; 121 122/* 123 * Serialize log access. Note that the log can be written at interrupt level, 124 * so any log manipulations that can be done from, or affect, another processor 125 * at interrupt level must be guarded with a spin lock. 126 */ 127 128#define LOG_LOCK() bsd_log_lock() 129#define LOG_UNLOCK() bsd_log_unlock() 130 131#if DEBUG 132#define LOG_SETSIZE_DEBUG(x...) kprintf(x) 133#else 134#define LOG_SETSIZE_DEBUG(x...) do { } while(0) 135#endif 136 137static int sysctl_kern_msgbuf(struct sysctl_oid *oidp, 138 void *arg1, 139 int arg2, 140 struct sysctl_req *req); 141 142/*ARGSUSED*/ 143int 144logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p) 145{ 146 LOG_LOCK(); 147 if (log_open) { 148 LOG_UNLOCK(); 149 return (EBUSY); 150 } 151 logsoftc.sc_pgid = p->p_pid; /* signal process only */ 152 log_open = 1; 153 154 LOG_UNLOCK(); 155 156 return (0); 157} 158 159/*ARGSUSED*/ 160int 161logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p) 162{ 163 LOG_LOCK(); 164 selwakeup(&logsoftc.sc_selp); 165 selthreadclear(&logsoftc.sc_selp); 166 log_open = 0; 167 LOG_UNLOCK(); 168 return (0); 169} 170 171/*ARGSUSED*/ 172int 173logread(__unused dev_t dev, struct uio *uio, int flag) 174{ 175 int l; 176 int error = 0; 177 178 LOG_LOCK(); 179 while (msgbufp->msg_bufr == msgbufp->msg_bufx) { 180 if (flag & IO_NDELAY) { 181 error = EWOULDBLOCK; 182 goto out; 183 } 184 if (logsoftc.sc_state & LOG_NBIO) { 185 error = EWOULDBLOCK; 186 goto out; 187 } 188 logsoftc.sc_state |= LOG_RDWAIT; 189 LOG_UNLOCK(); 190 /* 191 * If the wakeup is missed 192 * then wait for 5 sec and reevaluate 193 */ 194 if ((error = tsleep((caddr_t)msgbufp, LOG_RDPRI | PCATCH, 195 "klog", 5 * hz)) != 0) { 196 /* if it times out; ignore */ 197 if (error != EWOULDBLOCK) 198 return (error); 199 } 200 LOG_LOCK(); 201 } 202 logsoftc.sc_state &= ~LOG_RDWAIT; 203 204 while (uio_resid(uio) > 0) { 205 int readpos; 206 207 l = msgbufp->msg_bufx - msgbufp->msg_bufr; 208 if (l < 0) 209 l = msgbufp->msg_size - msgbufp->msg_bufr; 210 l = min(l, uio_resid(uio)); 211 if (l == 0) 212 break; 213 214 readpos = msgbufp->msg_bufr; 215 LOG_UNLOCK(); 216 error = uiomove((caddr_t)&msgbufp->msg_bufc[readpos], 217 l, uio); 218 LOG_LOCK(); 219 if (error) 220 break; 221 msgbufp->msg_bufr = readpos + l; 222 if (msgbufp->msg_bufr >= msgbufp->msg_size) 223 msgbufp->msg_bufr = 0; 224 } 225out: 226 LOG_UNLOCK(); 227 return (error); 228} 229 230/*ARGSUSED*/ 231int 232logselect(__unused dev_t dev, int rw, void * wql, struct proc *p) 233{ 234 switch (rw) { 235 236 case FREAD: 237 LOG_LOCK(); 238 if (msgbufp->msg_bufr != msgbufp->msg_bufx) { 239 LOG_UNLOCK(); 240 return (1); 241 } 242 selrecord(p, &logsoftc.sc_selp, wql); 243 LOG_UNLOCK(); 244 break; 245 } 246 return (0); 247} 248 249void 250logwakeup(void) 251{ 252 int pgid; 253 254 LOG_LOCK(); 255 if (!log_open) { 256 LOG_UNLOCK(); 257 return; 258 } 259 selwakeup(&logsoftc.sc_selp); 260 if (logsoftc.sc_state & LOG_ASYNC) { 261 pgid = logsoftc.sc_pgid; 262 LOG_UNLOCK(); 263 if (pgid < 0) 264 gsignal(-pgid, SIGIO); 265 else 266 proc_signal(pgid, SIGIO); 267 LOG_LOCK(); 268 } 269 if (logsoftc.sc_state & LOG_RDWAIT) { 270 wakeup((caddr_t)msgbufp); 271 logsoftc.sc_state &= ~LOG_RDWAIT; 272 } 273 LOG_UNLOCK(); 274} 275 276 277/*ARGSUSED*/ 278int 279logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p) 280{ 281 int l; 282 283 LOG_LOCK(); 284 switch (com) { 285 286 /* return number of characters immediately available */ 287 case FIONREAD: 288 l = msgbufp->msg_bufx - msgbufp->msg_bufr; 289 if (l < 0) 290 l += msgbufp->msg_size; 291 *(off_t *)data = l; 292 break; 293 294 case FIONBIO: 295 if (*(int *)data) 296 logsoftc.sc_state |= LOG_NBIO; 297 else 298 logsoftc.sc_state &= ~LOG_NBIO; 299 break; 300 301 case FIOASYNC: 302 if (*(int *)data) 303 logsoftc.sc_state |= LOG_ASYNC; 304 else 305 logsoftc.sc_state &= ~LOG_ASYNC; 306 break; 307 308 case TIOCSPGRP: 309 logsoftc.sc_pgid = *(int *)data; 310 break; 311 312 case TIOCGPGRP: 313 *(int *)data = logsoftc.sc_pgid; 314 break; 315 316 default: 317 LOG_UNLOCK(); 318 return (-1); 319 } 320 LOG_UNLOCK(); 321 return (0); 322} 323 324void 325bsd_log_init(void) 326{ 327 /* After this point, we must be ready to accept characters */ 328} 329 330 331/* 332 * log_putc_locked 333 * 334 * Decription: Output a character to the log; assumes the LOG_LOCK() is held 335 * by the caller. 336 * 337 * Parameters: c Character to output 338 * 339 * Returns: (void) 340 * 341 * Notes: This functions is used for multibyte output to the log; it 342 * should be used preferrentially where possible to ensure that 343 * log entries do not end up interspersed due to preemption or 344 * SMP reentrancy. 345 */ 346void 347log_putc_locked(char c) 348{ 349 struct msgbuf *mbp; 350 351 mbp = msgbufp; 352 mbp->msg_bufc[mbp->msg_bufx++] = c; 353 if (mbp->msg_bufx >= msgbufp->msg_size) 354 mbp->msg_bufx = 0; 355} 356 357 358/* 359 * log_putc 360 * 361 * Decription: Output a character to the log; assumes the LOG_LOCK() is NOT 362 * held by the caller. 363 * 364 * Parameters: c Character to output 365 * 366 * Returns: (void) 367 * 368 * Notes: This function is used for syingle byte output to the log. It 369 * primarily exists to maintain binary backward compatibility. 370 */ 371void 372log_putc(char c) 373{ 374 int unread_count = 0; 375 LOG_LOCK(); 376 log_putc_locked(c); 377 unread_count = msgbufp->msg_bufx - msgbufp->msg_bufr; 378 LOG_UNLOCK(); 379 380 if (unread_count < 0) 381 unread_count = 0 - unread_count; 382 if (c == '\n' || unread_count >= MAX_UNREAD_CHARS) 383 logwakeup(); 384} 385 386 387/* 388 * it is possible to increase the kernel log buffer size by adding 389 * msgbuf=n 390 * to the kernel command line, and to read the current size using 391 * sysctl kern.msgbuf 392 * If there is no parameter on the kernel command line, the buffer is 393 * allocated statically and is CONFIG_MSG_BSIZE characters in size, otherwise 394 * memory is dynamically allocated. Memory management must already be up. 395 */ 396int 397log_setsize(int size) { 398 char *new_logdata; 399 int new_logsize, new_bufr, new_bufx; 400 char *old_logdata; 401 int old_logsize, old_bufr, old_bufx; 402 int i, count; 403 char *p, ch; 404 405 if (size > MAX_MSG_BSIZE) 406 return (EINVAL); 407 408 if (size <= 0) 409 return (EINVAL); 410 411 new_logsize = size; 412 if (!(new_logdata = (char*)kalloc(size))) { 413 printf("log_setsize: unable to allocate memory\n"); 414 return (ENOMEM); 415 } 416 bzero(new_logdata, new_logsize); 417 418 LOG_LOCK(); 419 420 old_logsize = msgbufp->msg_size; 421 old_logdata = msgbufp->msg_bufc; 422 old_bufr = msgbufp->msg_bufr; 423 old_bufx = msgbufp->msg_bufx; 424 425 LOG_SETSIZE_DEBUG("log_setsize(%d): old_logdata %p old_logsize %d old_bufr %d old_bufx %d\n", 426 size, old_logdata, old_logsize, old_bufr, old_bufx); 427 428 /* start "new_logsize" bytes before the write pointer */ 429 if (new_logsize <= old_bufx) { 430 count = new_logsize; 431 p = old_logdata + old_bufx - count; 432 } else { 433 /* 434 * if new buffer is bigger, copy what we have and let the 435 * bzero above handle the difference 436 */ 437 count = MIN(new_logsize, old_logsize); 438 p = old_logdata + old_logsize - (count - old_bufx); 439 } 440 for (i = 0; i < count; i++) { 441 if (p >= old_logdata + old_logsize) 442 p = old_logdata; 443 444 ch = *p++; 445 new_logdata[i] = ch; 446 } 447 448 new_bufx = i; 449 if (new_bufx >= new_logsize) 450 new_bufx = 0; 451 msgbufp->msg_bufx = new_bufx; 452 453 new_bufr = old_bufx - old_bufr; /* how much were we trailing bufx by? */ 454 if (new_bufr < 0) 455 new_bufr += old_logsize; 456 new_bufr = new_bufx - new_bufr; /* now relative to oldest data in new buffer */ 457 if (new_bufr < 0) 458 new_bufr += new_logsize; 459 msgbufp->msg_bufr = new_bufr; 460 461 msgbufp->msg_size = new_logsize; 462 msgbufp->msg_bufc = new_logdata; 463 464 LOG_SETSIZE_DEBUG("log_setsize(%d): new_logdata %p new_logsize %d new_bufr %d new_bufx %d\n", 465 size, new_logdata, new_logsize, new_bufr, new_bufx); 466 467 LOG_UNLOCK(); 468 469 /* this memory is now dead - clear it so that it compresses better 470 in case of suspend to disk etc. */ 471 bzero(old_logdata, old_logsize); 472 if (old_logdata != smsg_bufc) { 473 /* dynamic memory that must be freed */ 474 kfree(old_logdata, old_logsize); 475 } 476 477 printf("set system log size to %d bytes\n", new_logsize); 478 479 return 0; 480} 481 482SYSCTL_PROC(_kern, OID_AUTO, msgbuf, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, sysctl_kern_msgbuf, "I", ""); 483 484static int sysctl_kern_msgbuf(struct sysctl_oid *oidp __unused, 485 void *arg1 __unused, 486 int arg2 __unused, 487 struct sysctl_req *req) 488{ 489 int old_bufsize, bufsize; 490 int error; 491 492 LOG_LOCK(); 493 old_bufsize = bufsize = msgbufp->msg_size; 494 LOG_UNLOCK(); 495 496 error = sysctl_io_number(req, bufsize, sizeof(bufsize), &bufsize, NULL); 497 if (error) 498 return (error); 499 500 if (bufsize != old_bufsize) { 501 error = log_setsize(bufsize); 502 } 503 504 return (error); 505} 506 507 508/* 509 * This should be called by /sbin/dmesg only via libproc. 510 * It returns as much data still in the buffer as possible. 511 */ 512int 513log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t * retval) { 514 uint32_t i; 515 uint32_t localbuff_size; 516 int error = 0, newl, skip; 517 char *localbuff, *p, *copystart, ch; 518 size_t copysize; 519 520 LOG_LOCK(); 521 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */ 522 LOG_UNLOCK(); 523 524 /* Allocate a temporary non-circular buffer for copyout */ 525 if (!(localbuff = (char *)kalloc(localbuff_size))) { 526 printf("log_dmesg: unable to allocate memory\n"); 527 return (ENOMEM); 528 } 529 530 /* in between here, the log could become bigger, but that's fine */ 531 LOG_LOCK(); 532 533 /* 534 * The message buffer is circular; start at the write pointer, and 535 * make one loop up to write pointer - 1. 536 */ 537 p = msgbufp->msg_bufc + msgbufp->msg_bufx; 538 for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) { 539 if (p >= msgbufp->msg_bufc + msgbufp->msg_size) 540 p = msgbufp->msg_bufc; 541 ch = *p; 542 /* Skip "\n<.*>" syslog sequences. */ 543 if (skip) { 544 if (ch == '>') 545 newl = skip = 0; 546 continue; 547 } 548 if (newl && ch == '<') { 549 skip = 1; 550 continue; 551 } 552 if (ch == '\0') 553 continue; 554 newl = (ch == '\n'); 555 localbuff[i++] = ch; 556 /* The original version of this routine contained a buffer 557 * overflow. At the time, a "small" targeted fix was desired 558 * so the change below to check the buffer bounds was made. 559 * TODO: rewrite this needlessly convoluted routine. 560 */ 561 if (i == (localbuff_size - 2)) 562 break; 563 } 564 if (!newl) 565 localbuff[i++] = '\n'; 566 localbuff[i++] = 0; 567 568 if (buffersize >= i) { 569 copystart = localbuff; 570 copysize = i; 571 } else { 572 copystart = localbuff + i - buffersize; 573 copysize = buffersize; 574 } 575 576 LOG_UNLOCK(); 577 578 error = copyout(copystart, buffer, copysize); 579 if (!error) 580 *retval = copysize; 581 582 kfree(localbuff, localbuff_size); 583 return (error); 584} 585