1 /* $NetBSD: subr_log.c,v 1.44 2007/12/05 17:19:58 pooka Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Copyright (c) 1982, 1986, 1993 41 * The Regents of the University of California. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. Neither the name of the University nor the names of its contributors 52 * may be used to endorse or promote products derived from this software 53 * without specific prior written permission. 54 * 55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 65 * SUCH DAMAGE. 66 * 67 * @(#)subr_log.c 8.3 (Berkeley) 2/14/95 68 */ 69 70 /* 71 * Error log buffer for kernel printf's. 72 */ 73 74 #include <sys/cdefs.h> 75 __KERNEL_RCSID(0, "$NetBSD: subr_log.c,v 1.44 2007/12/05 17:19:58 pooka Exp $"); 76 77 #include <sys/param.h> 78 #include <sys/systm.h> 79 #include <sys/kernel.h> 80 #include <sys/proc.h> 81 #include <sys/vnode.h> 82 #include <sys/ioctl.h> 83 #include <sys/msgbuf.h> 84 #include <sys/file.h> 85 #include <sys/syslog.h> 86 #include <sys/conf.h> 87 #include <sys/select.h> 88 #include <sys/poll.h> 89 #include <sys/intr.h> 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 kmutex_t log_lock; 98 static void *log_sih; 99 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 147 /*ARGSUSED*/ 148 static int 149 logopen(dev_t dev, int flags, int mode, struct lwp *l) 150 { 151 struct kern_msgbuf *mbp = msgbufp; 152 int error = 0; 153 154 mutex_spin_enter(&log_lock); 155 if (log_open) { 156 error = EBUSY; 157 } else { 158 log_open = 1; 159 log_pgid = l->l_proc->p_pid; /* signal process only */ 160 /* 161 * The message buffer is initialized during system 162 * configuration. If it's been clobbered, note that 163 * and return an error. (This allows a user to read 164 * the buffer via /dev/kmem, and try to figure out 165 * what clobbered it. 166 */ 167 if (mbp->msg_magic != MSG_MAGIC) { 168 msgbufenabled = 0; 169 error = ENXIO; 170 } 171 } 172 mutex_spin_exit(&log_lock); 173 174 return error; 175 } 176 177 /*ARGSUSED*/ 178 static int 179 logclose(dev_t dev, int flag, int mode, struct lwp *l) 180 { 181 182 mutex_spin_enter(&log_lock); 183 log_pgid = 0; 184 log_open = 0; 185 log_async = 0; 186 mutex_spin_exit(&log_lock); 187 188 return 0; 189 } 190 191 /*ARGSUSED*/ 192 static int 193 logread(dev_t dev, struct uio *uio, int flag) 194 { 195 struct kern_msgbuf *mbp = msgbufp; 196 long l; 197 int error = 0; 198 199 mutex_spin_enter(&log_lock); 200 while (mbp->msg_bufr == mbp->msg_bufx) { 201 if (flag & IO_NDELAY) { 202 mutex_spin_exit(&log_lock); 203 return EWOULDBLOCK; 204 } 205 error = cv_wait_sig(&log_cv, &log_lock); 206 if (error) { 207 mutex_spin_exit(&log_lock); 208 return error; 209 } 210 } 211 while (uio->uio_resid > 0) { 212 l = mbp->msg_bufx - mbp->msg_bufr; 213 if (l < 0) 214 l = mbp->msg_bufs - mbp->msg_bufr; 215 l = min(l, uio->uio_resid); 216 if (l == 0) 217 break; 218 mutex_spin_exit(&log_lock); 219 error = uiomove(&mbp->msg_bufc[mbp->msg_bufr], (int)l, uio); 220 mutex_spin_enter(&log_lock); 221 if (error) 222 break; 223 mbp->msg_bufr += l; 224 if (mbp->msg_bufr < 0 || mbp->msg_bufr >= mbp->msg_bufs) 225 mbp->msg_bufr = 0; 226 } 227 mutex_spin_exit(&log_lock); 228 229 return error; 230 } 231 232 /*ARGSUSED*/ 233 static int 234 logpoll(dev_t dev, int events, struct lwp *l) 235 { 236 int revents = 0; 237 238 if (events & (POLLIN | POLLRDNORM)) { 239 mutex_spin_enter(&log_lock); 240 if (msgbufp->msg_bufr != msgbufp->msg_bufx) 241 revents |= events & (POLLIN | POLLRDNORM); 242 else 243 selrecord(l, &log_selp); 244 mutex_spin_exit(&log_lock); 245 } 246 247 return revents; 248 } 249 250 static void 251 filt_logrdetach(struct knote *kn) 252 { 253 254 mutex_spin_enter(&log_lock); 255 SLIST_REMOVE(&log_selp.sel_klist, kn, knote, kn_selnext); 256 mutex_spin_exit(&log_lock); 257 } 258 259 static int 260 filt_logread(struct knote *kn, long hint) 261 { 262 int rv; 263 264 if ((hint & NOTE_SUBMIT) == 0) 265 mutex_spin_enter(&log_lock); 266 if (msgbufp->msg_bufr == msgbufp->msg_bufx) { 267 rv = 0; 268 } else if (msgbufp->msg_bufr < msgbufp->msg_bufx) { 269 kn->kn_data = msgbufp->msg_bufx - msgbufp->msg_bufr; 270 rv = 1; 271 } else { 272 kn->kn_data = (msgbufp->msg_bufs - msgbufp->msg_bufr) + 273 msgbufp->msg_bufx; 274 rv = 1; 275 } 276 if ((hint & NOTE_SUBMIT) == 0) 277 mutex_spin_exit(&log_lock); 278 279 return rv; 280 } 281 282 static const struct filterops logread_filtops = 283 { 1, NULL, filt_logrdetach, filt_logread }; 284 285 static int 286 logkqfilter(dev_t dev, struct knote *kn) 287 { 288 struct klist *klist; 289 290 switch (kn->kn_filter) { 291 case EVFILT_READ: 292 klist = &log_selp.sel_klist; 293 kn->kn_fop = &logread_filtops; 294 break; 295 296 default: 297 return (EINVAL); 298 } 299 300 mutex_spin_enter(&log_lock); 301 kn->kn_hook = NULL; 302 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 303 mutex_spin_exit(&log_lock); 304 305 return (0); 306 } 307 308 void 309 logwakeup(void) 310 { 311 312 if (!cold && log_open) { 313 mutex_spin_enter(&log_lock); 314 selnotify(&log_selp, NOTE_SUBMIT); 315 if (log_async) 316 softint_schedule(log_sih); 317 cv_broadcast(&log_cv); 318 mutex_spin_exit(&log_lock); 319 } 320 } 321 322 static void 323 logsoftintr(void *cookie) 324 { 325 pid_t pid; 326 327 if ((pid = log_pgid) != 0) 328 fownsignal(pid, SIGIO, 0, 0, NULL); 329 } 330 331 /*ARGSUSED*/ 332 static int 333 logioctl(dev_t dev, u_long com, void *data, int flag, struct lwp *lwp) 334 { 335 struct proc *p = lwp->l_proc; 336 long l; 337 338 switch (com) { 339 340 /* return number of characters immediately available */ 341 case FIONREAD: 342 mutex_spin_enter(&log_lock); 343 l = msgbufp->msg_bufx - msgbufp->msg_bufr; 344 if (l < 0) 345 l += msgbufp->msg_bufs; 346 mutex_spin_exit(&log_lock); 347 *(int *)data = l; 348 break; 349 350 case FIONBIO: 351 break; 352 353 case FIOASYNC: 354 /* No locking needed, 'thread private'. */ 355 log_async = (*((int *)data) != 0); 356 break; 357 358 case TIOCSPGRP: 359 case FIOSETOWN: 360 return fsetown(p, &log_pgid, com, data); 361 362 case TIOCGPGRP: 363 case FIOGETOWN: 364 return fgetown(p, log_pgid, com, data); 365 366 default: 367 return (EPASSTHROUGH); 368 } 369 return (0); 370 } 371 372 void 373 logputchar(int c) 374 { 375 struct kern_msgbuf *mbp; 376 377 if (!cold) 378 mutex_spin_enter(&log_lock); 379 if (msgbufenabled) { 380 mbp = msgbufp; 381 if (mbp->msg_magic != MSG_MAGIC) { 382 /* 383 * Arguably should panic or somehow notify the 384 * user... but how? Panic may be too drastic, 385 * and would obliterate the message being kicked 386 * out (maybe a panic itself), and printf 387 * would invoke us recursively. Silently punt 388 * for now. If syslog is running, it should 389 * notice. 390 */ 391 msgbufenabled = 0; 392 } else { 393 mbp->msg_bufc[mbp->msg_bufx++] = c; 394 if (mbp->msg_bufx < 0 || mbp->msg_bufx >= mbp->msg_bufs) 395 mbp->msg_bufx = 0; 396 /* If the buffer is full, keep the most recent data. */ 397 if (mbp->msg_bufr == mbp->msg_bufx) { 398 if (++mbp->msg_bufr >= mbp->msg_bufs) 399 mbp->msg_bufr = 0; 400 } 401 } 402 } 403 if (!cold) 404 mutex_spin_exit(&log_lock); 405 } 406 407 const struct cdevsw log_cdevsw = { 408 logopen, logclose, logread, nowrite, logioctl, 409 nostop, notty, logpoll, nommap, logkqfilter, 410 D_OTHER | D_MPSAFE 411 }; 412