]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/subr_log.c
xnu-1228.0.2.tar.gz
[apple/xnu.git] / bsd / kern / subr_log.c
1 /*
2 * Copyright (c) 2000-2006 Apple Computer, 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
85 /* XXX should be in a common header somewhere */
86 extern void klogwakeup(void);
87 extern 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 struct logsoftc {
96 int sc_state; /* see above for possibilities */
97 struct selinfo sc_selp; /* thread waiting for select */
98 int sc_pgid; /* process/group for async I/O */
99 } logsoftc;
100
101 int log_open; /* also used in log() */
102 char smsg_bufc[MSG_BSIZE]; /* static buffer */
103 struct msgbuf temp_msgbuf = {0,MSG_BSIZE,0,0,smsg_bufc};
104 struct msgbuf *msgbufp;
105 static int _logentrypend = 0;
106 static int log_inited = 0;
107 /* the following are implemented in osfmk/kern/printf.c */
108 extern void bsd_log_lock(void);
109 extern void bsd_log_unlock(void);
110 extern void bsd_log_init(void);
111
112 /* XXX wants a linker set so these can be static */
113 extern d_open_t logopen;
114 extern d_close_t logclose;
115 extern d_read_t logread;
116 extern d_ioctl_t logioctl;
117 extern d_select_t logselect;
118
119 /*
120 * Serialize log access. Note that the log can be written at interrupt level,
121 * so any log manipulations that can be done from, or affect, another processor
122 * at interrupt level must be guarded with a spin lock.
123 */
124
125 #define LOG_LOCK() bsd_log_lock()
126 #define LOG_UNLOCK() bsd_log_unlock()
127
128
129 /*ARGSUSED*/
130 int
131 logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
132 {
133 LOG_LOCK();
134 if (log_open) {
135 LOG_UNLOCK();
136 return (EBUSY);
137 }
138 log_open = 1;
139 logsoftc.sc_pgid = p->p_pid; /* signal process only */
140 /*
141 * Potential race here with putchar() but since putchar should be
142 * called by autoconf, msg_magic should be initialized by the time
143 * we get here.
144 */
145 if (msgbufp->msg_magic != MSG_MAGIC) {
146 register int i;
147
148 msgbufp->msg_magic = MSG_MAGIC;
149 msgbufp->msg_bufx = msgbufp->msg_bufr = 0;
150 for (i=0; i < MSG_BSIZE; i++)
151 msgbufp->msg_bufc[i] = 0;
152 }
153 LOG_UNLOCK();
154
155 return (0);
156 }
157
158 /*ARGSUSED*/
159 int
160 logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
161 {
162 LOG_LOCK();
163 log_open = 0;
164 selwakeup(&logsoftc.sc_selp);
165 selthreadclear(&logsoftc.sc_selp);
166 LOG_UNLOCK();
167 return (0);
168 }
169
170 /*ARGSUSED*/
171 int
172 logread(__unused dev_t dev, struct uio *uio, int flag)
173 {
174 register long l;
175 int error = 0;
176
177 LOG_LOCK();
178 while (msgbufp->msg_bufr == msgbufp->msg_bufx) {
179 if (flag & IO_NDELAY) {
180 error = EWOULDBLOCK;
181 goto out;
182 }
183 if (logsoftc.sc_state & LOG_NBIO) {
184 error = EWOULDBLOCK;
185 goto out;
186 }
187 logsoftc.sc_state |= LOG_RDWAIT;
188 LOG_UNLOCK();
189 /*
190 * If the wakeup is missed the ligtening bolt will wake this up
191 * if there are any new characters. If that doesn't do it
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 l = msgbufp->msg_bufx - msgbufp->msg_bufr;
206 if (l < 0)
207 l = msgbufp->msg_size - msgbufp->msg_bufr;
208 l = min(l, uio_resid(uio));
209 if (l == 0)
210 break;
211 LOG_UNLOCK();
212 error = uiomove((caddr_t)&msgbufp->msg_bufc[msgbufp->msg_bufr],
213 (int)l, uio);
214 LOG_LOCK();
215 if (error)
216 break;
217 msgbufp->msg_bufr += l;
218 if (msgbufp->msg_bufr < 0 || msgbufp->msg_bufr >= msgbufp->msg_size)
219 msgbufp->msg_bufr = 0;
220 }
221 out:
222 LOG_UNLOCK();
223 return (error);
224 }
225
226 /*ARGSUSED*/
227 int
228 logselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
229 {
230 switch (rw) {
231
232 case FREAD:
233 LOG_LOCK();
234 if (msgbufp->msg_bufr != msgbufp->msg_bufx) {
235 LOG_UNLOCK();
236 return (1);
237 }
238 selrecord(p, &logsoftc.sc_selp, wql);
239 LOG_UNLOCK();
240 break;
241 }
242 return (0);
243 }
244
245 void
246 logwakeup(void)
247 {
248 int pgid;
249
250 LOG_LOCK();
251 if (!log_open) {
252 LOG_UNLOCK();
253 return;
254 }
255 selwakeup(&logsoftc.sc_selp);
256 if (logsoftc.sc_state & LOG_ASYNC) {
257 pgid = logsoftc.sc_pgid;
258 LOG_UNLOCK();
259 if (pgid < 0)
260 gsignal(-pgid, SIGIO);
261 else
262 proc_signal(pgid, SIGIO);
263 LOG_LOCK();
264 }
265 if (logsoftc.sc_state & LOG_RDWAIT) {
266 wakeup((caddr_t)msgbufp);
267 logsoftc.sc_state &= ~LOG_RDWAIT;
268 }
269 LOG_UNLOCK();
270 }
271
272 void
273 klogwakeup(void)
274 {
275 if (_logentrypend) {
276 _logentrypend = 0;
277 logwakeup();
278 }
279 }
280
281 /*ARGSUSED*/
282 int
283 logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
284 {
285 long l;
286
287 LOG_LOCK();
288 switch (com) {
289
290 /* return number of characters immediately available */
291 case FIONREAD:
292 l = msgbufp->msg_bufx - msgbufp->msg_bufr;
293 if (l < 0)
294 l += msgbufp->msg_size;
295 *(off_t *)data = l;
296 break;
297
298 case FIONBIO:
299 if (*(int *)data)
300 logsoftc.sc_state |= LOG_NBIO;
301 else
302 logsoftc.sc_state &= ~LOG_NBIO;
303 break;
304
305 case FIOASYNC:
306 if (*(int *)data)
307 logsoftc.sc_state |= LOG_ASYNC;
308 else
309 logsoftc.sc_state &= ~LOG_ASYNC;
310 break;
311
312 case TIOCSPGRP:
313 logsoftc.sc_pgid = *(int *)data;
314 break;
315
316 case TIOCGPGRP:
317 *(int *)data = logsoftc.sc_pgid;
318 break;
319
320 default:
321 LOG_UNLOCK();
322 return (-1);
323 }
324 LOG_UNLOCK();
325 return (0);
326 }
327
328 void
329 bsd_log_init(void)
330 {
331 if (!log_inited) {
332 msgbufp = &temp_msgbuf;
333 log_inited = 1;
334 }
335 }
336
337
338 /*
339 * log_putc_locked
340 *
341 * Decription: Output a character to the log; assumes the LOG_LOCK() is held
342 * by the caller.
343 *
344 * Parameters: c Character to output
345 *
346 * Returns: (void)
347 *
348 * Notes: This functions is used for multibyte output to the log; it
349 * should be used preferrentially where possible to ensure that
350 * log entries do not end up interspersed due to preemption or
351 * SMP reentrancy.
352 */
353 void
354 log_putc_locked(char c)
355 {
356 register struct msgbuf *mbp;
357
358 if (!log_inited) {
359 panic("bsd log is not inited");
360 }
361
362 mbp = msgbufp;
363 if (mbp-> msg_magic != MSG_MAGIC) {
364 register int i;
365
366 mbp->msg_magic = MSG_MAGIC;
367 mbp->msg_bufx = mbp->msg_bufr = 0;
368 for (i=0; i < MSG_BSIZE; i++)
369 mbp->msg_bufc[i] = 0;
370 }
371 mbp->msg_bufc[mbp->msg_bufx++] = c;
372 _logentrypend = 1;
373 if (mbp->msg_bufx < 0 || mbp->msg_bufx >= msgbufp->msg_size)
374 mbp->msg_bufx = 0;
375 }
376
377
378 /*
379 * log_putc
380 *
381 * Decription: Output a character to the log; assumes the LOG_LOCK() is NOT
382 * held by the caller.
383 *
384 * Parameters: c Character to output
385 *
386 * Returns: (void)
387 *
388 * Notes: This function is used for syingle byte output to the log. It
389 * primarily exists to maintain binary backward compatibility.
390 */
391 void
392 log_putc(char c)
393 {
394 if (!log_inited) {
395 panic("bsd log is not inited");
396 }
397 LOG_LOCK();
398 log_putc_locked(c);
399 LOG_UNLOCK();
400 }
401
402
403 /*
404 * it is possible to increase the kernel log buffer size by adding
405 * msgbuf=n
406 * to the kernel command line, and to read the current size using
407 * sysctl kern.msgbuf
408 * If there is no parameter on the kernel command line, the buffer is
409 * allocated statically and is MSG_BSIZE characters in size, otherwise
410 * memory is dynamically allocated.
411 * This function may only be called once, during kernel initialization.
412 * Memory management must already be up. The buffer must not have
413 * overflown yet.
414 */
415 void
416 log_setsize(long size) {
417 char *new_logdata;
418 if (msgbufp->msg_size!=MSG_BSIZE) {
419 printf("log_setsize: attempt to change size more than once\n");
420 return;
421 }
422 if (size==MSG_BSIZE)
423 return;
424 if (size<MSG_BSIZE) { /* we don't support reducing the log size */
425 printf("log_setsize: can't decrease log size\n");
426 return;
427 }
428 if (!(new_logdata = (char*)kalloc(size))) {
429 printf("log_setsize: unable to allocate memory\n");
430 return;
431 }
432 LOG_LOCK();
433 bcopy(smsg_bufc, new_logdata, MSG_BSIZE);
434 bzero(new_logdata+MSG_BSIZE, size - MSG_BSIZE);
435 /* this memory is now dead - clear it so that it compresses better
436 in case of suspend to disk etc. */
437 bzero(smsg_bufc, MSG_BSIZE);
438 msgbufp->msg_size = size;
439 msgbufp->msg_bufc = new_logdata;
440 LOG_UNLOCK();
441 printf("set system log size to %ld bytes\n", msgbufp->msg_size);
442 }
443
444 SYSCTL_LONG(_kern, OID_AUTO, msgbuf, CTLFLAG_RD, &temp_msgbuf.msg_size, "");
445
446 /*
447 * This should be called by single user mode /sbin/dmesg only.
448 * It returns as much data still in the buffer as possible.
449 */
450 int
451 log_dmesg(user_addr_t buffer, uint32_t buffersize, register_t * retval) {
452 unsigned long i;
453 int error = 0, newl, skip;
454 char *localbuff, *p, *copystart, ch;
455 long localbuff_size = msgbufp->msg_size+2, copysize;
456
457 if (!(localbuff = (char *)kalloc(localbuff_size))) {
458 printf("log_dmesg: unable to allocate memory\n");
459 return (ENOMEM);
460 }
461 /* in between here, the log could become bigger, but that's fine */
462 LOG_LOCK();
463
464 /*
465 * The message buffer is circular; start at the write pointer, and
466 * make one loop up to write pointer - 1.
467 */
468 p = msgbufp->msg_bufc + msgbufp->msg_bufx;
469 for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
470 if (p >= msgbufp->msg_bufc + msgbufp->msg_size)
471 p = msgbufp->msg_bufc;
472 ch = *p;
473 /* Skip "\n<.*>" syslog sequences. */
474 if (skip) {
475 if (ch == '>')
476 newl = skip = 0;
477 continue;
478 }
479 if (newl && ch == '<') {
480 skip = 1;
481 continue;
482 }
483 if (ch == '\0')
484 continue;
485 newl = ch == '\n';
486 localbuff[i++] = ch;
487 }
488 if (!newl)
489 localbuff[i++] = '\n';
490 localbuff[i++] = 0;
491
492 if (buffersize >= i) {
493 copystart = localbuff;
494 copysize = i;
495 } else {
496 copystart = localbuff + i - buffersize;
497 copysize = buffersize;
498 }
499
500 LOG_UNLOCK();
501
502 error = copyout(copystart, buffer, copysize);
503 if (!error)
504 *retval = copysize;
505
506 kfree(localbuff, localbuff_size);
507 return (error);
508 }