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