1 /* $NetBSD: cons.c,v 1.77 2019/12/06 04:15:38 riastradh 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.77 2019/12/06 04:15:38 riastradh 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 vrele(vp); 162 } 163 return error; 164 } 165 166 int 167 cnread(dev_t dev, struct uio *uio, int flag) 168 { 169 int error; 170 171 /* 172 * If we would redirect input, punt. This will keep strange 173 * things from happening to people who are using the real 174 * console. Nothing should be using /dev/console for 175 * input (except a shell in single-user mode, but then, 176 * one wouldn't TIOCCONS then). 177 */ 178 if (!cn_redirect(&dev, 1, &error)) 179 return error; 180 return cdev_read(dev, uio, flag); 181 } 182 183 int 184 cnwrite(dev_t dev, struct uio *uio, int flag) 185 { 186 int error; 187 188 /* Redirect output, if that's appropriate. */ 189 if (!cn_redirect(&dev, 0, &error)) 190 return error; 191 return cdev_write(dev, uio, flag); 192 } 193 194 int 195 cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 196 { 197 int error; 198 199 error = 0; 200 201 /* 202 * Superuser can always use this to wrest control of console 203 * output from the "virtual" console. 204 */ 205 if (cmd == TIOCCONS && constty != NULL) { 206 error = kauth_authorize_device_tty(l->l_cred, 207 KAUTH_DEVICE_TTY_VIRTUAL, constty); 208 if (!error) 209 constty = NULL; 210 return (error); 211 } 212 213 /* 214 * Redirect the ioctl, if that's appropriate. 215 * Note that strange things can happen, if a program does 216 * ioctls on /dev/console, then the console is redirected 217 * out from under it. 218 */ 219 if (!cn_redirect(&dev, 0, &error)) 220 return error; 221 return cdev_ioctl(dev, cmd, data, flag, l); 222 } 223 224 /*ARGSUSED*/ 225 int 226 cnpoll(dev_t dev, int events, struct lwp *l) 227 { 228 int error; 229 230 /* 231 * Redirect the poll, if that's appropriate. 232 * I don't want to think of the possible side effects 233 * of console redirection here. 234 */ 235 if (!cn_redirect(&dev, 0, &error)) 236 return POLLHUP; 237 return cdev_poll(dev, events, l); 238 } 239 240 /*ARGSUSED*/ 241 int 242 cnkqfilter(dev_t dev, struct knote *kn) 243 { 244 int error; 245 246 /* 247 * Redirect the kqfilter, if that's appropriate. 248 * I don't want to think of the possible side effects 249 * of console redirection here. 250 */ 251 if (!cn_redirect(&dev, 0, &error)) 252 return error; 253 return cdev_kqfilter(dev, kn); 254 } 255 256 int 257 cngetc(void) 258 { 259 if (cn_tab == NULL) 260 return (0); 261 int s = splhigh(); 262 for (;;) { 263 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev); 264 if (rv >= 0) { 265 splx(s); 266 return rv; 267 } 268 docritpollhooks(); 269 } 270 } 271 272 int 273 cngetsn(char *cp, int size) 274 { 275 char *lp; 276 int c, len; 277 278 cnpollc(1); 279 280 lp = cp; 281 len = 0; 282 for (;;) { 283 c = cngetc(); 284 switch (c) { 285 case '\n': 286 case '\r': 287 printf("\n"); 288 *lp++ = '\0'; 289 cnpollc(0); 290 return (len); 291 case '\b': 292 case '\177': 293 case '#': 294 if (len) { 295 --len; 296 --lp; 297 printf("\b \b"); 298 } 299 continue; 300 case '@': 301 case 'u'&037: /* CTRL-u */ 302 len = 0; 303 lp = cp; 304 printf("\n"); 305 continue; 306 default: 307 if (len + 1 >= size || c < ' ') { 308 printf("\007"); 309 continue; 310 } 311 printf("%c", c); 312 ++len; 313 *lp++ = c; 314 } 315 } 316 } 317 318 void 319 cnputc(int c) 320 { 321 322 if (cn_tab == NULL) 323 return; 324 325 /* 326 * XXX 327 * for some reason this causes ARCS firmware to output an endless stream of 328 * whitespaces with n32 kernels, so use the pre-1.74 code for now until I can 329 * figure out why this happens 330 */ 331 #ifndef sgimips 332 if (c) { 333 if (c == '\n') { 334 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 335 docritpollhooks(); 336 } 337 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 338 } 339 #else 340 if (c) { 341 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 342 if (c == '\n') { 343 docritpollhooks(); 344 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 345 } 346 } 347 #endif 348 } 349 350 void 351 cnpollc(int on) 352 { 353 static int refcount = 0; 354 355 if (cn_tab == NULL) 356 return; 357 if (!on) 358 --refcount; 359 if (refcount == 0) 360 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on); 361 if (on) 362 ++refcount; 363 } 364 365 void 366 nullcnpollc(dev_t dev, int on) 367 { 368 369 } 370 371 void 372 cnbell(u_int pitch, u_int period, u_int volume) 373 { 374 375 if (cn_tab == NULL || cn_tab->cn_bell == NULL) 376 return; 377 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume); 378 } 379 380 void 381 cnflush(void) 382 { 383 if (cn_tab == NULL || cn_tab->cn_flush == NULL) 384 return; 385 (*cn_tab->cn_flush)(cn_tab->cn_dev); 386 } 387 388 void 389 cnhalt(void) 390 { 391 if (cn_tab == NULL || cn_tab->cn_halt == NULL) 392 return; 393 (*cn_tab->cn_halt)(cn_tab->cn_dev); 394 } 395 396 /* 397 * Redirect output, if that's appropriate. If there's no real console, 398 * return ENXIO. 399 * 400 * Call with tty_mutex held. 401 */ 402 static bool 403 cn_redirect(dev_t *devp, int is_read, int *error) 404 { 405 dev_t dev = *devp; 406 407 *error = ENXIO; 408 if (constty != NULL && minor(dev) == 0 && 409 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) { 410 if (is_read) { 411 *error = 0; 412 return false; 413 } 414 dev = constty->t_dev; 415 } else if (cn_tab == NULL) 416 return false; 417 else 418 dev = cn_tab->cn_dev; 419 *devp = dev; 420 return true; 421 } 422