subr_log.c revision 1.46
1/*	$OpenBSD: subr_log.c,v 1.46 2016/06/08 11:11:47 bluhm Exp $	*/
2/*	$NetBSD: subr_log.c,v 1.11 1996/03/30 22:24:44 christos Exp $	*/
3
4/*
5 * Copyright (c) 1982, 1986, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	@(#)subr_log.c	8.1 (Berkeley) 6/10/93
33 */
34
35/*
36 * Error log buffer for kernel printf's.
37 */
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/proc.h>
42#include <sys/vnode.h>
43#include <sys/ioctl.h>
44#include <sys/msgbuf.h>
45#include <sys/file.h>
46#include <sys/tty.h>
47#include <sys/signalvar.h>
48#include <sys/syslog.h>
49#include <sys/poll.h>
50#include <sys/malloc.h>
51#include <sys/filedesc.h>
52#include <sys/socket.h>
53#include <sys/socketvar.h>
54
55#ifdef KTRACE
56#include <sys/ktrace.h>
57#endif
58
59#include <sys/mount.h>
60#include <sys/syscallargs.h>
61
62#include <dev/cons.h>
63
64#define LOG_RDPRI	(PZERO + 1)
65
66#define LOG_ASYNC	0x04
67#define LOG_RDWAIT	0x08
68
69struct logsoftc {
70	int	sc_state;		/* see above for possibilities */
71	struct	selinfo sc_selp;	/* process waiting on select call */
72	int	sc_pgid;		/* process/group for async I/O */
73	uid_t	sc_siguid;		/* uid for process that set sc_pgid */
74	uid_t	sc_sigeuid;		/* euid for process that set sc_pgid */
75} logsoftc;
76
77int	log_open;			/* also used in log() */
78int	msgbufmapped;			/* is the message buffer mapped */
79struct	msgbuf *msgbufp;		/* the mapped buffer, itself. */
80struct	msgbuf *consbufp;		/* console message buffer. */
81struct	file *syslogf;
82
83void filt_logrdetach(struct knote *kn);
84int filt_logread(struct knote *kn, long hint);
85
86struct filterops logread_filtops =
87	{ 1, NULL, filt_logrdetach, filt_logread};
88
89int dosendsyslog(struct proc *, const char *, size_t, int, enum uio_seg);
90
91void
92initmsgbuf(caddr_t buf, size_t bufsize)
93{
94	struct msgbuf *mbp;
95	long new_bufs;
96
97	/* Sanity-check the given size. */
98	if (bufsize < sizeof(struct msgbuf))
99		return;
100
101	mbp = msgbufp = (struct msgbuf *)buf;
102
103	new_bufs = bufsize - offsetof(struct msgbuf, msg_bufc);
104	if ((mbp->msg_magic != MSG_MAGIC) || (mbp->msg_bufs != new_bufs) ||
105	    (mbp->msg_bufr < 0) || (mbp->msg_bufr >= mbp->msg_bufs) ||
106	    (mbp->msg_bufx < 0) || (mbp->msg_bufx >= mbp->msg_bufs)) {
107		/*
108		 * If the buffer magic number is wrong, has changed
109		 * size (which shouldn't happen often), or is
110		 * internally inconsistent, initialize it.
111		 */
112
113		memset(buf, 0, bufsize);
114		mbp->msg_magic = MSG_MAGIC;
115		mbp->msg_bufs = new_bufs;
116	}
117
118	/* Always start new buffer data on a new line. */
119	if (mbp->msg_bufx > 0 && mbp->msg_bufc[mbp->msg_bufx - 1] != '\n')
120		msgbuf_putchar(msgbufp, '\n');
121
122	/* mark it as ready for use. */
123	msgbufmapped = 1;
124}
125
126void
127initconsbuf(void)
128{
129	long new_bufs;
130
131	/* Set up a buffer to collect /dev/console output */
132	consbufp = malloc(CONSBUFSIZE, M_TEMP, M_NOWAIT|M_ZERO);
133	if (consbufp) {
134		new_bufs = CONSBUFSIZE - offsetof(struct msgbuf, msg_bufc);
135		consbufp->msg_magic = MSG_MAGIC;
136		consbufp->msg_bufs = new_bufs;
137	}
138}
139
140void
141msgbuf_putchar(struct msgbuf *mbp, const char c)
142{
143	int s;
144
145	if (mbp->msg_magic != MSG_MAGIC)
146		/* Nothing we can do */
147		return;
148
149	s = splhigh();
150	mbp->msg_bufc[mbp->msg_bufx++] = c;
151	mbp->msg_bufl = lmin(mbp->msg_bufl+1, mbp->msg_bufs);
152	if (mbp->msg_bufx < 0 || mbp->msg_bufx >= mbp->msg_bufs)
153		mbp->msg_bufx = 0;
154	/* If the buffer is full, keep the most recent data. */
155	if (mbp->msg_bufr == mbp->msg_bufx) {
156		if (++mbp->msg_bufr >= mbp->msg_bufs)
157			mbp->msg_bufr = 0;
158	}
159	splx(s);
160}
161
162int
163logopen(dev_t dev, int flags, int mode, struct proc *p)
164{
165	if (log_open)
166		return (EBUSY);
167	log_open = 1;
168	return (0);
169}
170
171int
172logclose(dev_t dev, int flag, int mode, struct proc *p)
173{
174
175	if (syslogf)
176		FRELE(syslogf, p);
177	syslogf = NULL;
178	log_open = 0;
179	logsoftc.sc_state = 0;
180	return (0);
181}
182
183int
184logread(dev_t dev, struct uio *uio, int flag)
185{
186	struct msgbuf *mbp = msgbufp;
187	size_t l;
188	int s, error = 0;
189
190	s = splhigh();
191	while (mbp->msg_bufr == mbp->msg_bufx) {
192		if (flag & IO_NDELAY) {
193			error = EWOULDBLOCK;
194			goto out;
195		}
196		logsoftc.sc_state |= LOG_RDWAIT;
197		error = tsleep(mbp, LOG_RDPRI | PCATCH,
198			       "klog", 0);
199		if (error)
200			goto out;
201	}
202	logsoftc.sc_state &= ~LOG_RDWAIT;
203
204	while (uio->uio_resid > 0) {
205		if (mbp->msg_bufx >= mbp->msg_bufr)
206			l = mbp->msg_bufx - mbp->msg_bufr;
207		else
208			l = mbp->msg_bufs - mbp->msg_bufr;
209		l = ulmin(l, uio->uio_resid);
210		if (l == 0)
211			break;
212		error = uiomove(&mbp->msg_bufc[mbp->msg_bufr], l, uio);
213		if (error)
214			break;
215		mbp->msg_bufr += l;
216		if (mbp->msg_bufr < 0 || mbp->msg_bufr >= mbp->msg_bufs)
217			mbp->msg_bufr = 0;
218	}
219 out:
220	splx(s);
221	return (error);
222}
223
224int
225logpoll(dev_t dev, int events, struct proc *p)
226{
227	int s, revents = 0;
228
229	s = splhigh();
230	if (events & (POLLIN | POLLRDNORM)) {
231		if (msgbufp->msg_bufr != msgbufp->msg_bufx)
232			revents |= events & (POLLIN | POLLRDNORM);
233		else
234			selrecord(p, &logsoftc.sc_selp);
235	}
236	splx(s);
237	return (revents);
238}
239
240int
241logkqfilter(dev_t dev, struct knote *kn)
242{
243	struct klist *klist;
244	int s;
245
246	switch (kn->kn_filter) {
247	case EVFILT_READ:
248		klist = &logsoftc.sc_selp.si_note;
249		kn->kn_fop = &logread_filtops;
250		break;
251	default:
252		return (EINVAL);
253	}
254
255	kn->kn_hook = (void *)msgbufp;
256
257	s = splhigh();
258	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
259	splx(s);
260
261	return (0);
262}
263
264void
265filt_logrdetach(struct knote *kn)
266{
267	int s;
268
269	s = splhigh();
270	SLIST_REMOVE(&logsoftc.sc_selp.si_note, kn, knote, kn_selnext);
271	splx(s);
272}
273
274int
275filt_logread(struct knote *kn, long hint)
276{
277	struct  msgbuf *p = (struct  msgbuf *)kn->kn_hook;
278	int s, event = 0;
279
280	s = splhigh();
281	kn->kn_data = (int)(p->msg_bufx - p->msg_bufr);
282	event = (p->msg_bufx != p->msg_bufr);
283	splx(s);
284	return (event);
285}
286
287void
288logwakeup(void)
289{
290	if (!log_open)
291		return;
292	selwakeup(&logsoftc.sc_selp);
293	if (logsoftc.sc_state & LOG_ASYNC)
294		csignal(logsoftc.sc_pgid, SIGIO,
295		    logsoftc.sc_siguid, logsoftc.sc_sigeuid);
296	if (logsoftc.sc_state & LOG_RDWAIT) {
297		wakeup(msgbufp);
298		logsoftc.sc_state &= ~LOG_RDWAIT;
299	}
300}
301
302int
303logioctl(dev_t dev, u_long com, caddr_t data, int flag, struct proc *p)
304{
305	struct file *fp;
306	long l;
307	int error, s;
308
309	switch (com) {
310
311	/* return number of characters immediately available */
312	case FIONREAD:
313		s = splhigh();
314		l = msgbufp->msg_bufx - msgbufp->msg_bufr;
315		splx(s);
316		if (l < 0)
317			l += msgbufp->msg_bufs;
318		*(int *)data = l;
319		break;
320
321	case FIONBIO:
322		break;
323
324	case FIOASYNC:
325		if (*(int *)data)
326			logsoftc.sc_state |= LOG_ASYNC;
327		else
328			logsoftc.sc_state &= ~LOG_ASYNC;
329		break;
330
331	case TIOCSPGRP:
332		logsoftc.sc_pgid = *(int *)data;
333		logsoftc.sc_siguid = p->p_ucred->cr_ruid;
334		logsoftc.sc_sigeuid = p->p_ucred->cr_uid;
335		break;
336
337	case TIOCGPGRP:
338		*(int *)data = logsoftc.sc_pgid;
339		break;
340
341	case LIOCSFD:
342		if ((error = suser(p, 0)) != 0)
343			return (error);
344		if ((error = getsock(p, *(int *)data, &fp)) != 0)
345			return (error);
346		if (syslogf)
347			FRELE(syslogf, p);
348		syslogf = fp;
349		break;
350
351	default:
352		return (ENOTTY);
353	}
354	return (0);
355}
356
357int
358sys_sendsyslog(struct proc *p, void *v, register_t *retval)
359{
360	struct sys_sendsyslog_args /* {
361		syscallarg(const void *) buf;
362		syscallarg(size_t) nbyte;
363		syscallarg(int) flags;
364	} */ *uap = v;
365	int error;
366#ifndef SMALL_KERNEL
367	static int dropped_count, orig_error;
368	int len;
369	char buf[64];
370
371	if (dropped_count) {
372		len = snprintf(buf, sizeof(buf),
373		    "<%d>sendsyslog: dropped %d message%s, error %d",
374		    LOG_KERN|LOG_WARNING, dropped_count,
375		    dropped_count == 1 ? "" : "s", orig_error);
376		error = dosendsyslog(p, buf, MIN((size_t)len, sizeof(buf) - 1),
377		    0, UIO_SYSSPACE);
378		if (error == 0)
379			dropped_count = 0;
380	}
381#endif
382	error = dosendsyslog(p, SCARG(uap, buf), SCARG(uap, nbyte),
383	    SCARG(uap, flags), UIO_USERSPACE);
384#ifndef SMALL_KERNEL
385	if (error) {
386		dropped_count++;
387		orig_error = error;
388	}
389#endif
390	return (error);
391}
392
393int
394dosendsyslog(struct proc *p, const char *buf, size_t nbyte, int flags,
395    enum uio_seg sflg)
396{
397#ifdef KTRACE
398	struct iovec *ktriov = NULL;
399	int iovlen;
400#endif
401	char pri[6], *kbuf;
402	struct iovec aiov;
403	struct uio auio;
404	size_t i, len;
405	int error;
406
407	if (syslogf)
408		FREF(syslogf);
409	else if (!ISSET(flags, LOG_CONS))
410		return (ENOTCONN);
411	else {
412		/*
413		 * Strip off syslog priority when logging to console.
414		 * LOG_PRIMASK | LOG_FACMASK is 0x03ff, so at most 4
415		 * decimal digits may appear in priority as <1023>.
416		 */
417		len = MIN(nbyte, sizeof(pri));
418		if (sflg == UIO_USERSPACE) {
419			if ((error = copyin(buf, pri, len)))
420				return (error);
421		} else
422			memcpy(pri, buf, len);
423		if (0 < len && pri[0] == '<') {
424			for (i = 1; i < len; i++) {
425				if (pri[i] < '0' || pri[i] > '9')
426					break;
427			}
428			if (i < len && pri[i] == '>') {
429				i++;
430				/* There must be at least one digit <0>. */
431				if (i >= 3) {
432					buf += i;
433					nbyte -= i;
434				}
435			}
436		}
437	}
438
439	aiov.iov_base = (char *)buf;
440	aiov.iov_len = nbyte;
441	auio.uio_iov = &aiov;
442	auio.uio_iovcnt = 1;
443	auio.uio_segflg = sflg;
444	auio.uio_rw = UIO_WRITE;
445	auio.uio_procp = p;
446	auio.uio_offset = 0;
447	auio.uio_resid = aiov.iov_len;
448#ifdef KTRACE
449	if (KTRPOINT(p, KTR_GENIO)) {
450		ktriov = mallocarray(auio.uio_iovcnt, sizeof(struct iovec),
451		    M_TEMP, M_WAITOK);
452		iovlen = auio.uio_iovcnt * sizeof (struct iovec);
453
454		memcpy(ktriov, auio.uio_iov, iovlen);
455	}
456#endif
457
458	len = auio.uio_resid;
459	if (syslogf) {
460		error = sosend(syslogf->f_data, NULL, &auio, NULL, NULL, 0);
461		if (error == 0)
462			len -= auio.uio_resid;
463	} else if (constty || cn_devvp) {
464		error = cnwrite(0, &auio, 0);
465		if (error == 0)
466			len -= auio.uio_resid;
467		aiov.iov_base = "\r\n";
468		aiov.iov_len = 2;
469		auio.uio_iov = &aiov;
470		auio.uio_iovcnt = 1;
471		auio.uio_segflg = UIO_SYSSPACE;
472		auio.uio_rw = UIO_WRITE;
473		auio.uio_procp = p;
474		auio.uio_offset = 0;
475		auio.uio_resid = aiov.iov_len;
476		cnwrite(0, &auio, 0);
477	} else {
478		/* XXX console redirection breaks down... */
479		if (sflg == UIO_USERSPACE) {
480			kbuf = malloc(len, M_TEMP, M_WAITOK);
481			error = copyin(aiov.iov_base, kbuf, len);
482		} else {
483			kbuf = aiov.iov_base;
484			error = 0;
485		}
486		if (error == 0)
487			for (i = 0; i < len; i++) {
488				if (kbuf[i] == '\0')
489					break;
490				cnputc(kbuf[i]);
491				auio.uio_resid--;
492			}
493		if (sflg == UIO_USERSPACE)
494			free(kbuf, M_TEMP, len);
495		if (error == 0)
496			len -= auio.uio_resid;
497		cnputc('\n');
498	}
499
500#ifdef KTRACE
501	if (ktriov != NULL) {
502		if (error == 0)
503			ktrgenio(p, -1, UIO_WRITE, ktriov, len);
504		free(ktriov, M_TEMP, iovlen);
505	}
506#endif
507	if (syslogf)
508		FRELE(syslogf, p);
509	else
510		error = ENOTCONN;
511	return (error);
512}
513