1 /* $NetBSD: cons.c,v 1.70 2013/12/22 18:05:40 matt 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.70 2013/12/22 18:05:40 matt 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 cnopen, cnclose, cnread, cnwrite, cnioctl, 71 nostop, notty, cnpoll, nommap, cnkqfilter, D_TTY 72 }; 73 74 struct tty *constty = NULL; /* virtual console output device */ 75 struct consdev *cn_tab; /* physical console device info */ 76 struct vnode *cn_devvp[2]; /* vnode for underlying device. */ 77 78 int 79 cnopen(dev_t dev, int flag, int mode, struct lwp *l) 80 { 81 dev_t cndev; 82 int unit, error; 83 84 unit = minor(dev); 85 if (unit > 1) 86 return ENODEV; 87 88 if (cn_tab == NULL) 89 return (0); 90 91 /* 92 * always open the 'real' console device, so we don't get nailed 93 * later. This follows normal device semantics; they always get 94 * open() calls. 95 */ 96 cndev = cn_tab->cn_dev; 97 if (cndev == NODEV) { 98 /* 99 * This is most likely an error in the console attach 100 * code. Panicking looks better than jumping into nowhere 101 * through cdevsw below.... 102 */ 103 panic("cnopen: no console device"); 104 } 105 if (dev == cndev) { 106 /* 107 * This causes cnopen() to be called recursively, which 108 * is generally a bad thing. It is often caused when 109 * dev == 0 and cn_dev has not been set, but was probably 110 * initialised to 0. 111 */ 112 panic("cnopen: cn_tab->cn_dev == dev"); 113 } 114 if (cn_devvp[unit] != NULLVP) 115 return 0; 116 if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0) 117 printf("cnopen: unable to get vnode reference\n"); 118 error = vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY); 119 if (error == 0) { 120 error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get()); 121 VOP_UNLOCK(cn_devvp[unit]); 122 } 123 return error; 124 } 125 126 int 127 cnclose(dev_t dev, int flag, int mode, struct lwp *l) 128 { 129 struct vnode *vp; 130 int unit, error; 131 132 unit = minor(dev); 133 134 if (cn_tab == NULL) 135 return (0); 136 137 vp = cn_devvp[unit]; 138 cn_devvp[unit] = NULL; 139 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 140 if (error == 0) { 141 error = VOP_CLOSE(vp, flag, kauth_cred_get()); 142 VOP_UNLOCK(vp); 143 } 144 return error; 145 } 146 147 int 148 cnread(dev_t dev, struct uio *uio, int flag) 149 { 150 int error; 151 152 /* 153 * If we would redirect input, punt. This will keep strange 154 * things from happening to people who are using the real 155 * console. Nothing should be using /dev/console for 156 * input (except a shell in single-user mode, but then, 157 * one wouldn't TIOCCONS then). 158 */ 159 if (!cn_redirect(&dev, 1, &error)) 160 return error; 161 return cdev_read(dev, uio, flag); 162 } 163 164 int 165 cnwrite(dev_t dev, struct uio *uio, int flag) 166 { 167 int error; 168 169 /* Redirect output, if that's appropriate. */ 170 if (!cn_redirect(&dev, 0, &error)) 171 return error; 172 return cdev_write(dev, uio, flag); 173 } 174 175 int 176 cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 177 { 178 int error; 179 180 error = 0; 181 182 /* 183 * Superuser can always use this to wrest control of console 184 * output from the "virtual" console. 185 */ 186 if (cmd == TIOCCONS && constty != NULL) { 187 error = kauth_authorize_device_tty(l->l_cred, 188 KAUTH_DEVICE_TTY_VIRTUAL, constty); 189 if (!error) 190 constty = NULL; 191 return (error); 192 } 193 194 /* 195 * Redirect the ioctl, if that's appropriate. 196 * Note that strange things can happen, if a program does 197 * ioctls on /dev/console, then the console is redirected 198 * out from under it. 199 */ 200 if (!cn_redirect(&dev, 0, &error)) 201 return error; 202 return cdev_ioctl(dev, cmd, data, flag, l); 203 } 204 205 /*ARGSUSED*/ 206 int 207 cnpoll(dev_t dev, int events, struct lwp *l) 208 { 209 int error; 210 211 /* 212 * Redirect the poll, if that's appropriate. 213 * I don't want to think of the possible side effects 214 * of console redirection here. 215 */ 216 if (!cn_redirect(&dev, 0, &error)) 217 return POLLHUP; 218 return cdev_poll(dev, events, l); 219 } 220 221 /*ARGSUSED*/ 222 int 223 cnkqfilter(dev_t dev, struct knote *kn) 224 { 225 int error; 226 227 /* 228 * Redirect the kqfilter, if that's appropriate. 229 * I don't want to think of the possible side effects 230 * of console redirection here. 231 */ 232 if (!cn_redirect(&dev, 0, &error)) 233 return error; 234 return cdev_kqfilter(dev, kn); 235 } 236 237 int 238 cngetc(void) 239 { 240 if (cn_tab == NULL) 241 return (0); 242 int s = splhigh(); 243 for (;;) { 244 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev); 245 if (rv >= 0) { 246 splx(s); 247 return rv; 248 } 249 docritpollhooks(); 250 } 251 } 252 253 int 254 cngetsn(char *cp, int size) 255 { 256 char *lp; 257 int c, len; 258 259 cnpollc(1); 260 261 lp = cp; 262 len = 0; 263 for (;;) { 264 c = cngetc(); 265 switch (c) { 266 case '\n': 267 case '\r': 268 printf("\n"); 269 *lp++ = '\0'; 270 cnpollc(0); 271 return (len); 272 case '\b': 273 case '\177': 274 case '#': 275 if (len) { 276 --len; 277 --lp; 278 printf("\b \b"); 279 } 280 continue; 281 case '@': 282 case 'u'&037: /* CTRL-u */ 283 len = 0; 284 lp = cp; 285 printf("\n"); 286 continue; 287 default: 288 if (len + 1 >= size || c < ' ') { 289 printf("\007"); 290 continue; 291 } 292 printf("%c", c); 293 ++len; 294 *lp++ = c; 295 } 296 } 297 } 298 299 void 300 cnputc(int c) 301 { 302 303 if (cn_tab == NULL) 304 return; 305 306 if (c) { 307 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 308 if (c == '\n') { 309 docritpollhooks(); 310 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 311 } 312 } 313 } 314 315 void 316 cnpollc(int on) 317 { 318 static int refcount = 0; 319 320 if (cn_tab == NULL) 321 return; 322 if (!on) 323 --refcount; 324 if (refcount == 0) 325 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on); 326 if (on) 327 ++refcount; 328 } 329 330 void 331 nullcnpollc(dev_t dev, int on) 332 { 333 334 } 335 336 void 337 cnbell(u_int pitch, u_int period, u_int volume) 338 { 339 340 if (cn_tab == NULL || cn_tab->cn_bell == NULL) 341 return; 342 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume); 343 } 344 345 void 346 cnflush(void) 347 { 348 if (cn_tab == NULL || cn_tab->cn_flush == NULL) 349 return; 350 (*cn_tab->cn_flush)(cn_tab->cn_dev); 351 } 352 353 void 354 cnhalt(void) 355 { 356 if (cn_tab == NULL || cn_tab->cn_halt == NULL) 357 return; 358 (*cn_tab->cn_halt)(cn_tab->cn_dev); 359 } 360 361 /* 362 * Redirect output, if that's appropriate. If there's no real console, 363 * return ENXIO. 364 * 365 * Call with tty_mutex held. 366 */ 367 static bool 368 cn_redirect(dev_t *devp, int is_read, int *error) 369 { 370 dev_t dev = *devp; 371 372 *error = ENXIO; 373 if (constty != NULL && minor(dev) == 0 && 374 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) { 375 if (is_read) { 376 *error = 0; 377 return false; 378 } 379 dev = constty->t_dev; 380 } else if (cn_tab == NULL) 381 return false; 382 else 383 dev = cn_tab->cn_dev; 384 *devp = dev; 385 return true; 386 } 387