1 /* $NetBSD: cons.c,v 1.71 2014/03/16 05:20:26 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: Utah $Hdr: cons.c 1.7 92/01/21$ 37 * 38 * @(#)cons.c 8.2 (Berkeley) 1/12/94 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: cons.c,v 1.71 2014/03/16 05:20:26 dholland Exp $"); 43 44 #include <sys/param.h> 45 #include <sys/proc.h> 46 #include <sys/systm.h> 47 #include <sys/buf.h> 48 #include <sys/ioctl.h> 49 #include <sys/poll.h> 50 #include <sys/tty.h> 51 #include <sys/file.h> 52 #include <sys/conf.h> 53 #include <sys/vnode.h> 54 #include <sys/kauth.h> 55 #include <sys/mutex.h> 56 57 #include <dev/cons.h> 58 59 dev_type_open(cnopen); 60 dev_type_close(cnclose); 61 dev_type_read(cnread); 62 dev_type_write(cnwrite); 63 dev_type_ioctl(cnioctl); 64 dev_type_poll(cnpoll); 65 dev_type_kqfilter(cnkqfilter); 66 67 static bool cn_redirect(dev_t *, int, int *); 68 69 const struct cdevsw cons_cdevsw = { 70 .d_open = cnopen, 71 .d_close = cnclose, 72 .d_read = cnread, 73 .d_write = cnwrite, 74 .d_ioctl = cnioctl, 75 .d_stop = nostop, 76 .d_tty = notty, 77 .d_poll = cnpoll, 78 .d_mmap = nommap, 79 .d_kqfilter = cnkqfilter, 80 .d_flag = D_TTY 81 }; 82 83 struct tty *constty = NULL; /* virtual console output device */ 84 struct consdev *cn_tab; /* physical console device info */ 85 struct vnode *cn_devvp[2]; /* vnode for underlying device. */ 86 87 int 88 cnopen(dev_t dev, int flag, int mode, struct lwp *l) 89 { 90 dev_t cndev; 91 int unit, error; 92 93 unit = minor(dev); 94 if (unit > 1) 95 return ENODEV; 96 97 if (cn_tab == NULL) 98 return (0); 99 100 /* 101 * always open the 'real' console device, so we don't get nailed 102 * later. This follows normal device semantics; they always get 103 * open() calls. 104 */ 105 cndev = cn_tab->cn_dev; 106 if (cndev == NODEV) { 107 /* 108 * This is most likely an error in the console attach 109 * code. Panicking looks better than jumping into nowhere 110 * through cdevsw below.... 111 */ 112 panic("cnopen: no console device"); 113 } 114 if (dev == cndev) { 115 /* 116 * This causes cnopen() to be called recursively, which 117 * is generally a bad thing. It is often caused when 118 * dev == 0 and cn_dev has not been set, but was probably 119 * initialised to 0. 120 */ 121 panic("cnopen: cn_tab->cn_dev == dev"); 122 } 123 if (cn_devvp[unit] != NULLVP) 124 return 0; 125 if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0) 126 printf("cnopen: unable to get vnode reference\n"); 127 error = vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY); 128 if (error == 0) { 129 error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get()); 130 VOP_UNLOCK(cn_devvp[unit]); 131 } 132 return error; 133 } 134 135 int 136 cnclose(dev_t dev, int flag, int mode, struct lwp *l) 137 { 138 struct vnode *vp; 139 int unit, error; 140 141 unit = minor(dev); 142 143 if (cn_tab == NULL) 144 return (0); 145 146 vp = cn_devvp[unit]; 147 cn_devvp[unit] = NULL; 148 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 149 if (error == 0) { 150 error = VOP_CLOSE(vp, flag, kauth_cred_get()); 151 VOP_UNLOCK(vp); 152 } 153 return error; 154 } 155 156 int 157 cnread(dev_t dev, struct uio *uio, int flag) 158 { 159 int error; 160 161 /* 162 * If we would redirect input, punt. This will keep strange 163 * things from happening to people who are using the real 164 * console. Nothing should be using /dev/console for 165 * input (except a shell in single-user mode, but then, 166 * one wouldn't TIOCCONS then). 167 */ 168 if (!cn_redirect(&dev, 1, &error)) 169 return error; 170 return cdev_read(dev, uio, flag); 171 } 172 173 int 174 cnwrite(dev_t dev, struct uio *uio, int flag) 175 { 176 int error; 177 178 /* Redirect output, if that's appropriate. */ 179 if (!cn_redirect(&dev, 0, &error)) 180 return error; 181 return cdev_write(dev, uio, flag); 182 } 183 184 int 185 cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 186 { 187 int error; 188 189 error = 0; 190 191 /* 192 * Superuser can always use this to wrest control of console 193 * output from the "virtual" console. 194 */ 195 if (cmd == TIOCCONS && constty != NULL) { 196 error = kauth_authorize_device_tty(l->l_cred, 197 KAUTH_DEVICE_TTY_VIRTUAL, constty); 198 if (!error) 199 constty = NULL; 200 return (error); 201 } 202 203 /* 204 * Redirect the ioctl, if that's appropriate. 205 * Note that strange things can happen, if a program does 206 * ioctls on /dev/console, then the console is redirected 207 * out from under it. 208 */ 209 if (!cn_redirect(&dev, 0, &error)) 210 return error; 211 return cdev_ioctl(dev, cmd, data, flag, l); 212 } 213 214 /*ARGSUSED*/ 215 int 216 cnpoll(dev_t dev, int events, struct lwp *l) 217 { 218 int error; 219 220 /* 221 * Redirect the poll, if that's appropriate. 222 * I don't want to think of the possible side effects 223 * of console redirection here. 224 */ 225 if (!cn_redirect(&dev, 0, &error)) 226 return POLLHUP; 227 return cdev_poll(dev, events, l); 228 } 229 230 /*ARGSUSED*/ 231 int 232 cnkqfilter(dev_t dev, struct knote *kn) 233 { 234 int error; 235 236 /* 237 * Redirect the kqfilter, if that's appropriate. 238 * I don't want to think of the possible side effects 239 * of console redirection here. 240 */ 241 if (!cn_redirect(&dev, 0, &error)) 242 return error; 243 return cdev_kqfilter(dev, kn); 244 } 245 246 int 247 cngetc(void) 248 { 249 if (cn_tab == NULL) 250 return (0); 251 int s = splhigh(); 252 for (;;) { 253 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev); 254 if (rv >= 0) { 255 splx(s); 256 return rv; 257 } 258 docritpollhooks(); 259 } 260 } 261 262 int 263 cngetsn(char *cp, int size) 264 { 265 char *lp; 266 int c, len; 267 268 cnpollc(1); 269 270 lp = cp; 271 len = 0; 272 for (;;) { 273 c = cngetc(); 274 switch (c) { 275 case '\n': 276 case '\r': 277 printf("\n"); 278 *lp++ = '\0'; 279 cnpollc(0); 280 return (len); 281 case '\b': 282 case '\177': 283 case '#': 284 if (len) { 285 --len; 286 --lp; 287 printf("\b \b"); 288 } 289 continue; 290 case '@': 291 case 'u'&037: /* CTRL-u */ 292 len = 0; 293 lp = cp; 294 printf("\n"); 295 continue; 296 default: 297 if (len + 1 >= size || c < ' ') { 298 printf("\007"); 299 continue; 300 } 301 printf("%c", c); 302 ++len; 303 *lp++ = c; 304 } 305 } 306 } 307 308 void 309 cnputc(int c) 310 { 311 312 if (cn_tab == NULL) 313 return; 314 315 if (c) { 316 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 317 if (c == '\n') { 318 docritpollhooks(); 319 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 320 } 321 } 322 } 323 324 void 325 cnpollc(int on) 326 { 327 static int refcount = 0; 328 329 if (cn_tab == NULL) 330 return; 331 if (!on) 332 --refcount; 333 if (refcount == 0) 334 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on); 335 if (on) 336 ++refcount; 337 } 338 339 void 340 nullcnpollc(dev_t dev, int on) 341 { 342 343 } 344 345 void 346 cnbell(u_int pitch, u_int period, u_int volume) 347 { 348 349 if (cn_tab == NULL || cn_tab->cn_bell == NULL) 350 return; 351 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume); 352 } 353 354 void 355 cnflush(void) 356 { 357 if (cn_tab == NULL || cn_tab->cn_flush == NULL) 358 return; 359 (*cn_tab->cn_flush)(cn_tab->cn_dev); 360 } 361 362 void 363 cnhalt(void) 364 { 365 if (cn_tab == NULL || cn_tab->cn_halt == NULL) 366 return; 367 (*cn_tab->cn_halt)(cn_tab->cn_dev); 368 } 369 370 /* 371 * Redirect output, if that's appropriate. If there's no real console, 372 * return ENXIO. 373 * 374 * Call with tty_mutex held. 375 */ 376 static bool 377 cn_redirect(dev_t *devp, int is_read, int *error) 378 { 379 dev_t dev = *devp; 380 381 *error = ENXIO; 382 if (constty != NULL && minor(dev) == 0 && 383 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) { 384 if (is_read) { 385 *error = 0; 386 return false; 387 } 388 dev = constty->t_dev; 389 } else if (cn_tab == NULL) 390 return false; 391 else 392 dev = cn_tab->cn_dev; 393 *devp = dev; 394 return true; 395 } 396