xref: /netbsd-src/sys/kern/subr_log.c (revision 313c6c94c424eed90c7b7e494aa83308a0a5d0ce)
1 /*	$NetBSD: subr_log.c,v 1.50 2008/04/28 20:24:04 martin 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.50 2008/04/28 20:24:04 martin 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 
84 static void	logsoftintr(void *);
85 
86 static bool	log_async;
87 static struct selinfo log_selp;		/* process waiting on select call */
88 static pid_t	log_pgid;		/* process/group for async I/O */
89 static kcondvar_t log_cv;
90 static void	*log_sih;
91 
92 kmutex_t log_lock;
93 int	log_open;			/* also used in log() */
94 int	msgbufmapped;			/* is the message buffer mapped */
95 int	msgbufenabled;			/* is logging to the buffer enabled */
96 struct	kern_msgbuf *msgbufp;		/* the mapped buffer, itself. */
97 
98 void
99 initmsgbuf(void *bf, size_t bufsize)
100 {
101 	struct kern_msgbuf *mbp;
102 	long new_bufs;
103 
104 	/* Sanity-check the given size. */
105 	if (bufsize < sizeof(struct kern_msgbuf))
106 		return;
107 
108 	mbp = msgbufp = (struct kern_msgbuf *)bf;
109 
110 	new_bufs = bufsize - offsetof(struct kern_msgbuf, msg_bufc);
111 	if ((mbp->msg_magic != MSG_MAGIC) || (mbp->msg_bufs != new_bufs) ||
112 	    (mbp->msg_bufr < 0) || (mbp->msg_bufr >= mbp->msg_bufs) ||
113 	    (mbp->msg_bufx < 0) || (mbp->msg_bufx >= mbp->msg_bufs)) {
114 		/*
115 		 * If the buffer magic number is wrong, has changed
116 		 * size (which shouldn't happen often), or is
117 		 * internally inconsistent, initialize it.
118 		 */
119 
120 		memset(bf, 0, bufsize);
121 		mbp->msg_magic = MSG_MAGIC;
122 		mbp->msg_bufs = new_bufs;
123 	}
124 
125 	/* mark it as ready for use. */
126 	msgbufmapped = msgbufenabled = 1;
127 }
128 
129 void
130 loginit(void)
131 {
132 
133 	mutex_init(&log_lock, MUTEX_DEFAULT, IPL_VM);
134 	selinit(&log_selp);
135 	cv_init(&log_cv, "klog");
136 	log_sih = softint_establish(SOFTINT_CLOCK | SOFTINT_MPSAFE,
137 	    logsoftintr, NULL);
138 }
139 
140 /*ARGSUSED*/
141 static int
142 logopen(dev_t dev, int flags, int mode, struct lwp *l)
143 {
144 	struct kern_msgbuf *mbp = msgbufp;
145 	int error = 0;
146 
147 	mutex_spin_enter(&log_lock);
148 	if (log_open) {
149 		error = EBUSY;
150 	} else {
151 		log_open = 1;
152 		log_pgid = l->l_proc->p_pid;	/* signal process only */
153 		/*
154 		 * The message buffer is initialized during system
155 		 * configuration.  If it's been clobbered, note that
156 		 * and return an error.  (This allows a user to read
157 		 * the buffer via /dev/kmem, and try to figure out
158 		 * what clobbered it.
159 		 */
160 		if (mbp->msg_magic != MSG_MAGIC) {
161 			msgbufenabled = 0;
162 			error = ENXIO;
163 		}
164 	}
165 	mutex_spin_exit(&log_lock);
166 
167 	return error;
168 }
169 
170 /*ARGSUSED*/
171 static int
172 logclose(dev_t dev, int flag, int mode, struct lwp *l)
173 {
174 
175 	mutex_spin_enter(&log_lock);
176 	log_pgid = 0;
177 	log_open = 0;
178 	log_async = 0;
179 	mutex_spin_exit(&log_lock);
180 
181 	return 0;
182 }
183 
184 /*ARGSUSED*/
185 static int
186 logread(dev_t dev, struct uio *uio, int flag)
187 {
188 	struct kern_msgbuf *mbp = msgbufp;
189 	long l;
190 	int error = 0;
191 
192 	mutex_spin_enter(&log_lock);
193 	while (mbp->msg_bufr == mbp->msg_bufx) {
194 		if (flag & IO_NDELAY) {
195 			mutex_spin_exit(&log_lock);
196 			return EWOULDBLOCK;
197 		}
198 		error = cv_wait_sig(&log_cv, &log_lock);
199 		if (error) {
200 			mutex_spin_exit(&log_lock);
201 			return error;
202 		}
203 	}
204 	while (uio->uio_resid > 0) {
205 		l = mbp->msg_bufx - mbp->msg_bufr;
206 		if (l < 0)
207 			l = mbp->msg_bufs - mbp->msg_bufr;
208 		l = min(l, uio->uio_resid);
209 		if (l == 0)
210 			break;
211 		mutex_spin_exit(&log_lock);
212 		error = uiomove(&mbp->msg_bufc[mbp->msg_bufr], (int)l, uio);
213 		mutex_spin_enter(&log_lock);
214 		if (error)
215 			break;
216 		mbp->msg_bufr += l;
217 		if (mbp->msg_bufr < 0 || mbp->msg_bufr >= mbp->msg_bufs)
218 			mbp->msg_bufr = 0;
219 	}
220 	mutex_spin_exit(&log_lock);
221 
222 	return error;
223 }
224 
225 /*ARGSUSED*/
226 static int
227 logpoll(dev_t dev, int events, struct lwp *l)
228 {
229 	int revents = 0;
230 
231 	if (events & (POLLIN | POLLRDNORM)) {
232 		mutex_spin_enter(&log_lock);
233 		if (msgbufp->msg_bufr != msgbufp->msg_bufx)
234 			revents |= events & (POLLIN | POLLRDNORM);
235 		else
236 			selrecord(l, &log_selp);
237 		mutex_spin_exit(&log_lock);
238 	}
239 
240 	return revents;
241 }
242 
243 static void
244 filt_logrdetach(struct knote *kn)
245 {
246 
247 	mutex_spin_enter(&log_lock);
248 	SLIST_REMOVE(&log_selp.sel_klist, kn, knote, kn_selnext);
249 	mutex_spin_exit(&log_lock);
250 }
251 
252 static int
253 filt_logread(struct knote *kn, long hint)
254 {
255 	int rv;
256 
257 	if ((hint & NOTE_SUBMIT) == 0)
258 		mutex_spin_enter(&log_lock);
259 	if (msgbufp->msg_bufr == msgbufp->msg_bufx) {
260 		rv = 0;
261 	} else if (msgbufp->msg_bufr < msgbufp->msg_bufx) {
262 		kn->kn_data = msgbufp->msg_bufx - msgbufp->msg_bufr;
263 		rv = 1;
264 	} else {
265 		kn->kn_data = (msgbufp->msg_bufs - msgbufp->msg_bufr) +
266 		    msgbufp->msg_bufx;
267 		rv = 1;
268 	}
269 	if ((hint & NOTE_SUBMIT) == 0)
270 		mutex_spin_exit(&log_lock);
271 
272 	return rv;
273 }
274 
275 static const struct filterops logread_filtops =
276 	{ 1, NULL, filt_logrdetach, filt_logread };
277 
278 static int
279 logkqfilter(dev_t dev, struct knote *kn)
280 {
281 	struct klist *klist;
282 
283 	switch (kn->kn_filter) {
284 	case EVFILT_READ:
285 		klist = &log_selp.sel_klist;
286 		kn->kn_fop = &logread_filtops;
287 		break;
288 
289 	default:
290 		return (EINVAL);
291 	}
292 
293 	mutex_spin_enter(&log_lock);
294 	kn->kn_hook = NULL;
295 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
296 	mutex_spin_exit(&log_lock);
297 
298 	return (0);
299 }
300 
301 void
302 logwakeup(void)
303 {
304 
305 	if (!cold && log_open) {
306 		mutex_spin_enter(&log_lock);
307 		selnotify(&log_selp, 0, NOTE_SUBMIT);
308 		if (log_async)
309 			softint_schedule(log_sih);
310 		cv_broadcast(&log_cv);
311 		mutex_spin_exit(&log_lock);
312 	}
313 }
314 
315 static void
316 logsoftintr(void *cookie)
317 {
318 	pid_t pid;
319 
320 	if ((pid = log_pgid) != 0)
321 		fownsignal(pid, SIGIO, 0, 0, NULL);
322 }
323 
324 /*ARGSUSED*/
325 static int
326 logioctl(dev_t dev, u_long com, void *data, int flag, struct lwp *lwp)
327 {
328 	long l;
329 
330 	switch (com) {
331 
332 	/* return number of characters immediately available */
333 	case FIONREAD:
334 		mutex_spin_enter(&log_lock);
335 		l = msgbufp->msg_bufx - msgbufp->msg_bufr;
336 		if (l < 0)
337 			l += msgbufp->msg_bufs;
338 		mutex_spin_exit(&log_lock);
339 		*(int *)data = l;
340 		break;
341 
342 	case FIONBIO:
343 		break;
344 
345 	case FIOASYNC:
346 		/* No locking needed, 'thread private'. */
347 		log_async = (*((int *)data) != 0);
348 		break;
349 
350 	case TIOCSPGRP:
351 	case FIOSETOWN:
352 		return fsetown(&log_pgid, com, data);
353 
354 	case TIOCGPGRP:
355 	case FIOGETOWN:
356 		return fgetown(log_pgid, com, data);
357 
358 	default:
359 		return (EPASSTHROUGH);
360 	}
361 	return (0);
362 }
363 
364 void
365 logputchar(int c)
366 {
367 	struct kern_msgbuf *mbp;
368 
369 	if (!cold)
370 		mutex_spin_enter(&log_lock);
371 	if (msgbufenabled) {
372 		mbp = msgbufp;
373 		if (mbp->msg_magic != MSG_MAGIC) {
374 			/*
375 			 * Arguably should panic or somehow notify the
376 			 * user...  but how?  Panic may be too drastic,
377 			 * and would obliterate the message being kicked
378 			 * out (maybe a panic itself), and printf
379 			 * would invoke us recursively.  Silently punt
380 			 * for now.  If syslog is running, it should
381 			 * notice.
382 			 */
383 			msgbufenabled = 0;
384 		} else {
385 			mbp->msg_bufc[mbp->msg_bufx++] = c;
386 			if (mbp->msg_bufx < 0 || mbp->msg_bufx >= mbp->msg_bufs)
387 				mbp->msg_bufx = 0;
388 			/* If the buffer is full, keep the most recent data. */
389 			if (mbp->msg_bufr == mbp->msg_bufx) {
390 				 if (++mbp->msg_bufr >= mbp->msg_bufs)
391 					mbp->msg_bufr = 0;
392 			}
393 		}
394 	}
395 	if (!cold)
396 		mutex_spin_exit(&log_lock);
397 }
398 
399 const struct cdevsw log_cdevsw = {
400 	logopen, logclose, logread, nowrite, logioctl,
401 	nostop, notty, logpoll, nommap, logkqfilter,
402 	D_OTHER | D_MPSAFE
403 };
404