]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/subr_log.c
xnu-3789.21.4.tar.gz
[apple/xnu.git] / bsd / kern / subr_log.c
CommitLineData
1c79356b 1/*
39037602 2 * Copyright (c) 2000-2016 Apple, Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
39037602 5 *
2d21ac55
A
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.
39037602 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
39037602 17 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
39037602 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
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>
91447636 70#include <sys/proc_internal.h>
1c79356b 71#include <sys/vnode.h>
39037602
A
72#include <stdbool.h>
73#include <firehose/tracepoint_private.h>
74#include <firehose/ioctl_private.h>
75#include <os/firehose_buffer_private.h>
76
77#include <os/log_private.h>
1c79356b
A
78#include <sys/ioctl.h>
79#include <sys/msgbuf.h>
91447636 80#include <sys/file_internal.h>
1c79356b
A
81#include <sys/errno.h>
82#include <sys/select.h>
91447636 83#include <sys/kernel.h>
1c79356b 84#include <kern/thread.h>
39037602
A
85#include <kern/sched_prim.h>
86#include <kern/simple_lock.h>
91447636 87#include <sys/lock.h>
2d21ac55
A
88#include <sys/signalvar.h>
89#include <sys/conf.h>
90#include <sys/sysctl.h>
39037602 91#include <sys/queue.h>
2d21ac55 92#include <kern/kalloc.h>
6d2010ae 93#include <pexpert/pexpert.h>
39037602
A
94#include <mach/mach_port.h>
95#include <mach/mach_vm.h>
96#include <mach/vm_map.h>
97#include <vm/vm_kern.h>
98#include <kern/task.h>
99#include <kern/locks.h>
2d21ac55
A
100
101/* XXX should be in a common header somewhere */
2d21ac55 102extern void logwakeup(void);
39037602
A
103extern void oslogwakeup(void);
104extern void oslog_streamwakeup(void);
105static void oslog_streamwakeup_locked(void);
106vm_offset_t kernel_firehose_addr = 0;
107
108/* log message counters for streaming mode */
109uint32_t oslog_s_streamed_msgcount = 0;
110uint32_t oslog_s_dropped_msgcount = 0;
111extern uint32_t oslog_s_error_count;
1c79356b
A
112
113#define LOG_RDPRI (PZERO + 1)
114
115#define LOG_NBIO 0x02
116#define LOG_ASYNC 0x04
117#define LOG_RDWAIT 0x08
118
39236c6e 119#define MAX_UNREAD_CHARS (CONFIG_MSG_BSIZE/2)
6d2010ae
A
120/* All globals should be accessed under LOG_LOCK() */
121
122/* logsoftc only valid while log_open=1 */
1c79356b
A
123struct logsoftc {
124 int sc_state; /* see above for possibilities */
125 struct selinfo sc_selp; /* thread waiting for select */
126 int sc_pgid; /* process/group for async I/O */
127} logsoftc;
128
129int log_open; /* also used in log() */
6d2010ae 130char smsg_bufc[CONFIG_MSG_BSIZE]; /* static buffer */
39037602
A
131char oslog_stream_bufc[FIREHOSE_BUFFER_CHUNK_SIZE]; /* static buffer */
132struct firehose_buffer_chunk_s __attribute__((aligned(8))) oslog_boot_buf = {
133 .fbc_pos = {
134 .fbc_next_entry_offs = offsetof(struct firehose_buffer_chunk_s, fbc_data),
135 .fbc_private_offs = FIREHOSE_BUFFER_CHUNK_SIZE,
136 .fbc_refcnt = 1, // indicate that there is a writer to this chunk
137 .fbc_stream = firehose_stream_persist,
138 .fbc_flag_io = 1, // for now, lets assume this is coming from the io bank
139 },
140}; /* static buffer */
141firehose_buffer_chunk_t firehose_boot_chunk = &oslog_boot_buf;
6d2010ae 142struct msgbuf msgbuf = {MSG_MAGIC,sizeof(smsg_bufc),0,0,smsg_bufc};
39037602 143struct msgbuf oslog_stream_buf = {MSG_MAGIC,0,0,0,NULL};
39236c6e 144struct msgbuf *msgbufp __attribute__((used)) = &msgbuf;
39037602
A
145struct msgbuf *oslog_streambufp __attribute__((used)) = &oslog_stream_buf;
146
147// List entries for keeping track of the streaming buffer
148static oslog_stream_buf_entry_t oslog_stream_buf_entries;
149
150#define OSLOG_NUM_STREAM_ENTRIES 64
151#define OSLOG_STREAM_BUF_SIZE 4096
152
153int oslog_open = 0;
154int os_log_wakeup = 0;
155int oslog_stream_open = 0;
156int oslog_stream_buf_size = OSLOG_STREAM_BUF_SIZE;
157int oslog_stream_num_entries = OSLOG_NUM_STREAM_ENTRIES;
158
159/* oslogsoftc only valid while oslog_open=1 */
160struct oslogsoftc {
161 int sc_state; /* see above for possibilities */
162 struct selinfo sc_selp; /* thread waiting for select */
163 int sc_pgid; /* process/group for async I/O */
164} oslogsoftc;
165
166struct oslog_streamsoftc {
167 int sc_state; /* see above for possibilities */
168 struct selinfo sc_selp; /* thread waiting for select */
169 int sc_pgid; /* process/group for async I/O */
170}oslog_streamsoftc;
171
172STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_free_head =
173 STAILQ_HEAD_INITIALIZER(oslog_stream_free_head);
174STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_buf_head =
175 STAILQ_HEAD_INITIALIZER(oslog_stream_buf_head);
6d2010ae 176
39037602
A
177/* defined in osfmk/kern/printf.c */
178extern void oslog_lock_init(void);
2d21ac55 179extern void bsd_log_lock(void);
91447636 180extern void bsd_log_unlock(void);
39037602
A
181
182/* defined for osfmk/kern/printf.c */
183void bsd_log_init(void);
184
185/*
186 * Ideally this file would define this lock, but bsd doesn't have the definition
187 * for lock groups.
188 */
189decl_lck_spin_data(extern, oslog_stream_lock)
1c79356b 190
2d21ac55
A
191/* XXX wants a linker set so these can be static */
192extern d_open_t logopen;
193extern d_close_t logclose;
194extern d_read_t logread;
195extern d_ioctl_t logioctl;
196extern d_select_t logselect;
197
39037602
A
198/* XXX wants a linker set so these can be static */
199extern d_open_t oslogopen;
200extern d_close_t oslogclose;
201extern d_select_t oslogselect;
202extern d_ioctl_t oslogioctl;
203
204/* XXX wants a linker set so these can be static */
205extern d_open_t oslog_streamopen;
206extern d_close_t oslog_streamclose;
207extern d_read_t oslog_streamread;
208extern d_ioctl_t oslog_streamioctl;
209extern d_select_t oslog_streamselect;
210
211void oslog_init(void);
212void oslog_setsize(int size);
213void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
214 uint64_t stamp, const void *pubdata, size_t publen);
215void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry);
216static oslog_stream_buf_entry_t oslog_stream_find_free_buf_entry_locked(void);
217static void oslog_streamwrite_append_bytes(const char *buffer, int buflen);
218
1c79356b
A
219/*
220 * Serialize log access. Note that the log can be written at interrupt level,
221 * so any log manipulations that can be done from, or affect, another processor
222 * at interrupt level must be guarded with a spin lock.
223 */
91447636
A
224
225#define LOG_LOCK() bsd_log_lock()
226#define LOG_UNLOCK() bsd_log_unlock()
227
6d2010ae
A
228#if DEBUG
229#define LOG_SETSIZE_DEBUG(x...) kprintf(x)
230#else
231#define LOG_SETSIZE_DEBUG(x...) do { } while(0)
232#endif
233
234static int sysctl_kern_msgbuf(struct sysctl_oid *oidp,
235 void *arg1,
236 int arg2,
237 struct sysctl_req *req);
1c79356b
A
238
239/*ARGSUSED*/
2d21ac55
A
240int
241logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
1c79356b 242{
1c79356b
A
243 LOG_LOCK();
244 if (log_open) {
245 LOG_UNLOCK();
1c79356b
A
246 return (EBUSY);
247 }
1c79356b 248 logsoftc.sc_pgid = p->p_pid; /* signal process only */
6d2010ae 249 log_open = 1;
1c79356b 250
1c79356b 251 LOG_UNLOCK();
9bccf70c 252
1c79356b
A
253 return (0);
254}
255
256/*ARGSUSED*/
257int
2d21ac55 258logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
1c79356b 259{
1c79356b 260 LOG_LOCK();
39037602 261 logsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
1c79356b 262 selwakeup(&logsoftc.sc_selp);
1c79356b 263 selthreadclear(&logsoftc.sc_selp);
6d2010ae 264 log_open = 0;
1c79356b
A
265 LOG_UNLOCK();
266 return (0);
267}
268
39037602
A
269
270int
271oslogopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
272{
273 LOG_LOCK();
274 if (oslog_open) {
275 LOG_UNLOCK();
276 return(EBUSY);
277 }
278 oslogsoftc.sc_pgid = p->p_pid; /* signal process only */
279 oslog_open = 1;
280
281 LOG_UNLOCK();
282 return (0);
283}
284
285int
286oslogclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
287{
288 LOG_LOCK();
289 oslogsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
290 selwakeup(&oslogsoftc.sc_selp);
291 selthreadclear(&oslogsoftc.sc_selp);
292 oslog_open = 0;
293 LOG_UNLOCK();
294 return (0);
295}
296
297int
298oslog_streamopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
299{
300 char *oslog_stream_msg_bufc = NULL;
301 oslog_stream_buf_entry_t entries = NULL;
302
303 lck_spin_lock(&oslog_stream_lock);
304 if (oslog_stream_open) {
305 lck_spin_unlock(&oslog_stream_lock);
306 return EBUSY;
307 }
308 lck_spin_unlock(&oslog_stream_lock);
309
310 // Allocate the stream buffer
311 oslog_stream_msg_bufc = kalloc(oslog_stream_buf_size);
312 if (!oslog_stream_msg_bufc) {
313 return ENOMEM;
314 }
315
316 /* entries to support kernel logging in stream mode */
317 entries = kalloc(oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s));
318 if (!entries) {
319 kfree(oslog_stream_msg_bufc, oslog_stream_buf_size);
320 return ENOMEM;
321 }
322
323 lck_spin_lock(&oslog_stream_lock);
324 if (oslog_stream_open) {
325 lck_spin_unlock(&oslog_stream_lock);
326 kfree(oslog_stream_msg_bufc, oslog_stream_buf_size);
327 kfree(entries, oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s));
328 return EBUSY;
329 }
330
331 assert(oslog_streambufp->msg_bufc == NULL);
332 oslog_streambufp->msg_bufc = oslog_stream_msg_bufc;
333 oslog_streambufp->msg_size = oslog_stream_buf_size;
334
335 oslog_stream_buf_entries = entries;
336
337 STAILQ_INIT(&oslog_stream_free_head);
338 STAILQ_INIT(&oslog_stream_buf_head);
339
340 for (int i = 0; i < oslog_stream_num_entries; i++) {
341 oslog_stream_buf_entries[i].type = oslog_stream_link_type_log;
342 oslog_stream_buf_entries[i].offset = 0;
343 oslog_stream_buf_entries[i].size = 0;
344 oslog_stream_buf_entries[i].timestamp = 0;
345 STAILQ_INSERT_TAIL(&oslog_stream_free_head, &oslog_stream_buf_entries[i], buf_entries);
346 }
347
348 /* there should be no pending entries in the stream */
349 assert(STAILQ_EMPTY(&oslog_stream_buf_head));
350 assert(oslog_streambufp->msg_bufx == 0);
351 assert(oslog_streambufp->msg_bufr == 0);
352
353 oslog_streambufp->msg_bufx = 0;
354 oslog_streambufp->msg_bufr = 0;
355 oslog_streamsoftc.sc_pgid = p->p_pid; /* signal process only */
356 oslog_stream_open = 1;
357 lck_spin_unlock(&oslog_stream_lock);
358
359 return 0;
360}
361
362int
363oslog_streamclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
364{
365 oslog_stream_buf_entry_t next_entry = NULL;
366 char *oslog_stream_msg_bufc = NULL;
367 oslog_stream_buf_entry_t entries = NULL;
368
369 lck_spin_lock(&oslog_stream_lock);
370
371 if (oslog_stream_open == 0) {
372 lck_spin_unlock(&oslog_stream_lock);
373 return EBADF;
374 }
375
376 // Consume all log lines
377 while (!STAILQ_EMPTY(&oslog_stream_buf_head)) {
378 next_entry = STAILQ_FIRST(&oslog_stream_buf_head);
379 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
380 }
381 oslog_streamwakeup_locked();
382 oslog_streamsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
383 selwakeup(&oslog_streamsoftc.sc_selp);
384 selthreadclear(&oslog_streamsoftc.sc_selp);
385 oslog_stream_open = 0;
386 oslog_streambufp->msg_bufr = 0;
387 oslog_streambufp->msg_bufx = 0;
388 oslog_stream_msg_bufc = oslog_streambufp->msg_bufc;
389 oslog_streambufp->msg_bufc = NULL;
390 entries = oslog_stream_buf_entries;
391 oslog_stream_buf_entries = NULL;
392 oslog_streambufp->msg_size = 0;
393
394 lck_spin_unlock(&oslog_stream_lock);
395
396 // Free the stream buffer
397 kfree(oslog_stream_msg_bufc, oslog_stream_buf_size);
398 // Free the list entries
399 kfree(entries, oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s));
400
401 return 0;
402}
403
1c79356b
A
404/*ARGSUSED*/
405int
2d21ac55 406logread(__unused dev_t dev, struct uio *uio, int flag)
1c79356b 407{
6d2010ae 408 int l;
1c79356b
A
409 int error = 0;
410
91447636 411 LOG_LOCK();
1c79356b
A
412 while (msgbufp->msg_bufr == msgbufp->msg_bufx) {
413 if (flag & IO_NDELAY) {
91447636
A
414 error = EWOULDBLOCK;
415 goto out;
1c79356b
A
416 }
417 if (logsoftc.sc_state & LOG_NBIO) {
91447636
A
418 error = EWOULDBLOCK;
419 goto out;
1c79356b
A
420 }
421 logsoftc.sc_state |= LOG_RDWAIT;
91447636
A
422 LOG_UNLOCK();
423 /*
39236c6e 424 * If the wakeup is missed
91447636
A
425 * then wait for 5 sec and reevaluate
426 */
2d21ac55
A
427 if ((error = tsleep((caddr_t)msgbufp, LOG_RDPRI | PCATCH,
428 "klog", 5 * hz)) != 0) {
91447636
A
429 /* if it times out; ignore */
430 if (error != EWOULDBLOCK)
431 return (error);
1c79356b 432 }
91447636 433 LOG_LOCK();
1c79356b 434 }
1c79356b
A
435 logsoftc.sc_state &= ~LOG_RDWAIT;
436
91447636 437 while (uio_resid(uio) > 0) {
6d2010ae
A
438 int readpos;
439
1c79356b
A
440 l = msgbufp->msg_bufx - msgbufp->msg_bufr;
441 if (l < 0)
2d21ac55 442 l = msgbufp->msg_size - msgbufp->msg_bufr;
91447636 443 l = min(l, uio_resid(uio));
1c79356b
A
444 if (l == 0)
445 break;
6d2010ae
A
446
447 readpos = msgbufp->msg_bufr;
91447636 448 LOG_UNLOCK();
6d2010ae
A
449 error = uiomove((caddr_t)&msgbufp->msg_bufc[readpos],
450 l, uio);
91447636 451 LOG_LOCK();
1c79356b
A
452 if (error)
453 break;
6d2010ae
A
454 msgbufp->msg_bufr = readpos + l;
455 if (msgbufp->msg_bufr >= msgbufp->msg_size)
1c79356b
A
456 msgbufp->msg_bufr = 0;
457 }
91447636
A
458out:
459 LOG_UNLOCK();
1c79356b
A
460 return (error);
461}
462
39037602
A
463/*ARGSUSED*/
464int
465oslog_streamread(__unused dev_t dev, struct uio *uio, int flag)
466{
467 int error = 0;
468 int copy_size = 0;
469 static char logline[FIREHOSE_BUFFER_CHUNK_SIZE];
470
471 lck_spin_lock(&oslog_stream_lock);
472
473 if (!oslog_stream_open) {
474 lck_spin_unlock(&oslog_stream_lock);
475 return EBADF;
476 }
477
478 while (STAILQ_EMPTY(&oslog_stream_buf_head)) {
479 if (flag & IO_NDELAY || oslog_streamsoftc.sc_state & LOG_NBIO) {
480 lck_spin_unlock(&oslog_stream_lock);
481 return EWOULDBLOCK;
482 }
483
484 oslog_streamsoftc.sc_state |= LOG_RDWAIT;
485 wait_result_t wr = assert_wait((event_t)oslog_streambufp,
486 THREAD_INTERRUPTIBLE);
487 if (wr == THREAD_WAITING) {
488 lck_spin_unlock(&oslog_stream_lock);
489 wr = thread_block(THREAD_CONTINUE_NULL);
490 lck_spin_lock(&oslog_stream_lock);
491 }
492
493 switch (wr) {
494 case THREAD_AWAKENED:
495 case THREAD_TIMED_OUT:
496 break;
497 default:
498 lck_spin_unlock(&oslog_stream_lock);
499 return EINTR;
500 }
501 }
502
503 if (!oslog_stream_open) {
504 lck_spin_unlock(&oslog_stream_lock);
505 return EBADF;
506 }
507
508 int logpos = 0;
509 oslog_stream_buf_entry_t read_entry = NULL;
510 uint16_t rec_length;
511
512 read_entry = STAILQ_FIRST(&oslog_stream_buf_head);
513 assert(read_entry != NULL);
514 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
515
516 // Copy the timestamp first
517 memcpy(logline + logpos, &read_entry->timestamp, sizeof(uint64_t));
518 logpos += sizeof(uint64_t);
519
520 switch (read_entry->type) {
521 /* Handle metadata messages */
522 case oslog_stream_link_type_metadata:
523 {
524 memcpy(logline + logpos,
525 (read_entry->metadata), read_entry->size);
526 logpos += read_entry->size;
527
528 lck_spin_unlock(&oslog_stream_lock);
529
530 // Free the list entry
531 kfree(read_entry, (sizeof(struct oslog_stream_buf_entry_s) + read_entry->size));
532 break;
533 }
534 /* Handle log messages */
535 case oslog_stream_link_type_log:
536 {
537 /* ensure that the correct read entry was dequeued */
538 assert(read_entry->offset == oslog_streambufp->msg_bufr);
539 rec_length = read_entry->size;
540
541 // If the next log line is contiguous in the buffer, copy it out.
542 if(read_entry->offset + rec_length <= oslog_streambufp->msg_size) {
543 memcpy(logline + logpos,
544 oslog_streambufp->msg_bufc + read_entry->offset, rec_length);
545
546 oslog_streambufp->msg_bufr += rec_length;
547 if (oslog_streambufp->msg_bufr == oslog_streambufp->msg_size) {
548 oslog_streambufp->msg_bufr = 0;
549 }
550 logpos += rec_length;
551 } else {
552 // Otherwise, copy until the end of the buffer, and
553 // copy the remaining bytes starting at index 0.
554 int bytes_left = oslog_streambufp->msg_size - read_entry->offset;
555 memcpy(logline + logpos,
556 oslog_streambufp->msg_bufc + read_entry->offset, bytes_left);
557 logpos += bytes_left;
558 rec_length -= bytes_left;
559
560 memcpy(logline + logpos, (const void *)oslog_streambufp->msg_bufc,
561 rec_length);
562 oslog_streambufp->msg_bufr = rec_length;
563 logpos += rec_length;
564 }
565 assert(oslog_streambufp->msg_bufr < oslog_streambufp->msg_size);
566 STAILQ_INSERT_TAIL(&oslog_stream_free_head, read_entry, buf_entries);
567
568 lck_spin_unlock(&oslog_stream_lock);
569 break;
570 }
571 default:
572 {
573 panic("Got unexpected log entry type: %hhu\n", read_entry->type);
574 }
575 }
576
577 copy_size = min(logpos, uio_resid(uio));
578 if (copy_size != 0) {
579 error = uiomove((caddr_t)logline, copy_size, uio);
580 }
581 (void)hw_atomic_add(&oslog_s_streamed_msgcount, 1);
582
583 return error;
584}
585
1c79356b
A
586/*ARGSUSED*/
587int
2d21ac55 588logselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
1c79356b 589{
1c79356b
A
590 switch (rw) {
591
592 case FREAD:
91447636 593 LOG_LOCK();
1c79356b 594 if (msgbufp->msg_bufr != msgbufp->msg_bufx) {
91447636 595 LOG_UNLOCK();
1c79356b
A
596 return (1);
597 }
0b4e3aa0 598 selrecord(p, &logsoftc.sc_selp, wql);
91447636 599 LOG_UNLOCK();
1c79356b
A
600 break;
601 }
1c79356b
A
602 return (0);
603}
604
39037602
A
605int
606oslogselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
607{
608 switch (rw) {
609
610 case FREAD:
611 LOG_LOCK();
612 if (os_log_wakeup) {
613 LOG_UNLOCK();
614 return (1);
615 }
616 selrecord(p, &oslogsoftc.sc_selp, wql);
617 LOG_UNLOCK();
618 break;
619 }
620 return (0);
621}
622
623int
624oslog_streamselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
625{
626 int ret = 0;
627
628 lck_spin_lock(&oslog_stream_lock);
629
630 switch (rw) {
631 case FREAD:
632 if (STAILQ_EMPTY(&oslog_stream_buf_head)) {
633 selrecord(p, &oslog_streamsoftc.sc_selp, wql);
634 } else {
635 ret = 1;
636 }
637 break;
638 }
639
640 lck_spin_unlock(&oslog_stream_lock);
641 return ret;
642}
643
1c79356b 644void
2d21ac55 645logwakeup(void)
1c79356b 646{
1c79356b 647 int pgid;
1c79356b 648
39037602
A
649 /* cf. r24974766 & r25201228*/
650 if (oslog_is_safe() == FALSE) {
651 return;
652 }
653
91447636
A
654 LOG_LOCK();
655 if (!log_open) {
656 LOG_UNLOCK();
1c79356b 657 return;
91447636 658 }
1c79356b
A
659 selwakeup(&logsoftc.sc_selp);
660 if (logsoftc.sc_state & LOG_ASYNC) {
1c79356b
A
661 pgid = logsoftc.sc_pgid;
662 LOG_UNLOCK();
663 if (pgid < 0)
664 gsignal(-pgid, SIGIO);
2d21ac55
A
665 else
666 proc_signal(pgid, SIGIO);
91447636 667 LOG_LOCK();
1c79356b
A
668 }
669 if (logsoftc.sc_state & LOG_RDWAIT) {
670 wakeup((caddr_t)msgbufp);
671 logsoftc.sc_state &= ~LOG_RDWAIT;
672 }
91447636 673 LOG_UNLOCK();
1c79356b
A
674}
675
39037602
A
676void
677oslogwakeup(void)
678{
679 LOG_LOCK();
680 if (!oslog_open) {
681 LOG_UNLOCK();
682 return;
683 }
684 selwakeup(&oslogsoftc.sc_selp);
685 os_log_wakeup = 1;
686 LOG_UNLOCK();
687}
688
689static void
690oslog_streamwakeup_locked(void)
691{
692 lck_spin_assert(&oslog_stream_lock, LCK_ASSERT_OWNED);
693 if (!oslog_stream_open) {
694 return;
695 }
696 selwakeup(&oslog_streamsoftc.sc_selp);
697 if (oslog_streamsoftc.sc_state & LOG_RDWAIT) {
698 wakeup((caddr_t)oslog_streambufp);
699 oslog_streamsoftc.sc_state &= ~LOG_RDWAIT;
700 }
701}
702
703void
704oslog_streamwakeup(void)
705{
706 /* cf. r24974766 & r25201228*/
707 if (oslog_is_safe() == FALSE) {
708 return;
709 }
710
711 lck_spin_lock(&oslog_stream_lock);
712 oslog_streamwakeup_locked();
713 lck_spin_unlock(&oslog_stream_lock);
714}
1c79356b
A
715
716/*ARGSUSED*/
717int
2d21ac55 718logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
1c79356b 719{
6d2010ae 720 int l;
1c79356b 721
2d21ac55 722 LOG_LOCK();
1c79356b
A
723 switch (com) {
724
725 /* return number of characters immediately available */
726 case FIONREAD:
1c79356b 727 l = msgbufp->msg_bufx - msgbufp->msg_bufr;
1c79356b 728 if (l < 0)
2d21ac55 729 l += msgbufp->msg_size;
1c79356b
A
730 *(off_t *)data = l;
731 break;
732
733 case FIONBIO:
734 if (*(int *)data)
735 logsoftc.sc_state |= LOG_NBIO;
736 else
737 logsoftc.sc_state &= ~LOG_NBIO;
738 break;
739
740 case FIOASYNC:
741 if (*(int *)data)
742 logsoftc.sc_state |= LOG_ASYNC;
743 else
744 logsoftc.sc_state &= ~LOG_ASYNC;
745 break;
746
747 case TIOCSPGRP:
1c79356b 748 logsoftc.sc_pgid = *(int *)data;
1c79356b
A
749 break;
750
751 case TIOCGPGRP:
1c79356b 752 *(int *)data = logsoftc.sc_pgid;
1c79356b
A
753 break;
754
755 default:
91447636 756 LOG_UNLOCK();
1c79356b
A
757 return (-1);
758 }
91447636 759 LOG_UNLOCK();
1c79356b
A
760 return (0);
761}
762
39037602
A
763/*ARGSUSED*/
764int
765oslogioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
766{
767 int ret = 0;
768 mach_vm_size_t buffer_size = (FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT * FIREHOSE_BUFFER_CHUNK_SIZE);
769 firehose_buffer_map_info_t map_info = {0, 0};
770 firehose_buffer_t kernel_firehose_buffer = NULL;
771 mach_vm_address_t user_addr = 0;
772 mach_port_t mem_entry_ptr = MACH_PORT_NULL;
773
774 switch (com) {
775
776 /* return number of characters immediately available */
777
778 case LOGBUFFERMAP:
779 kernel_firehose_buffer = kernel_firehose_addr;
780
781 ret = mach_make_memory_entry_64(kernel_map,
782 &buffer_size,
783 (mach_vm_offset_t) kernel_firehose_buffer,
784 ( MAP_MEM_VM_SHARE | VM_PROT_READ ),
785 &mem_entry_ptr,
786 MACH_PORT_NULL);
787 if (ret == KERN_SUCCESS) {
788 ret = mach_vm_map(get_task_map(current_task()),
789 &user_addr,
790 buffer_size,
791 0, /* mask */
792 VM_FLAGS_ANYWHERE,
793 mem_entry_ptr,
794 0, /* offset */
795 FALSE, /* copy */
796 VM_PROT_READ,
797 VM_PROT_READ,
798 VM_INHERIT_SHARE);
799 }
800
801 if (ret == KERN_SUCCESS) {
802 map_info.fbmi_addr = (uint64_t) (user_addr);
803 map_info.fbmi_size = buffer_size;
804 bcopy(&map_info, data, sizeof(firehose_buffer_map_info_t));
805 }
806 break;
807 case LOGFLUSHED:
808 LOG_LOCK();
809 os_log_wakeup = 0;
810 LOG_UNLOCK();
811 __firehose_merge_updates(*(firehose_push_reply_t *)(data));
812 break;
813 default:
814 return (-1);
815 }
816 return (0);
817}
818
819/*ARGSUSED*/
820int
821oslog_streamioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
822{
823 int err = 0;
824
825 lck_spin_lock(&oslog_stream_lock);
826
827 switch (com) {
828 case FIONBIO:
829 if (data && *(int *)data)
830 oslog_streamsoftc.sc_state |= LOG_NBIO;
831 else
832 oslog_streamsoftc.sc_state &= ~LOG_NBIO;
833 break;
834 case FIOASYNC:
835 if (data && *(int *)data)
836 oslog_streamsoftc.sc_state |= LOG_ASYNC;
837 else
838 oslog_streamsoftc.sc_state &= ~LOG_ASYNC;
839 break;
840 default:
841 err = -1;
842 break;
843 }
844
845 lck_spin_unlock(&oslog_stream_lock);
846 return err;
847}
848
1c79356b 849void
2d21ac55 850bsd_log_init(void)
1c79356b 851{
6d2010ae 852 /* After this point, we must be ready to accept characters */
1c79356b
A
853}
854
39037602
A
855void
856oslog_init(void)
857{
858 kern_return_t kr;
859 vm_size_t size = FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT * FIREHOSE_BUFFER_CHUNK_SIZE;
860
861 oslog_lock_init();
862
863 kr = kmem_alloc_flags(kernel_map, &kernel_firehose_addr,
864 size + (2 * PAGE_SIZE), VM_KERN_MEMORY_LOG,
865 KMA_GUARD_FIRST | KMA_GUARD_LAST);
866 if (kr != KERN_SUCCESS) {
867 panic("Failed to allocate memory for firehose logging buffer");
868 }
869 kernel_firehose_addr += PAGE_SIZE;
870 bzero(kernel_firehose_addr, size);
871 /* register buffer with firehose */
872 kernel_firehose_addr = __firehose_buffer_create((size_t *) &size);
873
874 kprintf("oslog_init completed\n");
875}
2d21ac55
A
876
877/*
878 * log_putc_locked
879 *
880 * Decription: Output a character to the log; assumes the LOG_LOCK() is held
881 * by the caller.
882 *
883 * Parameters: c Character to output
884 *
885 * Returns: (void)
886 *
887 * Notes: This functions is used for multibyte output to the log; it
888 * should be used preferrentially where possible to ensure that
889 * log entries do not end up interspersed due to preemption or
890 * SMP reentrancy.
891 */
1c79356b 892void
2d21ac55 893log_putc_locked(char c)
1c79356b 894{
6d2010ae 895 struct msgbuf *mbp;
1c79356b
A
896
897 mbp = msgbufp;
1c79356b 898 mbp->msg_bufc[mbp->msg_bufx++] = c;
6d2010ae 899 if (mbp->msg_bufx >= msgbufp->msg_size)
1c79356b 900 mbp->msg_bufx = 0;
2d21ac55
A
901}
902
39037602
A
903static oslog_stream_buf_entry_t
904oslog_stream_find_free_buf_entry_locked(void)
905{
906 struct msgbuf *mbp;
907 oslog_stream_buf_entry_t buf_entry = NULL;
908
909 lck_spin_assert(&oslog_stream_lock, LCK_ASSERT_OWNED);
910
911 mbp = oslog_streambufp;
912
913 buf_entry = STAILQ_FIRST(&oslog_stream_free_head);
914 if (buf_entry) {
915 STAILQ_REMOVE_HEAD(&oslog_stream_free_head, buf_entries);
916 }
917 else {
918 // If no list elements are available in the free-list,
919 // consume the next log line so we can free up its list element
920 oslog_stream_buf_entry_t prev_entry = NULL;
921
922 buf_entry = STAILQ_FIRST(&oslog_stream_buf_head);
923 while (buf_entry->type == oslog_stream_link_type_metadata) {
924 prev_entry = buf_entry;
925 buf_entry = STAILQ_NEXT(buf_entry, buf_entries);
926 }
927
928 if (prev_entry == NULL) {
929 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
930 }
931 else {
932 STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries);
933 }
934
935 mbp->msg_bufr += buf_entry->size;
936 oslog_s_dropped_msgcount++;
937 if (mbp->msg_bufr >= mbp->msg_size) {
938 mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size);
939 }
940 }
941
942 return buf_entry;
943}
944
945void
946oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry)
947{
948 lck_spin_assert(&oslog_stream_lock, LCK_ASSERT_OWNED);
949 STAILQ_INSERT_TAIL(&oslog_stream_buf_head, m_entry, buf_entries);
950
951 return;
952}
953
954static void oslog_streamwrite_append_bytes(const char *buffer, int buflen)
955{
956 struct msgbuf *mbp;
957
958 lck_spin_assert(&oslog_stream_lock, LCK_ASSERT_OWNED);
959
960 mbp = oslog_streambufp;
961 // Check if we have enough space in the stream buffer to write the data
962 if (mbp->msg_bufx + buflen <= mbp->msg_size) {
963 memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, buflen);
964
965 mbp->msg_bufx += buflen;
966 if (mbp->msg_bufx == mbp->msg_size) {
967 mbp->msg_bufx = 0;
968 }
969 } else {
970 // Copy part of the data until the end of the stream
971 int bytes_left = mbp->msg_size - mbp->msg_bufx;
972 memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, bytes_left);
973
974 buflen -= bytes_left;
975 buffer += bytes_left;
976
977 // Copy the remainder of the data from the beginning of stream
978 memcpy((void *)mbp->msg_bufc, buffer, buflen);
979 mbp->msg_bufx = buflen;
980 }
981 return;
982}
983
984
985void
986oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
987 uint64_t stamp, const void *pubdata, size_t publen)
988{
989 struct msgbuf *mbp;
990 int available_space = 0;
991 oslog_stream_buf_entry_t buf_entry = NULL;
992 oslog_stream_buf_entry_t next_entry = NULL;
993
994 uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
995 int ft_length = ft_size + publen;
996
997 lck_spin_assert(&oslog_stream_lock, LCK_ASSERT_OWNED);
998
999 mbp = oslog_streambufp;
1000 if (ft_length > mbp->msg_size) {
1001 (void)hw_atomic_add(&oslog_s_error_count, 1);
1002 return;
1003 }
1004
1005 // Ensure that we have a list element for this record
1006 buf_entry = oslog_stream_find_free_buf_entry_locked();
1007
1008 assert(buf_entry != NULL);
1009
1010 // Ensure that we have space in the ring buffer for the current logline
1011 if (mbp->msg_bufr > mbp->msg_bufx) {
1012 available_space = mbp->msg_bufr - mbp->msg_bufx;
1013 } else {
1014 available_space = mbp->msg_size - mbp->msg_bufx + mbp->msg_bufr;
1015 }
1016 while(ft_length > available_space) {
1017 oslog_stream_buf_entry_t prev_entry = NULL;
1018
1019 next_entry = STAILQ_FIRST(&oslog_stream_buf_head);
1020 assert(next_entry != NULL);
1021 while (next_entry->type == oslog_stream_link_type_metadata) {
1022 prev_entry = next_entry;
1023 next_entry = STAILQ_NEXT(next_entry, buf_entries);
1024 }
1025
1026 if (prev_entry == NULL) {
1027 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
1028 }
1029 else {
1030 STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries);
1031 }
1032
1033 mbp->msg_bufr += next_entry->size;
1034 if (mbp->msg_bufr >= mbp->msg_size) {
1035 mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size);
1036 }
1037
1038 oslog_s_dropped_msgcount++;
1039 available_space += next_entry->size;
1040
1041 STAILQ_INSERT_TAIL(&oslog_stream_free_head, next_entry, buf_entries);
1042 }
1043
1044 assert(ft_length <= available_space);
1045
1046 // Write the log line and update the list entry for this record
1047 buf_entry->offset = mbp->msg_bufx;
1048 buf_entry->size = ft_length;
1049 buf_entry->timestamp = stamp;
1050 buf_entry->type = oslog_stream_link_type_log;
1051
1052 // Construct a tracepoint
1053 struct firehose_tracepoint_s fs = {
1054 .ft_thread = thread_tid(current_thread()),
1055 .ft_id.ftid_value = ftid.ftid_value,
1056 .ft_length = publen
1057 };
1058
1059 oslog_streamwrite_append_bytes((char *)&fs, sizeof(fs));
1060 oslog_streamwrite_append_bytes(pubdata, publen);
1061
1062 assert(mbp->msg_bufr < mbp->msg_size);
1063 // Insert the element to the buffer data list
1064 STAILQ_INSERT_TAIL(&oslog_stream_buf_head, buf_entry, buf_entries);
1065
1066 return;
1067}
1068
1069
2d21ac55
A
1070
1071/*
1072 * log_putc
1073 *
1074 * Decription: Output a character to the log; assumes the LOG_LOCK() is NOT
1075 * held by the caller.
1076 *
1077 * Parameters: c Character to output
1078 *
1079 * Returns: (void)
1080 *
1081 * Notes: This function is used for syingle byte output to the log. It
1082 * primarily exists to maintain binary backward compatibility.
1083 */
1084void
1085log_putc(char c)
1086{
39236c6e 1087 int unread_count = 0;
2d21ac55
A
1088 LOG_LOCK();
1089 log_putc_locked(c);
39236c6e 1090 unread_count = msgbufp->msg_bufx - msgbufp->msg_bufr;
91447636 1091 LOG_UNLOCK();
39236c6e
A
1092
1093 if (unread_count < 0)
1094 unread_count = 0 - unread_count;
1095 if (c == '\n' || unread_count >= MAX_UNREAD_CHARS)
1096 logwakeup();
1c79356b 1097}
91447636 1098
2d21ac55
A
1099
1100/*
1101 * it is possible to increase the kernel log buffer size by adding
1102 * msgbuf=n
1103 * to the kernel command line, and to read the current size using
1104 * sysctl kern.msgbuf
1105 * If there is no parameter on the kernel command line, the buffer is
6d2010ae
A
1106 * allocated statically and is CONFIG_MSG_BSIZE characters in size, otherwise
1107 * memory is dynamically allocated. Memory management must already be up.
2d21ac55 1108 */
6d2010ae
A
1109int
1110log_setsize(int size) {
2d21ac55 1111 char *new_logdata;
6d2010ae
A
1112 int new_logsize, new_bufr, new_bufx;
1113 char *old_logdata;
1114 int old_logsize, old_bufr, old_bufx;
1115 int i, count;
1116 char *p, ch;
1117
1118 if (size > MAX_MSG_BSIZE)
1119 return (EINVAL);
1120
1121 if (size <= 0)
1122 return (EINVAL);
1123
1124 new_logsize = size;
2d21ac55
A
1125 if (!(new_logdata = (char*)kalloc(size))) {
1126 printf("log_setsize: unable to allocate memory\n");
6d2010ae 1127 return (ENOMEM);
2d21ac55 1128 }
6d2010ae
A
1129 bzero(new_logdata, new_logsize);
1130
2d21ac55 1131 LOG_LOCK();
6d2010ae
A
1132
1133 old_logsize = msgbufp->msg_size;
1134 old_logdata = msgbufp->msg_bufc;
1135 old_bufr = msgbufp->msg_bufr;
1136 old_bufx = msgbufp->msg_bufx;
1137
1138 LOG_SETSIZE_DEBUG("log_setsize(%d): old_logdata %p old_logsize %d old_bufr %d old_bufx %d\n",
1139 size, old_logdata, old_logsize, old_bufr, old_bufx);
1140
1141 /* start "new_logsize" bytes before the write pointer */
1142 if (new_logsize <= old_bufx) {
1143 count = new_logsize;
1144 p = old_logdata + old_bufx - count;
1145 } else {
1146 /*
1147 * if new buffer is bigger, copy what we have and let the
1148 * bzero above handle the difference
1149 */
1150 count = MIN(new_logsize, old_logsize);
1151 p = old_logdata + old_logsize - (count - old_bufx);
1152 }
1153 for (i = 0; i < count; i++) {
1154 if (p >= old_logdata + old_logsize)
1155 p = old_logdata;
1156
1157 ch = *p++;
1158 new_logdata[i] = ch;
1159 }
1160
1161 new_bufx = i;
1162 if (new_bufx >= new_logsize)
1163 new_bufx = 0;
1164 msgbufp->msg_bufx = new_bufx;
1165
1166 new_bufr = old_bufx - old_bufr; /* how much were we trailing bufx by? */
1167 if (new_bufr < 0)
1168 new_bufr += old_logsize;
1169 new_bufr = new_bufx - new_bufr; /* now relative to oldest data in new buffer */
1170 if (new_bufr < 0)
1171 new_bufr += new_logsize;
1172 msgbufp->msg_bufr = new_bufr;
1173
1174 msgbufp->msg_size = new_logsize;
1175 msgbufp->msg_bufc = new_logdata;
1176
1177 LOG_SETSIZE_DEBUG("log_setsize(%d): new_logdata %p new_logsize %d new_bufr %d new_bufx %d\n",
1178 size, new_logdata, new_logsize, new_bufr, new_bufx);
1179
1180 LOG_UNLOCK();
1181
2d21ac55
A
1182 /* this memory is now dead - clear it so that it compresses better
1183 in case of suspend to disk etc. */
6d2010ae
A
1184 bzero(old_logdata, old_logsize);
1185 if (old_logdata != smsg_bufc) {
1186 /* dynamic memory that must be freed */
1187 kfree(old_logdata, old_logsize);
1188 }
1189
1190 printf("set system log size to %d bytes\n", new_logsize);
1191
1192 return 0;
1193}
1194
39037602
A
1195void oslog_setsize(int size)
1196{
1197 uint16_t scale = 0;
1198 // If the size is less than the default stream buffer
1199 // do nothing
1200 if (size <= OSLOG_STREAM_BUF_SIZE) {
1201 return;
1202 }
1203
1204 scale = (uint16_t) (size / OSLOG_STREAM_BUF_SIZE);
1205
1206 oslog_stream_buf_size = size;
1207 oslog_stream_num_entries = scale * OSLOG_NUM_STREAM_ENTRIES;
1208 printf("oslog_setsize: new buffer size = %d, new num entries= %d\n", oslog_stream_buf_size, oslog_stream_num_entries);
1209}
1210
6d2010ae
A
1211SYSCTL_PROC(_kern, OID_AUTO, msgbuf, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, sysctl_kern_msgbuf, "I", "");
1212
1213static int sysctl_kern_msgbuf(struct sysctl_oid *oidp __unused,
1214 void *arg1 __unused,
1215 int arg2 __unused,
1216 struct sysctl_req *req)
1217{
1218 int old_bufsize, bufsize;
1219 int error;
1220
1221 LOG_LOCK();
1222 old_bufsize = bufsize = msgbufp->msg_size;
2d21ac55 1223 LOG_UNLOCK();
6d2010ae
A
1224
1225 error = sysctl_io_number(req, bufsize, sizeof(bufsize), &bufsize, NULL);
1226 if (error)
1227 return (error);
1228
1229 if (bufsize != old_bufsize) {
1230 error = log_setsize(bufsize);
1231 }
1232
1233 return (error);
2d21ac55
A
1234}
1235
2d21ac55
A
1236
1237/*
6d2010ae 1238 * This should be called by /sbin/dmesg only via libproc.
2d21ac55
A
1239 * It returns as much data still in the buffer as possible.
1240 */
1241int
b0d623f7
A
1242log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t * retval) {
1243 uint32_t i;
6d2010ae 1244 uint32_t localbuff_size;
2d21ac55
A
1245 int error = 0, newl, skip;
1246 char *localbuff, *p, *copystart, ch;
6d2010ae 1247 size_t copysize;
2d21ac55 1248
6d2010ae
A
1249 LOG_LOCK();
1250 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
1251 LOG_UNLOCK();
1252
1253 /* Allocate a temporary non-circular buffer for copyout */
2d21ac55
A
1254 if (!(localbuff = (char *)kalloc(localbuff_size))) {
1255 printf("log_dmesg: unable to allocate memory\n");
1256 return (ENOMEM);
1257 }
6d2010ae 1258
2d21ac55
A
1259 /* in between here, the log could become bigger, but that's fine */
1260 LOG_LOCK();
1261
1262 /*
1263 * The message buffer is circular; start at the write pointer, and
1264 * make one loop up to write pointer - 1.
1265 */
1266 p = msgbufp->msg_bufc + msgbufp->msg_bufx;
1267 for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
1268 if (p >= msgbufp->msg_bufc + msgbufp->msg_size)
1269 p = msgbufp->msg_bufc;
1270 ch = *p;
1271 /* Skip "\n<.*>" syslog sequences. */
1272 if (skip) {
1273 if (ch == '>')
1274 newl = skip = 0;
1275 continue;
1276 }
1277 if (newl && ch == '<') {
1278 skip = 1;
1279 continue;
1280 }
1281 if (ch == '\0')
1282 continue;
6d2010ae 1283 newl = (ch == '\n');
2d21ac55 1284 localbuff[i++] = ch;
593a1d5f
A
1285 /* The original version of this routine contained a buffer
1286 * overflow. At the time, a "small" targeted fix was desired
1287 * so the change below to check the buffer bounds was made.
1288 * TODO: rewrite this needlessly convoluted routine.
1289 */
1290 if (i == (localbuff_size - 2))
1291 break;
2d21ac55
A
1292 }
1293 if (!newl)
1294 localbuff[i++] = '\n';
1295 localbuff[i++] = 0;
1296
1297 if (buffersize >= i) {
1298 copystart = localbuff;
1299 copysize = i;
1300 } else {
1301 copystart = localbuff + i - buffersize;
1302 copysize = buffersize;
1303 }
1304
1305 LOG_UNLOCK();
1306
1307 error = copyout(copystart, buffer, copysize);
1308 if (!error)
1309 *retval = copysize;
1310
1311 kfree(localbuff, localbuff_size);
1312 return (error);
1313}
39037602 1314