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