1 /* 2 * Copyright (c) 1982, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#)subr_log.c 8.1 (Berkeley) 6/10/93 30 * $FreeBSD: src/sys/kern/subr_log.c,v 1.39.2.2 2001/06/02 08:11:25 phk Exp $ 31 */ 32 33 /* 34 * Error log buffer for kernel printf's. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/conf.h> 40 #include <sys/device.h> 41 #include <sys/proc.h> 42 #include <sys/vnode.h> 43 #include <sys/filio.h> 44 #include <sys/ttycom.h> 45 #include <sys/msgbuf.h> 46 #include <sys/signalvar.h> 47 #include <sys/kernel.h> 48 #include <sys/event.h> 49 #include <sys/filedesc.h> 50 #include <sys/sysctl.h> 51 #include <sys/thread2.h> 52 53 #define LOG_ASYNC 0x04 54 #define LOG_RDWAIT 0x08 55 56 static d_open_t logopen; 57 static d_close_t logclose; 58 static d_read_t logread; 59 static d_ioctl_t logioctl; 60 static d_kqfilter_t logkqfilter; 61 62 static void logtimeout(void *arg); 63 static void logfiltdetach(struct knote *kn); 64 static int logfiltread(struct knote *kn, long hint); 65 66 #define CDEV_MAJOR 7 67 static struct dev_ops log_ops = { 68 { "log", 0, 0 }, 69 .d_open = logopen, 70 .d_close = logclose, 71 .d_read = logread, 72 .d_ioctl = logioctl, 73 .d_kqfilter = logkqfilter 74 }; 75 76 static struct logsoftc { 77 int sc_state; /* see above for possibilities */ 78 struct kqinfo sc_kqp; /* processes waiting on I/O */ 79 struct sigio *sc_sigio; /* information for async I/O */ 80 struct callout sc_callout; /* callout to wakeup syslog */ 81 } logsoftc; 82 83 int log_open; /* also used in log() */ 84 85 /* Times per second to check for a pending syslog wakeup. */ 86 static int log_wakeups_per_second = 5; 87 SYSCTL_INT(_kern, OID_AUTO, log_wakeups_per_second, CTLFLAG_RW, 88 &log_wakeups_per_second, 0, ""); 89 90 /*ARGSUSED*/ 91 static int 92 logopen(struct dev_open_args *ap) 93 { 94 struct proc *p = curproc; 95 96 KKASSERT(p != NULL); 97 if (log_open) 98 return (EBUSY); 99 log_open = 1; 100 callout_init_mp(&logsoftc.sc_callout); 101 fsetown(p->p_pid, &logsoftc.sc_sigio); /* signal process only */ 102 callout_reset(&logsoftc.sc_callout, hz / log_wakeups_per_second, 103 logtimeout, NULL); 104 return (0); 105 } 106 107 /*ARGSUSED*/ 108 static int 109 logclose(struct dev_close_args *ap) 110 { 111 log_open = 0; 112 callout_stop(&logsoftc.sc_callout); 113 logsoftc.sc_state = 0; 114 funsetown(&logsoftc.sc_sigio); 115 return (0); 116 } 117 118 /*ARGSUSED*/ 119 static int 120 logread(struct dev_read_args *ap) 121 { 122 struct uio *uio = ap->a_uio; 123 struct msgbuf *mbp = msgbufp; 124 long l; 125 int error = 0; 126 127 crit_enter(); 128 while (mbp->msg_bufr == mbp->msg_bufx) { 129 if (ap->a_ioflag & IO_NDELAY) { 130 crit_exit(); 131 return (EWOULDBLOCK); 132 } 133 logsoftc.sc_state |= LOG_RDWAIT; 134 if ((error = tsleep((caddr_t)mbp, PCATCH, "klog", 0))) { 135 crit_exit(); 136 return (error); 137 } 138 } 139 crit_exit(); 140 logsoftc.sc_state &= ~LOG_RDWAIT; 141 142 while (uio->uio_resid > 0) { 143 l = (long)mbp->msg_bufx - (long)mbp->msg_bufr; 144 if (l < 0) 145 l = mbp->msg_size - mbp->msg_bufr; 146 l = (long)szmin(l, uio->uio_resid); 147 if (l == 0) 148 break; 149 error = uiomove((caddr_t)msgbufp->msg_ptr + mbp->msg_bufr, 150 (size_t)l, uio); 151 if (error) 152 break; 153 mbp->msg_bufr += l; 154 if (mbp->msg_bufr >= mbp->msg_size) 155 mbp->msg_bufr = 0; 156 } 157 return (error); 158 } 159 160 static struct filterops logread_filtops = 161 { FILTEROP_ISFD, NULL, logfiltdetach, logfiltread }; 162 163 static int 164 logkqfilter(struct dev_kqfilter_args *ap) 165 { 166 struct knote *kn = ap->a_kn; 167 struct klist *klist = &logsoftc.sc_kqp.ki_note; 168 169 ap->a_result = 0; 170 switch (kn->kn_filter) { 171 case EVFILT_READ: 172 kn->kn_fop = &logread_filtops; 173 break; 174 default: 175 ap->a_result = EOPNOTSUPP; 176 return (0); 177 } 178 179 knote_insert(klist, kn); 180 181 return (0); 182 } 183 184 static void 185 logfiltdetach(struct knote *kn) 186 { 187 struct klist *klist = &logsoftc.sc_kqp.ki_note; 188 189 knote_remove(klist, kn); 190 } 191 192 static int 193 logfiltread(struct knote *kn, long hint) 194 { 195 int ret = 0; 196 197 crit_enter(); 198 if (msgbufp->msg_bufr != msgbufp->msg_bufx) 199 ret = 1; 200 crit_exit(); 201 202 return (ret); 203 } 204 205 static void 206 logtimeout(void *arg) 207 { 208 if (!log_open) 209 return; 210 if (msgbuftrigger == 0) { 211 callout_reset(&logsoftc.sc_callout, 212 hz / log_wakeups_per_second, logtimeout, NULL); 213 return; 214 } 215 msgbuftrigger = 0; 216 KNOTE(&logsoftc.sc_kqp.ki_note, 0); 217 if ((logsoftc.sc_state & LOG_ASYNC) && logsoftc.sc_sigio != NULL) 218 pgsigio(logsoftc.sc_sigio, SIGIO, 0); 219 if (logsoftc.sc_state & LOG_RDWAIT) { 220 wakeup((caddr_t)msgbufp); 221 logsoftc.sc_state &= ~LOG_RDWAIT; 222 } 223 callout_reset(&logsoftc.sc_callout, hz / log_wakeups_per_second, 224 logtimeout, NULL); 225 } 226 227 /*ARGSUSED*/ 228 static int 229 logioctl(struct dev_ioctl_args *ap) 230 { 231 long l; 232 233 switch (ap->a_cmd) { 234 case FIONREAD: 235 /* return number of characters immediately available */ 236 crit_enter(); 237 l = msgbufp->msg_bufx - msgbufp->msg_bufr; 238 crit_exit(); 239 if (l < 0) 240 l += msgbufp->msg_size; 241 *(int *)ap->a_data = l; 242 break; 243 244 case FIOASYNC: 245 if (*(int *)ap->a_data) 246 logsoftc.sc_state |= LOG_ASYNC; 247 else 248 logsoftc.sc_state &= ~LOG_ASYNC; 249 break; 250 251 case FIOSETOWN: 252 return (fsetown(*(int *)ap->a_data, &logsoftc.sc_sigio)); 253 254 case FIOGETOWN: 255 *(int *)ap->a_data = fgetown(&logsoftc.sc_sigio); 256 break; 257 258 /* This is deprecated, FIOSETOWN should be used instead. */ 259 case TIOCSPGRP: 260 return (fsetown(-(*(int *)ap->a_data), &logsoftc.sc_sigio)); 261 262 /* This is deprecated, FIOGETOWN should be used instead */ 263 case TIOCGPGRP: 264 *(int *)ap->a_data = -fgetown(&logsoftc.sc_sigio); 265 break; 266 267 default: 268 return (ENOTTY); 269 } 270 return (0); 271 } 272 273 static void 274 log_drvinit(void *unused) 275 { 276 make_dev(&log_ops, 0, UID_ROOT, GID_WHEEL, 0600, "klog"); 277 } 278 279 SYSINIT(logdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,log_drvinit,NULL) 280