subr_log.c revision 1.42 1 /* $NetBSD: subr_log.c,v 1.42 2007/11/07 00:19:08 ad 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.42 2007/11/07 00:19:08 ad 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_SPIN, 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 (1);
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