1 /*        $NetBSD: subr_log.c,v 1.66 2025/04/09 05:38:01 rin Exp $    */
2 
3 /*-
4  * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1982, 1986, 1993
34  *        The Regents of the University of California.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. Neither the name of the University nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58  * SUCH DAMAGE.
59  *
60  *        @(#)subr_log.c      8.3 (Berkeley) 2/14/95
61  */
62 
63 /*
64  * Error log buffer for kernel printf's.
65  */
66 
67 #include <sys/cdefs.h>
68 __KERNEL_RCSID(0, "$NetBSD: subr_log.c,v 1.66 2025/04/09 05:38:01 rin Exp $");
69 
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/proc.h>
74 #include <sys/vnode.h>
75 #include <sys/ioctl.h>
76 #include <sys/msgbuf.h>
77 #include <sys/file.h>
78 #include <sys/syslog.h>
79 #include <sys/conf.h>
80 #include <sys/select.h>
81 #include <sys/poll.h>
82 #include <sys/intr.h>
83 #include <sys/sysctl.h>
84 #include <sys/ktrace.h>
85 
86 static int sysctl_msgbuf(SYSCTLFN_PROTO);
87 
88 static void         logsoftintr(void *);
89 
90 static bool         log_async;
91 static struct selinfo log_selp;                   /* process waiting on select call */
92 static pid_t        log_pgid;           /* process/group for async I/O */
93 static kcondvar_t log_cv;
94 static void         *log_sih;
95 
96 static kmutex_t log_lock;
97 int       log_open;                     /* also used in log() */
98 int       msgbufmapped;                           /* is the message buffer mapped */
99 int       msgbufenabled;                          /* is logging to the buffer enabled */
100 struct    kern_msgbuf *msgbufp;                   /* the mapped buffer, itself. */
101 
102 void
initmsgbuf(void * bf,size_t bufsize)103 initmsgbuf(void *bf, size_t bufsize)
104 {
105           struct kern_msgbuf *mbp;
106           long new_bufs;
107 
108           /* Sanity-check the given size. */
109           if (bufsize < sizeof(struct kern_msgbuf))
110                     return;
111 
112           mbp = msgbufp = (struct kern_msgbuf *)bf;
113 
114           new_bufs = bufsize - offsetof(struct kern_msgbuf, msg_bufc);
115           if ((mbp->msg_magic != MSG_MAGIC) || (mbp->msg_bufs != new_bufs) ||
116               (mbp->msg_bufr < 0) || (mbp->msg_bufr >= mbp->msg_bufs) ||
117               (mbp->msg_bufx < 0) || (mbp->msg_bufx >= mbp->msg_bufs)) {
118                     /*
119                      * If the buffer magic number is wrong, has changed
120                      * size (which shouldn't happen often), or is
121                      * internally inconsistent, initialize it.
122                      */
123 
124                     memset(bf, 0, bufsize);
125                     mbp->msg_magic = MSG_MAGIC;
126                     mbp->msg_bufs = new_bufs;
127           }
128 
129           /* mark it as ready for use. */
130           msgbufmapped = msgbufenabled = 1;
131 }
132 
133 void
loginit(void)134 loginit(void)
135 {
136 
137           mutex_init(&log_lock, MUTEX_DEFAULT, IPL_VM);
138           selinit(&log_selp);
139           cv_init(&log_cv, "klog");
140           log_sih = softint_establish(SOFTINT_CLOCK | SOFTINT_MPSAFE,
141               logsoftintr, NULL);
142 
143           sysctl_createv(NULL, 0, NULL, NULL,
144                            CTLFLAG_PERMANENT,
145                            CTLTYPE_INT, "msgbufsize",
146                            SYSCTL_DESCR("Size of the kernel message buffer"),
147                            sysctl_msgbuf, 0, NULL, 0,
148                            CTL_KERN, KERN_MSGBUFSIZE, CTL_EOL);
149           sysctl_createv(NULL, 0, NULL, NULL,
150                            CTLFLAG_PERMANENT,
151                            CTLTYPE_INT, "msgbuf",
152                            SYSCTL_DESCR("Kernel message buffer"),
153                            sysctl_msgbuf, 0, NULL, 0,
154                            CTL_KERN, KERN_MSGBUF, CTL_EOL);
155 }
156 
157 /*ARGSUSED*/
158 static int
logopen(dev_t dev,int flags,int mode,struct lwp * l)159 logopen(dev_t dev, int flags, int mode, struct lwp *l)
160 {
161           struct kern_msgbuf *mbp = msgbufp;
162           int error = 0;
163 
164           mutex_spin_enter(&log_lock);
165           if (log_open) {
166                     error = EBUSY;
167           } else {
168                     log_open = 1;
169                     log_pgid = l->l_proc->p_pid;  /* signal process only */
170                     /*
171                      * The message buffer is initialized during system
172                      * configuration.  If it's been clobbered, note that
173                      * and return an error.  (This allows a user to read
174                      * the buffer via /dev/kmem, and try to figure out
175                      * what clobbered it.
176                      */
177                     if (mbp->msg_magic != MSG_MAGIC) {
178                               msgbufenabled = 0;
179                               error = ENXIO;
180                     }
181           }
182           mutex_spin_exit(&log_lock);
183 
184           return error;
185 }
186 
187 /*ARGSUSED*/
188 static int
logclose(dev_t dev,int flag,int mode,struct lwp * l)189 logclose(dev_t dev, int flag, int mode, struct lwp *l)
190 {
191 
192           mutex_spin_enter(&log_lock);
193           log_pgid = 0;
194           log_open = 0;
195           log_async = 0;
196           mutex_spin_exit(&log_lock);
197 
198           return 0;
199 }
200 
201 /*ARGSUSED*/
202 static int
logread(dev_t dev,struct uio * uio,int flag)203 logread(dev_t dev, struct uio *uio, int flag)
204 {
205           struct kern_msgbuf *mbp = msgbufp;
206           long l;
207           int error = 0;
208 
209           mutex_spin_enter(&log_lock);
210           while (mbp->msg_bufr == mbp->msg_bufx) {
211                     if (flag & IO_NDELAY) {
212                               mutex_spin_exit(&log_lock);
213                               return EWOULDBLOCK;
214                     }
215                     error = cv_wait_sig(&log_cv, &log_lock);
216                     if (error) {
217                               mutex_spin_exit(&log_lock);
218                               return error;
219                     }
220           }
221           while (uio->uio_resid > 0) {
222                     char buf[128]; /* taken from FreeBSD */
223 
224                     l = mbp->msg_bufx - mbp->msg_bufr;
225                     if (l < 0)
226                               l = mbp->msg_bufs - mbp->msg_bufr;
227                     l = ulmin(l, uio->uio_resid);
228                     if (l == 0)
229                               break;
230 
231                     l = ulmin(l, sizeof(buf));
232                     memcpy(buf, &mbp->msg_bufc[mbp->msg_bufr], l);
233                     mbp->msg_bufr += l;
234                     if (mbp->msg_bufr < 0 || mbp->msg_bufr >= mbp->msg_bufs)
235                               mbp->msg_bufr = 0;
236                     mutex_spin_exit(&log_lock);
237                     error = uiomove(buf, l, uio);
238                     mutex_spin_enter(&log_lock);
239                     if (error)
240                               break;
241           }
242           mutex_spin_exit(&log_lock);
243 
244           return error;
245 }
246 
247 /*ARGSUSED*/
248 static int
logpoll(dev_t dev,int events,struct lwp * l)249 logpoll(dev_t dev, int events, struct lwp *l)
250 {
251           int revents = 0;
252 
253           if (events & (POLLIN | POLLRDNORM)) {
254                     mutex_spin_enter(&log_lock);
255                     if (msgbufp->msg_bufr != msgbufp->msg_bufx)
256                               revents |= events & (POLLIN | POLLRDNORM);
257                     else
258                               selrecord(l, &log_selp);
259                     mutex_spin_exit(&log_lock);
260           }
261 
262           return revents;
263 }
264 
265 static void
filt_logrdetach(struct knote * kn)266 filt_logrdetach(struct knote *kn)
267 {
268 
269           mutex_spin_enter(&log_lock);
270           selremove_knote(&log_selp, kn);
271           mutex_spin_exit(&log_lock);
272 }
273 
274 static int
filt_logread(struct knote * kn,long hint)275 filt_logread(struct knote *kn, long hint)
276 {
277           int rv;
278 
279           if ((hint & NOTE_SUBMIT) == 0)
280                     mutex_spin_enter(&log_lock);
281           if (msgbufp->msg_bufr == msgbufp->msg_bufx) {
282                     rv = 0;
283           } else if (msgbufp->msg_bufr < msgbufp->msg_bufx) {
284                     kn->kn_data = msgbufp->msg_bufx - msgbufp->msg_bufr;
285                     rv = 1;
286           } else {
287                     kn->kn_data = (msgbufp->msg_bufs - msgbufp->msg_bufr) +
288                         msgbufp->msg_bufx;
289                     rv = 1;
290           }
291           if ((hint & NOTE_SUBMIT) == 0)
292                     mutex_spin_exit(&log_lock);
293 
294           return rv;
295 }
296 
297 static const struct filterops logread_filtops = {
298           .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
299           .f_attach = NULL,
300           .f_detach = filt_logrdetach,
301           .f_event = filt_logread,
302 };
303 
304 static int
logkqfilter(dev_t dev,struct knote * kn)305 logkqfilter(dev_t dev, struct knote *kn)
306 {
307 
308           switch (kn->kn_filter) {
309           case EVFILT_READ:
310                     kn->kn_fop = &logread_filtops;
311                     mutex_spin_enter(&log_lock);
312                     selrecord_knote(&log_selp, kn);
313                     mutex_spin_exit(&log_lock);
314                     break;
315 
316           default:
317                     return (EINVAL);
318           }
319 
320           return (0);
321 }
322 
323 void
logwakeup(void)324 logwakeup(void)
325 {
326 
327           if (!cold && log_open) {
328                     mutex_spin_enter(&log_lock);
329                     selnotify(&log_selp, 0, NOTE_SUBMIT);
330                     if (log_async)
331                               softint_schedule(log_sih);
332                     cv_broadcast(&log_cv);
333                     mutex_spin_exit(&log_lock);
334           }
335 }
336 
337 static void
logsoftintr(void * cookie)338 logsoftintr(void *cookie)
339 {
340           pid_t pid;
341 
342           if ((pid = log_pgid) != 0)
343                     fownsignal(pid, SIGIO, 0, 0, NULL);
344 }
345 
346 /*ARGSUSED*/
347 static int
logioctl(dev_t dev,u_long com,void * data,int flag,struct lwp * lwp)348 logioctl(dev_t dev, u_long com, void *data, int flag, struct lwp *lwp)
349 {
350           long l;
351 
352           switch (com) {
353 
354           /* return number of characters immediately available */
355           case FIONREAD:
356                     mutex_spin_enter(&log_lock);
357                     l = msgbufp->msg_bufx - msgbufp->msg_bufr;
358                     if (l < 0)
359                               l += msgbufp->msg_bufs;
360                     mutex_spin_exit(&log_lock);
361                     *(int *)data = l;
362                     break;
363 
364           case FIONBIO:
365                     break;
366 
367           case FIOASYNC:
368                     /* No locking needed, 'thread private'. */
369                     log_async = (*((int *)data) != 0);
370                     break;
371 
372           case TIOCSPGRP:
373           case FIOSETOWN:
374                     return fsetown(&log_pgid, com, data);
375 
376           case TIOCGPGRP:
377           case FIOGETOWN:
378                     return fgetown(log_pgid, com, data);
379 
380           default:
381                     return (EPASSTHROUGH);
382           }
383           return (0);
384 }
385 
386 static void
logskip(struct kern_msgbuf * mbp)387 logskip(struct kern_msgbuf *mbp)
388 {
389           /*
390            * Move forward read pointer to the next line
391            * in the buffer.  Note that the buffer is
392            * a ring buffer so we should reset msg_bufr
393            * to 0 when msg_bufr exceeds msg_bufs.
394            *
395            * To prevent to loop forever, give up if we
396            * cannot find a newline in mbp->msg_bufs
397            * characters (the max size of the buffer).
398            */
399           for (int i = 0; i < mbp->msg_bufs; i++) {
400                     char c0 = mbp->msg_bufc[mbp->msg_bufr];
401                     if (++mbp->msg_bufr >= mbp->msg_bufs)
402                               mbp->msg_bufr = 0;
403                     if (c0 == '\n')
404                               break;
405           }
406 }
407 
408 static void
logaddchar(struct kern_msgbuf * mbp,int c)409 logaddchar(struct kern_msgbuf *mbp, int c)
410 {
411           mbp->msg_bufc[mbp->msg_bufx++] = c;
412           if (mbp->msg_bufx < 0 || mbp->msg_bufx >= mbp->msg_bufs)
413                     mbp->msg_bufx = 0;
414 
415           /* If the buffer is full, keep the most recent data. */
416           if (mbp->msg_bufr == mbp->msg_bufx)
417                     logskip(mbp);
418 }
419 
420 void
logputchar(int c)421 logputchar(int c)
422 {
423           struct kern_msgbuf *mbp;
424 
425           if (!cold)
426                     mutex_spin_enter(&log_lock);
427 
428           if (!msgbufenabled)
429                     goto out;
430 
431           mbp = msgbufp;
432           if (mbp->msg_magic != MSG_MAGIC) {
433                     /*
434                      * Arguably should panic or somehow notify the
435                      * user...  but how?  Panic may be too drastic,
436                      * and would obliterate the message being kicked
437                      * out (maybe a panic itself), and printf
438                      * would invoke us recursively.  Silently punt
439                      * for now.  If syslog is running, it should
440                      * notice.
441                      */
442                     msgbufenabled = 0;
443                     goto out;
444 
445           }
446 
447           logaddchar(mbp, c);
448 
449 out:
450           if (!cold)
451                     mutex_spin_exit(&log_lock);
452 }
453 
454 /*
455  * sysctl helper routine for kern.msgbufsize and kern.msgbuf. For the
456  * former it merely checks the message buffer is set up. For the latter,
457  * it also copies out the data if necessary.
458  */
459 static int
sysctl_msgbuf(SYSCTLFN_ARGS)460 sysctl_msgbuf(SYSCTLFN_ARGS)
461 {
462           char *where = oldp;
463           size_t len, maxlen;
464           long beg, end;
465           int error;
466 
467           if (!logenabled(msgbufp)) {
468                     msgbufenabled = 0;
469                     return (ENXIO);
470           }
471 
472           switch (rnode->sysctl_num) {
473           case KERN_MSGBUFSIZE: {
474                     struct sysctlnode node = *rnode;
475                     int msg_bufs = (int)msgbufp->msg_bufs;
476                     node.sysctl_data = &msg_bufs;
477                     return (sysctl_lookup(SYSCTLFN_CALL(&node)));
478           }
479           case KERN_MSGBUF:
480                     break;
481           default:
482                     return (EOPNOTSUPP);
483           }
484 
485           if (newp != NULL)
486                     return (EPERM);
487 
488           if (oldp == NULL) {
489                     /* always return full buffer size */
490                     *oldlenp = msgbufp->msg_bufs;
491                     return (0);
492           }
493 
494           sysctl_unlock();
495 
496           /*
497            * First, copy from the write pointer to the end of
498            * message buffer.
499            */
500           error = 0;
501           mutex_spin_enter(&log_lock);
502           maxlen = MIN(msgbufp->msg_bufs, *oldlenp);
503           beg = msgbufp->msg_bufx;
504           end = msgbufp->msg_bufs;
505           mutex_spin_exit(&log_lock);
506 
507           while (maxlen > 0) {
508                     len = MIN(end - beg, maxlen);
509                     if (len == 0)
510                               break;
511                     /* XXX unlocked, but hardly matters. */
512                     error = copyout(&msgbufp->msg_bufc[beg], where, len);
513                     ktrmibio(-1, UIO_READ, where, len, error);
514                     if (error)
515                               break;
516                     where += len;
517                     maxlen -= len;
518 
519                     /*
520                      * ... then, copy from the beginning of message buffer to
521                      * the write pointer.
522                      */
523                     beg = 0;
524                     end = msgbufp->msg_bufx;
525           }
526 
527           sysctl_relock();
528           return (error);
529 }
530 
531 const struct cdevsw log_cdevsw = {
532           .d_open = logopen,
533           .d_close = logclose,
534           .d_read = logread,
535           .d_write = nowrite,
536           .d_ioctl = logioctl,
537           .d_stop = nostop,
538           .d_tty = notty,
539           .d_poll = logpoll,
540           .d_mmap = nommap,
541           .d_kqfilter = logkqfilter,
542           .d_discard = nodiscard,
543           .d_flag = D_OTHER | D_MPSAFE
544 };
545