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