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