Index: head/sys/kern/kern_cons.c =================================================================== --- head/sys/kern/kern_cons.c (revision 339467) +++ head/sys/kern/kern_cons.c (revision 339468) @@ -1,749 +1,755 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988 University of Utah. * Copyright (c) 1991 The Regents of the University of California. * Copyright (c) 1999 Michael Smith * Copyright (c) 2005 Pawel Jakub Dawidek * * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)cons.c 7.2 (Berkeley) 5/9/91 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_syscons.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling"); struct cn_device { STAILQ_ENTRY(cn_device) cnd_next; struct consdev *cnd_cn; }; #define CNDEVPATHMAX 32 #define CNDEVTAB_SIZE 4 static struct cn_device cn_devtab[CNDEVTAB_SIZE]; static STAILQ_HEAD(, cn_device) cn_devlist = STAILQ_HEAD_INITIALIZER(cn_devlist); int cons_avail_mask = 0; /* Bit mask. Each registered low level console * which is currently unavailable for inpit * (i.e., if it is in graphics mode) will have * this bit cleared. */ static int cn_mute; static char *consbuf; /* buffer used by `consmsgbuf' */ static struct callout conscallout; /* callout for outputting to constty */ struct msgbuf consmsgbuf; /* message buffer for console tty */ static u_char console_pausing; /* pause after each line during probe */ static char *console_pausestr= ""; struct tty *constty; /* pointer to console "window" tty */ static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ static int use_cnputs_mtx = 0; /* != 0 if cnputs_mtx locking reqd. */ static void constty_timeout(void *arg); static struct consdev cons_consdev; DATA_SET(cons_set, cons_consdev); SET_DECLARE(cons_set, struct consdev); void cninit(void) { struct consdev *best_cn, *cn, **list; /* * Check if we should mute the console (for security reasons perhaps) * It can be changes dynamically using sysctl kern.consmute * once we are up and going. * */ cn_mute = ((boothowto & (RB_MUTE |RB_SINGLE |RB_VERBOSE |RB_ASKNAME)) == RB_MUTE); /* * Find the first console with the highest priority. */ best_cn = NULL; SET_FOREACH(list, cons_set) { cn = *list; cnremove(cn); /* Skip cons_consdev. */ if (cn->cn_ops == NULL) continue; cn->cn_ops->cn_probe(cn); if (cn->cn_pri == CN_DEAD) continue; if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) best_cn = cn; if (boothowto & RB_MULTIPLE) { /* * Initialize console, and attach to it. */ cn->cn_ops->cn_init(cn); cnadd(cn); } } if (best_cn == NULL) return; if ((boothowto & RB_MULTIPLE) == 0) { best_cn->cn_ops->cn_init(best_cn); cnadd(best_cn); } if (boothowto & RB_PAUSE) console_pausing = 1; /* * Make the best console the preferred console. */ cnselect(best_cn); #ifdef EARLY_PRINTF /* * Release early console. */ early_putc = NULL; #endif } void cninit_finish() { console_pausing = 0; } /* add a new physical console to back the virtual console */ int cnadd(struct consdev *cn) { struct cn_device *cnd; int i; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) if (cnd->cnd_cn == cn) return (0); for (i = 0; i < CNDEVTAB_SIZE; i++) { cnd = &cn_devtab[i]; if (cnd->cnd_cn == NULL) break; } if (cnd->cnd_cn != NULL) return (ENOMEM); cnd->cnd_cn = cn; if (cn->cn_name[0] == '\0') { /* XXX: it is unclear if/where this print might output */ printf("WARNING: console at %p has no name\n", cn); } STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); if (STAILQ_FIRST(&cn_devlist) == cnd) ttyconsdev_select(cnd->cnd_cn->cn_name); /* Add device to the active mask. */ cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); return (0); } void cnremove(struct consdev *cn) { struct cn_device *cnd; int i; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { if (cnd->cnd_cn != cn) continue; if (STAILQ_FIRST(&cn_devlist) == cnd) ttyconsdev_select(NULL); STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); cnd->cnd_cn = NULL; /* Remove this device from available mask. */ for (i = 0; i < CNDEVTAB_SIZE; i++) if (cnd == &cn_devtab[i]) { cons_avail_mask &= ~(1 << i); break; } #if 0 /* * XXX * syscons gets really confused if console resources are * freed after the system has initialized. */ if (cn->cn_term != NULL) cn->cn_ops->cn_term(cn); #endif return; } } void cnselect(struct consdev *cn) { struct cn_device *cnd; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { if (cnd->cnd_cn != cn) continue; if (cnd == STAILQ_FIRST(&cn_devlist)) return; STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); ttyconsdev_select(cnd->cnd_cn->cn_name); return; } } void cnavailable(struct consdev *cn, int available) { int i; for (i = 0; i < CNDEVTAB_SIZE; i++) { if (cn_devtab[i].cnd_cn == cn) break; } if (available) { if (i < CNDEVTAB_SIZE) cons_avail_mask |= (1 << i); cn->cn_flags &= ~CN_FLAG_NOAVAIL; } else { if (i < CNDEVTAB_SIZE) cons_avail_mask &= ~(1 << i); cn->cn_flags |= CN_FLAG_NOAVAIL; } } int cnunavailable(void) { return (cons_avail_mask == 0); } /* * sysctl_kern_console() provides output parseable in conscontrol(1). */ static int sysctl_kern_console(SYSCTL_HANDLER_ARGS) { struct cn_device *cnd; struct consdev *cp, **list; char *p; int delete, error; struct sbuf *sb; sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND | SBUF_INCLUDENUL); if (sb == NULL) return (ENOMEM); sbuf_clear(sb); STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); sbuf_printf(sb, "/"); SET_FOREACH(list, cons_set) { cp = *list; if (cp->cn_name[0] != '\0') sbuf_printf(sb, "%s,", cp->cn_name); } sbuf_finish(sb); error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); if (error == 0 && req->newptr != NULL) { p = sbuf_data(sb); error = ENXIO; delete = 0; if (*p == '-') { delete = 1; p++; } SET_FOREACH(list, cons_set) { cp = *list; if (strcmp(p, cp->cn_name) != 0) continue; if (delete) { cnremove(cp); error = 0; } else { error = cnadd(cp); if (error == 0) cnselect(cp); } break; } } sbuf_delete(sb); return (error); } SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW, 0, 0, sysctl_kern_console, "A", "Console device control"); /* * User has changed the state of the console muting. * This may require us to open or close the device in question. */ static int sysctl_kern_consmute(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, &cn_mute, 0, req); if (error != 0 || req->newptr == NULL) return (error); return (error); } SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "State of the console muting"); void cngrab() { struct cn_device *cnd; struct consdev *cn; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { cn = cnd->cnd_cn; if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) cn->cn_ops->cn_grab(cn); } } void cnungrab() { struct cn_device *cnd; struct consdev *cn; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { cn = cnd->cnd_cn; if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) cn->cn_ops->cn_ungrab(cn); } } void cnresume() { struct cn_device *cnd; struct consdev *cn; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { cn = cnd->cnd_cn; if (cn->cn_ops->cn_resume != NULL) cn->cn_ops->cn_resume(cn); } } /* * Low level console routines. */ int cngetc(void) { int c; if (cn_mute) return (-1); while ((c = cncheckc()) == -1) cpu_spinwait(); if (c == '\r') c = '\n'; /* console input is always ICRNL */ return (c); } int cncheckc(void) { struct cn_device *cnd; struct consdev *cn; int c; if (cn_mute) return (-1); STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { cn = cnd->cnd_cn; if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { c = cn->cn_ops->cn_getc(cn); if (c != -1) return (c); } } return (-1); } void cngets(char *cp, size_t size, int visible) { char *lp, *end; int c; cngrab(); lp = cp; end = cp + size - 1; for (;;) { c = cngetc() & 0177; switch (c) { case '\n': case '\r': cnputc(c); *lp = '\0'; cnungrab(); return; case '\b': case '\177': if (lp > cp) { if (visible) cnputs("\b \b"); lp--; } continue; case '\0': continue; default: if (lp < end) { switch (visible) { case GETS_NOECHO: break; case GETS_ECHOPASS: cnputc('*'); break; default: cnputc(c); break; } *lp++ = c; } } } } void cnputc(int c) { struct cn_device *cnd; struct consdev *cn; char *cp; #ifdef EARLY_PRINTF if (early_putc != NULL) { if (c == '\n') early_putc('\r'); early_putc(c); return; } #endif if (cn_mute || c == '\0') return; STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { cn = cnd->cnd_cn; if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { if (c == '\n') cn->cn_ops->cn_putc(cn, '\r'); cn->cn_ops->cn_putc(cn, c); } } if (console_pausing && c == '\n' && !kdb_active) { for (cp = console_pausestr; *cp != '\0'; cp++) cnputc(*cp); cngrab(); if (cngetc() == '.') console_pausing = 0; cnungrab(); cnputc('\r'); for (cp = console_pausestr; *cp != '\0'; cp++) cnputc(' '); cnputc('\r'); } } void -cnputs(char *p) +cnputsn(const char *p, size_t n) { - int c; + size_t i; int unlock_reqd = 0; if (use_cnputs_mtx) { /* * NOTE: Debug prints and/or witness printouts in * console driver clients can cause the "cnputs_mtx" * mutex to recurse. Simply return if that happens. */ if (mtx_owned(&cnputs_mtx)) return; mtx_lock_spin(&cnputs_mtx); unlock_reqd = 1; } - while ((c = *p++) != '\0') - cnputc(c); + for (i = 0; i < n; i++) + cnputc(p[i]); if (unlock_reqd) mtx_unlock_spin(&cnputs_mtx); +} + +void +cnputs(char *p) +{ + cnputsn(p, strlen(p)); } static int consmsgbuf_size = 8192; SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, "Console tty buffer size"); /* * Redirect console output to a tty. */ void constty_set(struct tty *tp) { int size; KASSERT(tp != NULL, ("constty_set: NULL tp")); if (consbuf == NULL) { size = consmsgbuf_size; consbuf = malloc(size, M_TTYCONS, M_WAITOK); msgbuf_init(&consmsgbuf, consbuf, size); callout_init(&conscallout, 0); } constty = tp; constty_timeout(NULL); } /* * Disable console redirection to a tty. */ void constty_clear(void) { int c; constty = NULL; if (consbuf == NULL) return; callout_stop(&conscallout); while ((c = msgbuf_getchar(&consmsgbuf)) != -1) cnputc(c); free(consbuf, M_TTYCONS); consbuf = NULL; } /* Times per second to check for pending console tty messages. */ static int constty_wakeups_per_second = 5; SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, &constty_wakeups_per_second, 0, "Times per second to check for pending console tty messages"); static void constty_timeout(void *arg) { int c; if (constty != NULL) { tty_lock(constty); while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { if (tty_putchar(constty, c) < 0) { tty_unlock(constty); constty = NULL; break; } } if (constty != NULL) tty_unlock(constty); } if (constty != NULL) { callout_reset(&conscallout, hz / constty_wakeups_per_second, constty_timeout, NULL); } else { /* Deallocate the constty buffer memory. */ constty_clear(); } } static void cn_drvinit(void *unused) { mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS); use_cnputs_mtx = 1; } SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL); /* * Sysbeep(), if we have hardware for it */ #ifdef HAS_TIMER_SPKR static int beeping; static struct callout beeping_timer; static void sysbeepstop(void *chan) { timer_spkr_release(); beeping = 0; } int sysbeep(int pitch, int period) { if (timer_spkr_acquire()) { if (!beeping) { /* Something else owns it. */ return (EBUSY); } } timer_spkr_setfreq(pitch); if (!beeping) { beeping = period; callout_reset(&beeping_timer, period, sysbeepstop, NULL); } return (0); } static void sysbeep_init(void *unused) { callout_init(&beeping_timer, 1); } SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL); #else /* * No hardware, no sound */ int sysbeep(int pitch __unused, int period __unused) { return (ENODEV); } #endif /* * Temporary support for sc(4) to vt(4) transition. */ static unsigned vty_prefer; static char vty_name[16]; SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name, 0, "Console vty driver"); int vty_enabled(unsigned vty) { static unsigned vty_selected = 0; if (vty_selected == 0) { TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name)); do { #if defined(DEV_SC) if (strcmp(vty_name, "sc") == 0) { vty_selected = VTY_SC; break; } #endif #if defined(DEV_VT) if (strcmp(vty_name, "vt") == 0) { vty_selected = VTY_VT; break; } #endif if (vty_prefer != 0) { vty_selected = vty_prefer; break; } #if defined(DEV_VT) vty_selected = VTY_VT; #elif defined(DEV_SC) vty_selected = VTY_SC; #endif } while (0); if (vty_selected == VTY_VT) strcpy(vty_name, "vt"); else if (vty_selected == VTY_SC) strcpy(vty_name, "sc"); } return ((vty_selected & vty) != 0); } void vty_set_preferred(unsigned vty) { vty_prefer = vty; #if !defined(DEV_SC) vty_prefer &= ~VTY_SC; #endif #if !defined(DEV_VT) vty_prefer &= ~VTY_VT; #endif } Index: head/sys/kern/subr_prf.c =================================================================== --- head/sys/kern/subr_prf.c (revision 339467) +++ head/sys/kern/subr_prf.c (revision 339468) @@ -1,1277 +1,1256 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #ifdef _KERNEL #include "opt_ddb.h" #include "opt_printf.h" #endif /* _KERNEL */ #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #include #include #ifdef DDB #include #endif /* * Note that stdarg.h and the ANSI style va_start macro is used for both * ANSI and traditional C compilers. */ #ifdef _KERNEL #include #else #include #endif /* * This is needed for sbuf_putbuf() when compiled into userland. Due to the * shared nature of this file, it's the only place to put it. */ #ifndef _KERNEL #include #endif #ifdef _KERNEL #define TOCONS 0x01 #define TOTTY 0x02 #define TOLOG 0x04 /* Max number conversion buffer length: a u_quad_t in base 2, plus NUL byte. */ #define MAXNBUF (sizeof(intmax_t) * NBBY + 1) struct putchar_arg { int flags; int pri; struct tty *tty; char *p_bufr; size_t n_bufr; char *p_next; size_t remain; }; struct snprintf_arg { char *str; size_t remain; }; extern int log_open; static void msglogchar(int c, int pri); static void msglogstr(char *str, int pri, int filter_cr); static void putchar(int ch, void *arg); static char *ksprintn(char *nbuf, uintmax_t num, int base, int *len, int upper); static void snprintf_func(int ch, void *arg); static bool msgbufmapped; /* Set when safe to use msgbuf */ int msgbuftrigger; struct msgbuf *msgbufp; #ifndef BOOT_TAG_SZ #define BOOT_TAG_SZ 32 #endif #ifndef BOOT_TAG /* Tag used to mark the start of a boot in dmesg */ #define BOOT_TAG "---<>---" #endif static char current_boot_tag[BOOT_TAG_SZ + 1] = BOOT_TAG; SYSCTL_STRING(_kern, OID_AUTO, boot_tag, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, current_boot_tag, 0, "Tag added to dmesg at start of boot"); static int log_console_output = 1; SYSCTL_INT(_kern, OID_AUTO, log_console_output, CTLFLAG_RWTUN, &log_console_output, 0, "Duplicate console output to the syslog"); /* * See the comment in log_console() below for more explanation of this. */ static int log_console_add_linefeed; SYSCTL_INT(_kern, OID_AUTO, log_console_add_linefeed, CTLFLAG_RWTUN, &log_console_add_linefeed, 0, "log_console() adds extra newlines"); static int always_console_output; SYSCTL_INT(_kern, OID_AUTO, always_console_output, CTLFLAG_RWTUN, &always_console_output, 0, "Always output to console despite TIOCCONS"); /* * Warn that a system table is full. */ void tablefull(const char *tab) { log(LOG_ERR, "%s: table is full\n", tab); } /* * Uprintf prints to the controlling terminal for the current process. */ int uprintf(const char *fmt, ...) { va_list ap; struct putchar_arg pca; struct proc *p; struct thread *td; int retval; td = curthread; if (TD_IS_IDLETHREAD(td)) return (0); sx_slock(&proctree_lock); p = td->td_proc; PROC_LOCK(p); if ((p->p_flag & P_CONTROLT) == 0) { PROC_UNLOCK(p); sx_sunlock(&proctree_lock); return (0); } SESS_LOCK(p->p_session); pca.tty = p->p_session->s_ttyp; SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); if (pca.tty == NULL) { sx_sunlock(&proctree_lock); return (0); } pca.flags = TOTTY; pca.p_bufr = NULL; va_start(ap, fmt); tty_lock(pca.tty); sx_sunlock(&proctree_lock); retval = kvprintf(fmt, putchar, &pca, 10, ap); tty_unlock(pca.tty); va_end(ap); return (retval); } /* * tprintf and vtprintf print on the controlling terminal associated with the * given session, possibly to the log as well. */ void tprintf(struct proc *p, int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vtprintf(p, pri, fmt, ap); va_end(ap); } void vtprintf(struct proc *p, int pri, const char *fmt, va_list ap) { struct tty *tp = NULL; int flags = 0; struct putchar_arg pca; struct session *sess = NULL; sx_slock(&proctree_lock); if (pri != -1) flags |= TOLOG; if (p != NULL) { PROC_LOCK(p); if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { sess = p->p_session; sess_hold(sess); PROC_UNLOCK(p); tp = sess->s_ttyp; if (tp != NULL && tty_checkoutq(tp)) flags |= TOTTY; else tp = NULL; } else PROC_UNLOCK(p); } pca.pri = pri; pca.tty = tp; pca.flags = flags; pca.p_bufr = NULL; if (pca.tty != NULL) tty_lock(pca.tty); sx_sunlock(&proctree_lock); kvprintf(fmt, putchar, &pca, 10, ap); if (pca.tty != NULL) tty_unlock(pca.tty); if (sess != NULL) sess_release(sess); msgbuftrigger = 1; } -/* - * Ttyprintf displays a message on a tty; it should be used only by - * the tty driver, or anything that knows the underlying tty will not - * be revoke(2)'d away. Other callers should use tprintf. - */ -int -ttyprintf(struct tty *tp, const char *fmt, ...) -{ - va_list ap; - struct putchar_arg pca; - int retval; - - va_start(ap, fmt); - pca.tty = tp; - pca.flags = TOTTY; - pca.p_bufr = NULL; - retval = kvprintf(fmt, putchar, &pca, 10, ap); - va_end(ap); - return (retval); -} - static int _vprintf(int level, int flags, const char *fmt, va_list ap) { struct putchar_arg pca; int retval; #ifdef PRINTF_BUFR_SIZE char bufr[PRINTF_BUFR_SIZE]; #endif TSENTER(); pca.tty = NULL; pca.pri = level; pca.flags = flags; #ifdef PRINTF_BUFR_SIZE pca.p_bufr = bufr; pca.p_next = pca.p_bufr; pca.n_bufr = sizeof(bufr); pca.remain = sizeof(bufr); *pca.p_next = '\0'; #else /* Don't buffer console output. */ pca.p_bufr = NULL; #endif retval = kvprintf(fmt, putchar, &pca, 10, ap); #ifdef PRINTF_BUFR_SIZE /* Write any buffered console/log output: */ if (*pca.p_bufr != '\0') { if (pca.flags & TOLOG) msglogstr(pca.p_bufr, level, /*filter_cr*/1); if (pca.flags & TOCONS) cnputs(pca.p_bufr); } #endif TSEXIT(); return (retval); } /* * Log writes to the log buffer, and guarantees not to sleep (so can be * called by interrupt routines). If there is no process reading the * log yet, it writes to the console also. */ void log(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlog(level, fmt, ap); va_end(ap); } void vlog(int level, const char *fmt, va_list ap) { (void)_vprintf(level, log_open ? TOLOG : TOCONS | TOLOG, fmt, ap); msgbuftrigger = 1; } #define CONSCHUNK 128 void log_console(struct uio *uio) { int c, error, nl; char *consbuffer; int pri; if (!log_console_output) return; pri = LOG_INFO | LOG_CONSOLE; uio = cloneuio(uio); consbuffer = malloc(CONSCHUNK, M_TEMP, M_WAITOK); nl = 0; while (uio->uio_resid > 0) { c = imin(uio->uio_resid, CONSCHUNK - 1); error = uiomove(consbuffer, c, uio); if (error != 0) break; /* Make sure we're NUL-terminated */ consbuffer[c] = '\0'; if (consbuffer[c - 1] == '\n') nl = 1; else nl = 0; msglogstr(consbuffer, pri, /*filter_cr*/ 1); } /* * The previous behavior in log_console() is preserved when * log_console_add_linefeed is non-zero. For that behavior, if an * individual console write came in that was not terminated with a * line feed, it would add a line feed. * * This results in different data in the message buffer than * appears on the system console (which doesn't add extra line feed * characters). * * A number of programs and rc scripts write a line feed, or a period * and a line feed when they have completed their operation. On * the console, this looks seamless, but when displayed with * 'dmesg -a', you wind up with output that looks like this: * * Updating motd: * . * * On the console, it looks like this: * Updating motd:. * * We could add logic to detect that situation, or just not insert * the extra newlines. Set the kern.log_console_add_linefeed * sysctl/tunable variable to get the old behavior. */ if (!nl && log_console_add_linefeed) { consbuffer[0] = '\n'; consbuffer[1] = '\0'; msglogstr(consbuffer, pri, /*filter_cr*/ 1); } msgbuftrigger = 1; free(uio, M_IOV); free(consbuffer, M_TEMP); } int printf(const char *fmt, ...) { va_list ap; int retval; va_start(ap, fmt); retval = vprintf(fmt, ap); va_end(ap); return (retval); } int vprintf(const char *fmt, va_list ap) { int retval; retval = _vprintf(-1, TOCONS | TOLOG, fmt, ap); if (!panicstr) msgbuftrigger = 1; return (retval); } static void prf_putbuf(char *bufr, int flags, int pri) { if (flags & TOLOG) msglogstr(bufr, pri, /*filter_cr*/1); if (flags & TOCONS) { if ((panicstr == NULL) && (constty != NULL)) msgbuf_addstr(&consmsgbuf, -1, bufr, /*filter_cr*/ 0); if ((constty == NULL) ||(always_console_output)) cnputs(bufr); } } static void putbuf(int c, struct putchar_arg *ap) { /* Check if no console output buffer was provided. */ if (ap->p_bufr == NULL) { /* Output direct to the console. */ if (ap->flags & TOCONS) cnputc(c); if (ap->flags & TOLOG) msglogchar(c, ap->pri); } else { /* Buffer the character: */ *ap->p_next++ = c; ap->remain--; /* Always leave the buffer zero terminated. */ *ap->p_next = '\0'; /* Check if the buffer needs to be flushed. */ if (ap->remain == 2 || c == '\n') { prf_putbuf(ap->p_bufr, ap->flags, ap->pri); ap->p_next = ap->p_bufr; ap->remain = ap->n_bufr; *ap->p_next = '\0'; } /* * Since we fill the buffer up one character at a time, * this should not happen. We should always catch it when * ap->remain == 2 (if not sooner due to a newline), flush * the buffer and move on. One way this could happen is * if someone sets PRINTF_BUFR_SIZE to 1 or something * similarly silly. */ KASSERT(ap->remain > 2, ("Bad buffer logic, remain = %zd", ap->remain)); } } /* * Print a character on console or users terminal. If destination is * the console then the last bunch of characters are saved in msgbuf for * inspection later. */ static void putchar(int c, void *arg) { struct putchar_arg *ap = (struct putchar_arg*) arg; struct tty *tp = ap->tty; int flags = ap->flags; /* Don't use the tty code after a panic or while in ddb. */ if (kdb_active) { if (c != '\0') cnputc(c); return; } if ((flags & TOTTY) && tp != NULL && panicstr == NULL) tty_putchar(tp, c); if ((flags & (TOCONS | TOLOG)) && c != '\0') putbuf(c, ap); } /* * Scaled down version of sprintf(3). */ int sprintf(char *buf, const char *cfmt, ...) { int retval; va_list ap; va_start(ap, cfmt); retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); buf[retval] = '\0'; va_end(ap); return (retval); } /* * Scaled down version of vsprintf(3). */ int vsprintf(char *buf, const char *cfmt, va_list ap) { int retval; retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); buf[retval] = '\0'; return (retval); } /* * Scaled down version of snprintf(3). */ int snprintf(char *str, size_t size, const char *format, ...) { int retval; va_list ap; va_start(ap, format); retval = vsnprintf(str, size, format, ap); va_end(ap); return(retval); } /* * Scaled down version of vsnprintf(3). */ int vsnprintf(char *str, size_t size, const char *format, va_list ap) { struct snprintf_arg info; int retval; info.str = str; info.remain = size; retval = kvprintf(format, snprintf_func, &info, 10, ap); if (info.remain >= 1) *info.str++ = '\0'; return (retval); } /* * Kernel version which takes radix argument vsnprintf(3). */ int vsnrprintf(char *str, size_t size, int radix, const char *format, va_list ap) { struct snprintf_arg info; int retval; info.str = str; info.remain = size; retval = kvprintf(format, snprintf_func, &info, radix, ap); if (info.remain >= 1) *info.str++ = '\0'; return (retval); } static void snprintf_func(int ch, void *arg) { struct snprintf_arg *const info = arg; if (info->remain >= 2) { *info->str++ = ch; info->remain--; } } /* * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse * order; return an optional length and a pointer to the last character * written in the buffer (i.e., the first character of the string). * The buffer pointed to by `nbuf' must have length >= MAXNBUF. */ static char * ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) { char *p, c; p = nbuf; *p = '\0'; do { c = hex2ascii(num % base); *++p = upper ? toupper(c) : c; } while (num /= base); if (lenp) *lenp = p - nbuf; return (p); } /* * Scaled down version of printf(3). * * Two additional formats: * * The format %b is supported to decode error registers. * Its usage is: * * printf("reg=%b\n", regval, "*"); * * where is the output base expressed as a control character, e.g. * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, * the first of which gives the bit number to be inspected (origin 1), and * the next characters (up to a control character, i.e. a character <= 32), * give the name of the register. Thus: * * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE"); * * would produce output: * * reg=3 * * XXX: %D -- Hexdump, takes pointer and separator string: * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX * ("%*D", len, ptr, " " -> XX XX XX XX ... */ int kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap) { #define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; } char nbuf[MAXNBUF]; char *d; const char *p, *percent, *q; u_char *up; int ch, n; uintmax_t num; int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; int cflag, hflag, jflag, tflag, zflag; int bconv, dwidth, upper; char padc; int stop = 0, retval = 0; num = 0; q = NULL; if (!func) d = (char *) arg; else d = NULL; if (fmt == NULL) fmt = "(fmt null)\n"; if (radix < 2 || radix > 36) radix = 10; for (;;) { padc = ' '; width = 0; while ((ch = (u_char)*fmt++) != '%' || stop) { if (ch == '\0') return (retval); PCHAR(ch); } percent = fmt - 1; qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; sign = 0; dot = 0; bconv = 0; dwidth = 0; upper = 0; cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; reswitch: switch (ch = (u_char)*fmt++) { case '.': dot = 1; goto reswitch; case '#': sharpflag = 1; goto reswitch; case '+': sign = 1; goto reswitch; case '-': ladjust = 1; goto reswitch; case '%': PCHAR(ch); break; case '*': if (!dot) { width = va_arg(ap, int); if (width < 0) { ladjust = !ladjust; width = -width; } } else { dwidth = va_arg(ap, int); } goto reswitch; case '0': if (!dot) { padc = '0'; goto reswitch; } /* FALLTHROUGH */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (n = 0;; ++fmt) { n = n * 10 + ch - '0'; ch = *fmt; if (ch < '0' || ch > '9') break; } if (dot) dwidth = n; else width = n; goto reswitch; case 'b': ladjust = 1; bconv = 1; goto handle_nosign; case 'c': width -= 1; if (!ladjust && width > 0) while (width--) PCHAR(padc); PCHAR(va_arg(ap, int)); if (ladjust && width > 0) while (width--) PCHAR(padc); break; case 'D': up = va_arg(ap, u_char *); p = va_arg(ap, char *); if (!width) width = 16; while(width--) { PCHAR(hex2ascii(*up >> 4)); PCHAR(hex2ascii(*up & 0x0f)); up++; if (width) for (q=p;*q;q++) PCHAR(*q); } break; case 'd': case 'i': base = 10; sign = 1; goto handle_sign; case 'h': if (hflag) { hflag = 0; cflag = 1; } else hflag = 1; goto reswitch; case 'j': jflag = 1; goto reswitch; case 'l': if (lflag) { lflag = 0; qflag = 1; } else lflag = 1; goto reswitch; case 'n': if (jflag) *(va_arg(ap, intmax_t *)) = retval; else if (qflag) *(va_arg(ap, quad_t *)) = retval; else if (lflag) *(va_arg(ap, long *)) = retval; else if (zflag) *(va_arg(ap, size_t *)) = retval; else if (hflag) *(va_arg(ap, short *)) = retval; else if (cflag) *(va_arg(ap, char *)) = retval; else *(va_arg(ap, int *)) = retval; break; case 'o': base = 8; goto handle_nosign; case 'p': base = 16; sharpflag = (width == 0); sign = 0; num = (uintptr_t)va_arg(ap, void *); goto number; case 'q': qflag = 1; goto reswitch; case 'r': base = radix; if (sign) goto handle_sign; goto handle_nosign; case 's': p = va_arg(ap, char *); if (p == NULL) p = "(null)"; if (!dot) n = strlen (p); else for (n = 0; n < dwidth && p[n]; n++) continue; width -= n; if (!ladjust && width > 0) while (width--) PCHAR(padc); while (n--) PCHAR(*p++); if (ladjust && width > 0) while (width--) PCHAR(padc); break; case 't': tflag = 1; goto reswitch; case 'u': base = 10; goto handle_nosign; case 'X': upper = 1; case 'x': base = 16; goto handle_nosign; case 'y': base = 16; sign = 1; goto handle_sign; case 'z': zflag = 1; goto reswitch; handle_nosign: sign = 0; if (jflag) num = va_arg(ap, uintmax_t); else if (qflag) num = va_arg(ap, u_quad_t); else if (tflag) num = va_arg(ap, ptrdiff_t); else if (lflag) num = va_arg(ap, u_long); else if (zflag) num = va_arg(ap, size_t); else if (hflag) num = (u_short)va_arg(ap, int); else if (cflag) num = (u_char)va_arg(ap, int); else num = va_arg(ap, u_int); if (bconv) { q = va_arg(ap, char *); base = *q++; } goto number; handle_sign: if (jflag) num = va_arg(ap, intmax_t); else if (qflag) num = va_arg(ap, quad_t); else if (tflag) num = va_arg(ap, ptrdiff_t); else if (lflag) num = va_arg(ap, long); else if (zflag) num = va_arg(ap, ssize_t); else if (hflag) num = (short)va_arg(ap, int); else if (cflag) num = (char)va_arg(ap, int); else num = va_arg(ap, int); number: if (sign && (intmax_t)num < 0) { neg = 1; num = -(intmax_t)num; } p = ksprintn(nbuf, num, base, &n, upper); tmp = 0; if (sharpflag && num != 0) { if (base == 8) tmp++; else if (base == 16) tmp += 2; } if (neg) tmp++; if (!ladjust && padc == '0') dwidth = width - tmp; width -= tmp + imax(dwidth, n); dwidth -= n; if (!ladjust) while (width-- > 0) PCHAR(' '); if (neg) PCHAR('-'); if (sharpflag && num != 0) { if (base == 8) { PCHAR('0'); } else if (base == 16) { PCHAR('0'); PCHAR('x'); } } while (dwidth-- > 0) PCHAR('0'); while (*p) PCHAR(*p--); if (bconv && num != 0) { /* %b conversion flag format. */ tmp = retval; while (*q) { n = *q++; if (num & (1 << (n - 1))) { PCHAR(retval != tmp ? ',' : '<'); for (; (n = *q) > ' '; ++q) PCHAR(n); } else for (; *q > ' '; ++q) continue; } if (retval != tmp) { PCHAR('>'); width -= retval - tmp; } } if (ladjust) while (width-- > 0) PCHAR(' '); break; default: while (percent < fmt) PCHAR(*percent++); /* * Since we ignore a formatting argument it is no * longer safe to obey the remaining formatting * arguments as the arguments will no longer match * the format specs. */ stop = 1; break; } } #undef PCHAR } /* * Put character in log buffer with a particular priority. */ static void msglogchar(int c, int pri) { static int lastpri = -1; static int dangling; char nbuf[MAXNBUF]; char *p; if (!msgbufmapped) return; if (c == '\0' || c == '\r') return; if (pri != -1 && pri != lastpri) { if (dangling) { msgbuf_addchar(msgbufp, '\n'); dangling = 0; } msgbuf_addchar(msgbufp, '<'); for (p = ksprintn(nbuf, (uintmax_t)pri, 10, NULL, 0); *p;) msgbuf_addchar(msgbufp, *p--); msgbuf_addchar(msgbufp, '>'); lastpri = pri; } msgbuf_addchar(msgbufp, c); if (c == '\n') { dangling = 0; lastpri = -1; } else { dangling = 1; } } static void msglogstr(char *str, int pri, int filter_cr) { if (!msgbufmapped) return; msgbuf_addstr(msgbufp, pri, str, filter_cr); } void msgbufinit(void *ptr, int size) { char *cp; static struct msgbuf *oldp = NULL; bool print_boot_tag; size -= sizeof(*msgbufp); cp = (char *)ptr; print_boot_tag = !msgbufmapped; /* Attempt to fetch kern.boot_tag tunable on first mapping */ if (!msgbufmapped) TUNABLE_STR_FETCH("kern.boot_tag", current_boot_tag, sizeof(current_boot_tag)); msgbufp = (struct msgbuf *)(cp + size); msgbuf_reinit(msgbufp, cp, size); if (msgbufmapped && oldp != msgbufp) msgbuf_copy(oldp, msgbufp); msgbufmapped = true; if (print_boot_tag && *current_boot_tag != '\0') printf("%s\n", current_boot_tag); oldp = msgbufp; } /* Sysctls for accessing/clearing the msgbuf */ static int sysctl_kern_msgbuf(SYSCTL_HANDLER_ARGS) { char buf[128]; u_int seq; int error, len; error = priv_check(req->td, PRIV_MSGBUF); if (error) return (error); /* Read the whole buffer, one chunk at a time. */ mtx_lock(&msgbuf_lock); msgbuf_peekbytes(msgbufp, NULL, 0, &seq); for (;;) { len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq); mtx_unlock(&msgbuf_lock); if (len == 0) return (SYSCTL_OUT(req, "", 1)); /* add nulterm */ error = sysctl_handle_opaque(oidp, buf, len, req); if (error) return (error); mtx_lock(&msgbuf_lock); } } SYSCTL_PROC(_kern, OID_AUTO, msgbuf, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_kern_msgbuf, "A", "Contents of kernel message buffer"); static int msgbuf_clearflag; static int sysctl_kern_msgbuf_clear(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { mtx_lock(&msgbuf_lock); msgbuf_clear(msgbufp); mtx_unlock(&msgbuf_lock); msgbuf_clearflag = 0; } return (error); } SYSCTL_PROC(_kern, OID_AUTO, msgbuf_clear, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE | CTLFLAG_MPSAFE, &msgbuf_clearflag, 0, sysctl_kern_msgbuf_clear, "I", "Clear kernel message buffer"); #ifdef DDB DB_SHOW_COMMAND(msgbuf, db_show_msgbuf) { int i, j; if (!msgbufmapped) { db_printf("msgbuf not mapped yet\n"); return; } db_printf("msgbufp = %p\n", msgbufp); db_printf("magic = %x, size = %d, r= %u, w = %u, ptr = %p, cksum= %u\n", msgbufp->msg_magic, msgbufp->msg_size, msgbufp->msg_rseq, msgbufp->msg_wseq, msgbufp->msg_ptr, msgbufp->msg_cksum); for (i = 0; i < msgbufp->msg_size && !db_pager_quit; i++) { j = MSGBUF_SEQ_TO_POS(msgbufp, i + msgbufp->msg_rseq); db_printf("%c", msgbufp->msg_ptr[j]); } db_printf("\n"); } #endif /* DDB */ void hexdump(const void *ptr, int length, const char *hdr, int flags) { int i, j, k; int cols; const unsigned char *cp; char delim; if ((flags & HD_DELIM_MASK) != 0) delim = (flags & HD_DELIM_MASK) >> 8; else delim = ' '; if ((flags & HD_COLUMN_MASK) != 0) cols = flags & HD_COLUMN_MASK; else cols = 16; cp = ptr; for (i = 0; i < length; i+= cols) { if (hdr != NULL) printf("%s", hdr); if ((flags & HD_OMIT_COUNT) == 0) printf("%04x ", i); if ((flags & HD_OMIT_HEX) == 0) { for (j = 0; j < cols; j++) { k = i + j; if (k < length) printf("%c%02x", delim, cp[k]); else printf(" "); } } if ((flags & HD_OMIT_CHARS) == 0) { printf(" |"); for (j = 0; j < cols; j++) { k = i + j; if (k >= length) printf(" "); else if (cp[k] >= ' ' && cp[k] <= '~') printf("%c", cp[k]); else printf("."); } printf("|"); } printf("\n"); } } #endif /* _KERNEL */ void sbuf_hexdump(struct sbuf *sb, const void *ptr, int length, const char *hdr, int flags) { int i, j, k; int cols; const unsigned char *cp; char delim; if ((flags & HD_DELIM_MASK) != 0) delim = (flags & HD_DELIM_MASK) >> 8; else delim = ' '; if ((flags & HD_COLUMN_MASK) != 0) cols = flags & HD_COLUMN_MASK; else cols = 16; cp = ptr; for (i = 0; i < length; i+= cols) { if (hdr != NULL) sbuf_printf(sb, "%s", hdr); if ((flags & HD_OMIT_COUNT) == 0) sbuf_printf(sb, "%04x ", i); if ((flags & HD_OMIT_HEX) == 0) { for (j = 0; j < cols; j++) { k = i + j; if (k < length) sbuf_printf(sb, "%c%02x", delim, cp[k]); else sbuf_printf(sb, " "); } } if ((flags & HD_OMIT_CHARS) == 0) { sbuf_printf(sb, " |"); for (j = 0; j < cols; j++) { k = i + j; if (k >= length) sbuf_printf(sb, " "); else if (cp[k] >= ' ' && cp[k] <= '~') sbuf_printf(sb, "%c", cp[k]); else sbuf_printf(sb, "."); } sbuf_printf(sb, "|"); } sbuf_printf(sb, "\n"); } } #ifdef _KERNEL void counted_warning(unsigned *counter, const char *msg) { struct thread *td; unsigned c; for (;;) { c = *counter; if (c == 0) break; if (atomic_cmpset_int(counter, c, c - 1)) { td = curthread; log(LOG_INFO, "pid %d (%s) %s%s\n", td->td_proc->p_pid, td->td_name, msg, c > 1 ? "" : " - not logging anymore"); break; } } } #endif #ifdef _KERNEL void sbuf_putbuf(struct sbuf *sb) { prf_putbuf(sbuf_data(sb), TOLOG | TOCONS, -1); } #else void sbuf_putbuf(struct sbuf *sb) { printf("%s", sbuf_data(sb)); } #endif Index: head/sys/kern/tty_info.c =================================================================== --- head/sys/kern/tty_info.c (revision 339467) +++ head/sys/kern/tty_info.c (revision 339468) @@ -1,315 +1,347 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Copyright (c) 2002 Networks Associates Technologies, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include +#include +#include #include #include #include #include +#include #include #include #include #include #include #include /* * Returns 1 if p2 is "better" than p1 * * The algorithm for picking the "interesting" process is thus: * * 1) Only foreground processes are eligible - implied. * 2) Runnable processes are favored over anything else. The runner * with the highest cpu utilization is picked (p_estcpu). Ties are * broken by picking the highest pid. * 3) The sleeper with the shortest sleep time is next. With ties, * we pick out just "short-term" sleepers (P_SINTR == 0). * 4) Further ties are broken by picking the highest pid. */ #define TESTAB(a, b) ((a)<<1 | (b)) #define ONLYA 2 #define ONLYB 1 #define BOTH 3 static int proc_sum(struct proc *p, fixpt_t *estcpup) { struct thread *td; int estcpu; int val; val = 0; estcpu = 0; FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); if (TD_ON_RUNQ(td) || TD_IS_RUNNING(td)) val = 1; estcpu += sched_pctcpu(td); thread_unlock(td); } *estcpup = estcpu; return (val); } static int thread_compare(struct thread *td, struct thread *td2) { int runa, runb; int slpa, slpb; fixpt_t esta, estb; if (td == NULL) return (1); /* * Fetch running stats, pctcpu usage, and interruptable flag. */ thread_lock(td); runa = TD_IS_RUNNING(td) | TD_ON_RUNQ(td); slpa = td->td_flags & TDF_SINTR; esta = sched_pctcpu(td); thread_unlock(td); thread_lock(td2); runb = TD_IS_RUNNING(td2) | TD_ON_RUNQ(td2); estb = sched_pctcpu(td2); slpb = td2->td_flags & TDF_SINTR; thread_unlock(td2); /* * see if at least one of them is runnable */ switch (TESTAB(runa, runb)) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: break; } /* * favor one with highest recent cpu utilization */ if (estb > esta) return (1); if (esta > estb) return (0); /* * favor one sleeping in a non-interruptible sleep */ switch (TESTAB(slpa, slpb)) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: break; } return (td < td2); } static int proc_compare(struct proc *p1, struct proc *p2) { int runa, runb; fixpt_t esta, estb; if (p1 == NULL) return (1); /* * Fetch various stats about these processes. After we drop the * lock the information could be stale but the race is unimportant. */ PROC_LOCK(p1); runa = proc_sum(p1, &esta); PROC_UNLOCK(p1); PROC_LOCK(p2); runb = proc_sum(p2, &estb); PROC_UNLOCK(p2); /* * see if at least one of them is runnable */ switch (TESTAB(runa, runb)) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: break; } /* * favor one with highest recent cpu utilization */ if (estb > esta) return (1); if (esta > estb) return (0); /* * weed out zombies */ switch (TESTAB(p1->p_state == PRS_ZOMBIE, p2->p_state == PRS_ZOMBIE)) { case ONLYA: return (1); case ONLYB: return (0); case BOTH: break; } return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } +static int +sbuf_tty_drain(void *a, const char *d, int len) +{ + struct tty *tp; + int rc; + + tp = a; + + if (kdb_active) { + cnputsn(d, len); + return (len); + } + if (tp != NULL && panicstr == NULL) { + rc = tty_putstrn(tp, d, len); + if (rc != 0) + return (-ENXIO); + return (len); + } + return (-ENXIO); +} + /* * Report on state of foreground process group. */ void tty_info(struct tty *tp) { struct timeval rtime, utime, stime; struct proc *p, *ppick; struct thread *td, *tdpick; const char *stateprefix, *state; + struct sbuf sb; long rss; int load, pctcpu; pid_t pid; char comm[MAXCOMLEN + 1]; struct rusage ru; tty_lock_assert(tp, MA_OWNED); if (tty_checkoutq(tp) == 0) return; + (void)sbuf_new(&sb, tp->t_prbuf, sizeof(tp->t_prbuf), SBUF_FIXEDLEN); + sbuf_set_drain(&sb, sbuf_tty_drain, tp); + /* Print load average. */ load = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; - ttyprintf(tp, "%sload: %d.%02d ", tp->t_column == 0 ? "" : "\n", + sbuf_printf(&sb, "%sload: %d.%02d ", tp->t_column == 0 ? "" : "\n", load / 100, load % 100); if (tp->t_session == NULL) { - ttyprintf(tp, "not a controlling terminal\n"); - return; + sbuf_printf(&sb, "not a controlling terminal\n"); + goto out; } if (tp->t_pgrp == NULL) { - ttyprintf(tp, "no foreground process group\n"); - return; + sbuf_printf(&sb, "no foreground process group\n"); + goto out; } PGRP_LOCK(tp->t_pgrp); if (LIST_EMPTY(&tp->t_pgrp->pg_members)) { PGRP_UNLOCK(tp->t_pgrp); - ttyprintf(tp, "empty foreground process group\n"); - return; + sbuf_printf(&sb, "empty foreground process group\n"); + goto out; } /* * Pick the most interesting process and copy some of its * state for printing later. This operation could rely on stale * data as we can't hold the proc slock or thread locks over the * whole list. However, we're guaranteed not to reference an exited * thread or proc since we hold the tty locked. */ p = NULL; LIST_FOREACH(ppick, &tp->t_pgrp->pg_members, p_pglist) if (proc_compare(p, ppick)) p = ppick; PROC_LOCK(p); PGRP_UNLOCK(tp->t_pgrp); td = NULL; FOREACH_THREAD_IN_PROC(p, tdpick) if (thread_compare(td, tdpick)) td = tdpick; stateprefix = ""; thread_lock(td); if (TD_IS_RUNNING(td)) state = "running"; else if (TD_ON_RUNQ(td) || TD_CAN_RUN(td)) state = "runnable"; else if (TD_IS_SLEEPING(td)) { /* XXX: If we're sleeping, are we ever not in a queue? */ if (TD_ON_SLEEPQ(td)) state = td->td_wmesg; else state = "sleeping without queue"; } else if (TD_ON_LOCK(td)) { state = td->td_lockname; stateprefix = "*"; } else if (TD_IS_SUSPENDED(td)) state = "suspended"; else if (TD_AWAITING_INTR(td)) state = "intrwait"; else if (p->p_state == PRS_ZOMBIE) state = "zombie"; else state = "unknown"; pctcpu = (sched_pctcpu(td) * 10000 + FSCALE / 2) >> FSHIFT; thread_unlock(td); if (p->p_state == PRS_NEW || p->p_state == PRS_ZOMBIE) rss = 0; else rss = pgtok(vmspace_resident_count(p->p_vmspace)); microuptime(&rtime); timevalsub(&rtime, &p->p_stats->p_start); rufetchcalc(p, &ru, &utime, &stime); pid = p->p_pid; strlcpy(comm, p->p_comm, sizeof comm); PROC_UNLOCK(p); /* Print command, pid, state, rtime, utime, stime, %cpu, and rss. */ - ttyprintf(tp, + sbuf_printf(&sb, " cmd: %s %d [%s%s] %ld.%02ldr %ld.%02ldu %ld.%02lds %d%% %ldk\n", comm, pid, stateprefix, state, (long)rtime.tv_sec, rtime.tv_usec / 10000, (long)utime.tv_sec, utime.tv_usec / 10000, (long)stime.tv_sec, stime.tv_usec / 10000, pctcpu / 100, rss); + +out: + sbuf_finish(&sb); + sbuf_delete(&sb); } Index: head/sys/kern/tty_ttydisc.c =================================================================== --- head/sys/kern/tty_ttydisc.c (revision 339467) +++ head/sys/kern/tty_ttydisc.c (revision 339468) @@ -1,1267 +1,1277 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Ed Schouten * All rights reserved. * * Portions of this software were developed under sponsorship from Snow * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include /* * Standard TTYDISC `termios' line discipline. */ /* Statistics. */ static unsigned long tty_nin = 0; SYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, &tty_nin, 0, "Total amount of bytes received"); static unsigned long tty_nout = 0; SYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, &tty_nout, 0, "Total amount of bytes transmitted"); /* termios comparison macro's. */ #define CMP_CC(v,c) (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && \ tp->t_termios.c_cc[v] == (c)) #define CMP_FLAG(field,opt) (tp->t_termios.c_ ## field ## flag & (opt)) /* Characters that cannot be modified through c_cc. */ #define CTAB '\t' #define CNL '\n' #define CCR '\r' /* Character is a control character. */ #define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20) /* Control character should be processed on echo. */ #define CTL_ECHO(c,q) (!(q) && ((c) == CERASE2 || (c) == CTAB || \ (c) == CNL || (c) == CCR)) /* Control character should be printed using ^X notation. */ #define CTL_PRINT(c,q) ((c) == 0x7f || ((unsigned char)(c) < 0x20 && \ ((q) || ((c) != CTAB && (c) != CNL)))) /* Character is whitespace. */ #define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB) /* Character is alphanumeric. */ #define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) #define TTY_STACKBUF 256 void ttydisc_open(struct tty *tp) { ttydisc_optimize(tp); } void ttydisc_close(struct tty *tp) { /* Clean up our flags when leaving the discipline. */ tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); /* * POSIX states that we must drain output and flush input on * last close. Draining has already been done if possible. */ tty_flush(tp, FREAD | FWRITE); if (ttyhook_hashook(tp, close)) ttyhook_close(tp); } static int ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) { char breakc[4] = { CNL }; /* enough to hold \n, VEOF and VEOL. */ int error; size_t clen, flen = 0, n = 1; unsigned char lastc = _POSIX_VDISABLE; #define BREAK_ADD(c) do { \ if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ breakc[n++] = tp->t_termios.c_cc[c]; \ } while (0) /* Determine which characters we should trigger on. */ BREAK_ADD(VEOF); BREAK_ADD(VEOL); #undef BREAK_ADD breakc[n] = '\0'; do { error = tty_wait_background(tp, curthread, SIGTTIN); if (error) return (error); /* * Quite a tricky case: unlike the old TTY * implementation, this implementation copies data back * to userspace in large chunks. Unfortunately, we can't * calculate the line length on beforehand if it crosses * ttyinq_block boundaries, because multiple reads could * then make this code read beyond the newline. * * This is why we limit the read to: * - The size the user has requested * - The blocksize (done in tty_inq.c) * - The amount of bytes until the newline * * This causes the line length to be recalculated after * each block has been copied to userspace. This will * cause the TTY layer to return data in chunks using * the blocksize (except the first and last blocks). */ clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, &lastc); /* No more data. */ if (clen == 0) { if (tp->t_flags & TF_ZOMBIE) return (0); else if (ioflag & IO_NDELAY) return (EWOULDBLOCK); error = tty_wait(tp, &tp->t_inwait); if (error) return (error); continue; } /* Don't send the EOF char back to userspace. */ if (CMP_CC(VEOF, lastc)) flen = 1; MPASS(flen <= clen); /* Read and throw away the EOF character. */ error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen); if (error) return (error); } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE); return (0); } static int ttydisc_read_raw_no_timer(struct tty *tp, struct uio *uio, int ioflag) { size_t vmin = tp->t_termios.c_cc[VMIN]; ssize_t oresid = uio->uio_resid; int error; MPASS(tp->t_termios.c_cc[VTIME] == 0); /* * This routine implements the easy cases of read()s while in * non-canonical mode, namely case B and D, where we don't have * any timers at all. */ for (;;) { error = tty_wait_background(tp, curthread, SIGTTIN); if (error) return (error); error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0); if (error) return (error); if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) return (0); /* We have to wait for more. */ if (tp->t_flags & TF_ZOMBIE) return (0); else if (ioflag & IO_NDELAY) return (EWOULDBLOCK); error = tty_wait(tp, &tp->t_inwait); if (error) return (error); } } static int ttydisc_read_raw_read_timer(struct tty *tp, struct uio *uio, int ioflag, int oresid) { size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1); unsigned int vtime = tp->t_termios.c_cc[VTIME]; struct timeval end, now, left; int error, hz; MPASS(tp->t_termios.c_cc[VTIME] != 0); /* Determine when the read should be expired. */ end.tv_sec = vtime / 10; end.tv_usec = (vtime % 10) * 100000; getmicrotime(&now); timevaladd(&end, &now); for (;;) { error = tty_wait_background(tp, curthread, SIGTTIN); if (error) return (error); error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0); if (error) return (error); if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) return (0); /* Calculate how long we should wait. */ getmicrotime(&now); if (timevalcmp(&now, &end, >)) return (0); left = end; timevalsub(&left, &now); hz = tvtohz(&left); /* * We have to wait for more. If the timer expires, we * should return a 0-byte read. */ if (tp->t_flags & TF_ZOMBIE) return (0); else if (ioflag & IO_NDELAY) return (EWOULDBLOCK); error = tty_timedwait(tp, &tp->t_inwait, hz); if (error) return (error == EWOULDBLOCK ? 0 : error); } return (0); } static int ttydisc_read_raw_interbyte_timer(struct tty *tp, struct uio *uio, int ioflag) { size_t vmin = tp->t_termios.c_cc[VMIN]; ssize_t oresid = uio->uio_resid; int error; MPASS(tp->t_termios.c_cc[VMIN] != 0); MPASS(tp->t_termios.c_cc[VTIME] != 0); /* * When using the interbyte timer, the timer should be started * after the first byte has been received. We just call into the * generic read timer code after we've received the first byte. */ for (;;) { error = tty_wait_background(tp, curthread, SIGTTIN); if (error) return (error); error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0); if (error) return (error); if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) return (0); /* * Not enough data, but we did receive some, which means * we'll now start using the interbyte timer. */ if (oresid != uio->uio_resid) break; /* We have to wait for more. */ if (tp->t_flags & TF_ZOMBIE) return (0); else if (ioflag & IO_NDELAY) return (EWOULDBLOCK); error = tty_wait(tp, &tp->t_inwait); if (error) return (error); } return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid); } int ttydisc_read(struct tty *tp, struct uio *uio, int ioflag) { int error; tty_lock_assert(tp, MA_OWNED); if (uio->uio_resid == 0) return (0); if (CMP_FLAG(l, ICANON)) error = ttydisc_read_canonical(tp, uio, ioflag); else if (tp->t_termios.c_cc[VTIME] == 0) error = ttydisc_read_raw_no_timer(tp, uio, ioflag); else if (tp->t_termios.c_cc[VMIN] == 0) error = ttydisc_read_raw_read_timer(tp, uio, ioflag, uio->uio_resid); else error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag); if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow || ttyinq_bytescanonicalized(&tp->t_inq) == 0) { /* Unset the input watermark when we've got enough space. */ tty_hiwat_in_unblock(tp); } return (error); } static __inline unsigned int ttydisc_findchar(const char *obstart, unsigned int oblen) { const char *c = obstart; while (oblen--) { if (CTL_VALID(*c)) break; c++; } return (c - obstart); } static int ttydisc_write_oproc(struct tty *tp, char c) { unsigned int scnt, error; MPASS(CMP_FLAG(o, OPOST)); MPASS(CTL_VALID(c)); #define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1) switch (c) { case CEOF: /* End-of-text dropping. */ if (CMP_FLAG(o, ONOEOT)) return (0); return PRINT_NORMAL(); case CERASE2: /* Handle backspace to fix tab expansion. */ if (PRINT_NORMAL() != 0) return (-1); if (tp->t_column > 0) tp->t_column--; return (0); case CTAB: /* Tab expansion. */ scnt = 8 - (tp->t_column & 7); if (CMP_FLAG(o, TAB3)) { error = ttyoutq_write_nofrag(&tp->t_outq, " ", scnt); } else { error = PRINT_NORMAL(); } if (error) return (-1); tp->t_column += scnt; MPASS((tp->t_column % 8) == 0); return (0); case CNL: /* Newline conversion. */ if (CMP_FLAG(o, ONLCR)) { /* Convert \n to \r\n. */ error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2); } else { error = PRINT_NORMAL(); } if (error) return (-1); if (CMP_FLAG(o, ONLCR|ONLRET)) { tp->t_column = tp->t_writepos = 0; ttyinq_reprintpos_set(&tp->t_inq); } return (0); case CCR: /* Carriage return to newline conversion. */ if (CMP_FLAG(o, OCRNL)) c = CNL; /* Omit carriage returns on column 0. */ if (CMP_FLAG(o, ONOCR) && tp->t_column == 0) return (0); if (PRINT_NORMAL() != 0) return (-1); tp->t_column = tp->t_writepos = 0; ttyinq_reprintpos_set(&tp->t_inq); return (0); } /* * Invisible control character. Print it, but don't * increase the column count. */ return PRINT_NORMAL(); #undef PRINT_NORMAL } /* * Just like the old TTY implementation, we need to copy data in chunks * into a temporary buffer. One of the reasons why we need to do this, * is because output processing (only TAB3 though) may allow the buffer * to grow eight times. */ int ttydisc_write(struct tty *tp, struct uio *uio, int ioflag) { char ob[TTY_STACKBUF]; char *obstart; int error = 0; unsigned int oblen = 0; tty_lock_assert(tp, MA_OWNED); if (tp->t_flags & TF_ZOMBIE) return (EIO); /* * We don't need to check whether the process is the foreground * process group or if we have a carrier. This is already done * in ttydev_write(). */ while (uio->uio_resid > 0) { unsigned int nlen; MPASS(oblen == 0); /* Step 1: read data. */ obstart = ob; nlen = MIN(uio->uio_resid, sizeof ob); tty_unlock(tp); error = uiomove(ob, nlen, uio); tty_lock(tp); if (error != 0) break; oblen = nlen; if (tty_gone(tp)) { error = ENXIO; break; } MPASS(oblen > 0); /* Step 2: process data. */ do { unsigned int plen, wlen; /* Search for special characters for post processing. */ if (CMP_FLAG(o, OPOST)) { plen = ttydisc_findchar(obstart, oblen); } else { plen = oblen; } if (plen == 0) { /* * We're going to process a character * that needs processing */ if (ttydisc_write_oproc(tp, *obstart) == 0) { obstart++; oblen--; tp->t_writepos = tp->t_column; ttyinq_reprintpos_set(&tp->t_inq); continue; } } else { /* We're going to write regular data. */ wlen = ttyoutq_write(&tp->t_outq, obstart, plen); obstart += wlen; oblen -= wlen; tp->t_column += wlen; tp->t_writepos = tp->t_column; ttyinq_reprintpos_set(&tp->t_inq); if (wlen == plen) continue; } /* Watermark reached. Try to sleep. */ tp->t_flags |= TF_HIWAT_OUT; if (ioflag & IO_NDELAY) { error = EWOULDBLOCK; goto done; } /* * The driver may write back the data * synchronously. Be sure to check the high * water mark before going to sleep. */ ttydevsw_outwakeup(tp); if ((tp->t_flags & TF_HIWAT_OUT) == 0) continue; error = tty_wait(tp, &tp->t_outwait); if (error) goto done; if (tp->t_flags & TF_ZOMBIE) { error = EIO; goto done; } } while (oblen > 0); } done: if (!tty_gone(tp)) ttydevsw_outwakeup(tp); /* * Add the amount of bytes that we didn't process back to the * uio counters. We need to do this to make sure write() doesn't * count the bytes we didn't store in the queue. */ uio->uio_resid += oblen; return (error); } void ttydisc_optimize(struct tty *tp) { tty_lock_assert(tp, MA_OWNED); if (ttyhook_hashook(tp, rint_bypass)) { tp->t_flags |= TF_BYPASS; } else if (ttyhook_hashook(tp, rint)) { tp->t_flags &= ~TF_BYPASS; } else if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && (!CMP_FLAG(i, PARMRK) || CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { tp->t_flags |= TF_BYPASS; } else { tp->t_flags &= ~TF_BYPASS; } } void ttydisc_modem(struct tty *tp, int open) { tty_lock_assert(tp, MA_OWNED); if (open) cv_broadcast(&tp->t_dcdwait); /* * Ignore modem status lines when CLOCAL is turned on, but don't * enter the zombie state when the TTY isn't opened, because * that would cause the TTY to be in zombie state after being * opened. */ if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL)) return; if (open == 0) { /* * Lost carrier. */ tp->t_flags |= TF_ZOMBIE; tty_signal_sessleader(tp, SIGHUP); tty_flush(tp, FREAD|FWRITE); } else { /* * Carrier is back again. */ /* XXX: what should we do here? */ } } static int ttydisc_echo_force(struct tty *tp, char c, int quote) { if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote)) { /* * Only perform postprocessing when OPOST is turned on * and the character is an unquoted BS/TB/NL/CR. */ return ttydisc_write_oproc(tp, c); } else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote)) { /* * Only use ^X notation when ECHOCTL is turned on and * we've got an quoted control character. * * Print backspaces when echoing an end-of-file. */ char ob[4] = "^?\b\b"; /* Print ^X notation. */ if (c != 0x7f) ob[1] = c + 'A' - 1; if (!quote && CMP_CC(VEOF, c)) { return ttyoutq_write_nofrag(&tp->t_outq, ob, 4); } else { tp->t_column += 2; return ttyoutq_write_nofrag(&tp->t_outq, ob, 2); } } else { /* Can just be printed. */ tp->t_column++; return ttyoutq_write_nofrag(&tp->t_outq, &c, 1); } } static int ttydisc_echo(struct tty *tp, char c, int quote) { /* * Only echo characters when ECHO is turned on, or ECHONL when * the character is an unquoted newline. */ if (!CMP_FLAG(l, ECHO) && (!CMP_FLAG(l, ECHONL) || c != CNL || quote)) return (0); return ttydisc_echo_force(tp, c, quote); } static void ttydisc_reprint_char(void *d, char c, int quote) { struct tty *tp = d; ttydisc_echo(tp, c, quote); } static void ttydisc_reprint(struct tty *tp) { cc_t c; /* Print ^R\n, followed by the line. */ c = tp->t_termios.c_cc[VREPRINT]; if (c != _POSIX_VDISABLE) ttydisc_echo(tp, c, 0); ttydisc_echo(tp, CNL, 0); ttyinq_reprintpos_reset(&tp->t_inq); ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp); } struct ttydisc_recalc_length { struct tty *tp; unsigned int curlen; }; static void ttydisc_recalc_charlength(void *d, char c, int quote) { struct ttydisc_recalc_length *data = d; struct tty *tp = data->tp; if (CTL_PRINT(c, quote)) { if (CMP_FLAG(l, ECHOCTL)) data->curlen += 2; } else if (c == CTAB) { data->curlen += 8 - (data->curlen & 7); } else { data->curlen++; } } static unsigned int ttydisc_recalc_linelength(struct tty *tp) { struct ttydisc_recalc_length data = { tp, tp->t_writepos }; ttyinq_line_iterate_from_reprintpos(&tp->t_inq, ttydisc_recalc_charlength, &data); return (data.curlen); } static int ttydisc_rubchar(struct tty *tp) { char c; int quote; unsigned int prevpos, tablen; if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) return (-1); ttyinq_unputchar(&tp->t_inq); if (CMP_FLAG(l, ECHO)) { /* * Remove the character from the screen. This is even * safe for characters that span multiple characters * (tabs, quoted, etc). */ if (tp->t_writepos >= tp->t_column) { /* Retype the sentence. */ ttydisc_reprint(tp); } else if (CMP_FLAG(l, ECHOE)) { if (CTL_PRINT(c, quote)) { /* Remove ^X formatted chars. */ if (CMP_FLAG(l, ECHOCTL)) { tp->t_column -= 2; ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6); } } else if (c == ' ') { /* Space character needs no rubbing. */ tp->t_column -= 1; ttyoutq_write_nofrag(&tp->t_outq, "\b", 1); } else if (c == CTAB) { /* * Making backspace work with tabs is * quite hard. Recalculate the length of * this character and remove it. * * Because terminal settings could be * changed while the line is being * inserted, the calculations don't have * to be correct. Make sure we keep the * tab length within proper bounds. */ prevpos = ttydisc_recalc_linelength(tp); if (prevpos >= tp->t_column) tablen = 1; else tablen = tp->t_column - prevpos; if (tablen > 8) tablen = 8; tp->t_column = prevpos; ttyoutq_write_nofrag(&tp->t_outq, "\b\b\b\b\b\b\b\b", tablen); return (0); } else { /* * Remove a regular character by * punching a space over it. */ tp->t_column -= 1; ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3); } } else { /* Don't print spaces. */ ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0); } } return (0); } static void ttydisc_rubword(struct tty *tp) { char c; int quote, alnum; /* Strip whitespace first. */ for (;;) { if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) return; if (!CTL_WHITE(c)) break; ttydisc_rubchar(tp); } /* * Record whether the last character from the previous iteration * was alphanumeric or not. We need this to implement ALTWERASE. */ alnum = CTL_ALNUM(c); for (;;) { ttydisc_rubchar(tp); if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) return; if (CTL_WHITE(c)) return; if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum) return; } } int ttydisc_rint(struct tty *tp, char c, int flags) { int signal, quote = 0; char ob[3] = { 0xff, 0x00 }; size_t ol; tty_lock_assert(tp, MA_OWNED); atomic_add_long(&tty_nin, 1); if (ttyhook_hashook(tp, rint)) return ttyhook_rint(tp, c, flags); if (tp->t_flags & TF_BYPASS) goto processed; if (flags) { if (flags & TRE_BREAK) { if (CMP_FLAG(i, IGNBRK)) { /* Ignore break characters. */ return (0); } else if (CMP_FLAG(i, BRKINT)) { /* Generate SIGINT on break. */ tty_flush(tp, FREAD|FWRITE); tty_signal_pgrp(tp, SIGINT); return (0); } else { /* Just print it. */ goto parmrk; } } else if (flags & TRE_FRAMING || (flags & TRE_PARITY && CMP_FLAG(i, INPCK))) { if (CMP_FLAG(i, IGNPAR)) { /* Ignore bad characters. */ return (0); } else { /* Just print it. */ goto parmrk; } } } /* Allow any character to perform a wakeup. */ if (CMP_FLAG(i, IXANY)) tp->t_flags &= ~TF_STOPPED; /* Remove the top bit. */ if (CMP_FLAG(i, ISTRIP)) c &= ~0x80; /* Skip input processing when we want to print it literally. */ if (tp->t_flags & TF_LITERAL) { tp->t_flags &= ~TF_LITERAL; quote = 1; goto processed; } /* Special control characters that are implementation dependent. */ if (CMP_FLAG(l, IEXTEN)) { /* Accept the next character as literal. */ if (CMP_CC(VLNEXT, c)) { if (CMP_FLAG(l, ECHO)) { if (CMP_FLAG(l, ECHOE)) ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2); else ttydisc_echo(tp, c, 0); } tp->t_flags |= TF_LITERAL; return (0); } } /* * Handle signal processing. */ if (CMP_FLAG(l, ISIG)) { if (CMP_FLAG(l, ICANON|IEXTEN) == (ICANON|IEXTEN)) { if (CMP_CC(VSTATUS, c)) { tty_signal_pgrp(tp, SIGINFO); return (0); } } /* * When compared to the old implementation, this * implementation also flushes the output queue. POSIX * is really brief about this, but does makes us assume * we have to do so. */ signal = 0; if (CMP_CC(VINTR, c)) { signal = SIGINT; } else if (CMP_CC(VQUIT, c)) { signal = SIGQUIT; } else if (CMP_CC(VSUSP, c)) { signal = SIGTSTP; } if (signal != 0) { /* * Echo the character before signalling the * processes. */ if (!CMP_FLAG(l, NOFLSH)) tty_flush(tp, FREAD|FWRITE); ttydisc_echo(tp, c, 0); tty_signal_pgrp(tp, signal); return (0); } } /* * Handle start/stop characters. */ if (CMP_FLAG(i, IXON)) { if (CMP_CC(VSTOP, c)) { /* Stop it if we aren't stopped yet. */ if ((tp->t_flags & TF_STOPPED) == 0) { tp->t_flags |= TF_STOPPED; return (0); } /* * Fallthrough: * When VSTART == VSTOP, we should make this key * toggle it. */ if (!CMP_CC(VSTART, c)) return (0); } if (CMP_CC(VSTART, c)) { tp->t_flags &= ~TF_STOPPED; return (0); } } /* Conversion of CR and NL. */ switch (c) { case CCR: if (CMP_FLAG(i, IGNCR)) return (0); if (CMP_FLAG(i, ICRNL)) c = CNL; break; case CNL: if (CMP_FLAG(i, INLCR)) c = CCR; break; } /* Canonical line editing. */ if (CMP_FLAG(l, ICANON)) { if (CMP_CC(VERASE, c) || CMP_CC(VERASE2, c)) { ttydisc_rubchar(tp); return (0); } else if (CMP_CC(VKILL, c)) { while (ttydisc_rubchar(tp) == 0); return (0); } else if (CMP_FLAG(l, IEXTEN)) { if (CMP_CC(VWERASE, c)) { ttydisc_rubword(tp); return (0); } else if (CMP_CC(VREPRINT, c)) { ttydisc_reprint(tp); return (0); } } } processed: if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff) { /* Print 0xff 0xff. */ ob[1] = 0xff; ol = 2; quote = 1; } else { ob[0] = c; ol = 1; } goto print; parmrk: if (CMP_FLAG(i, PARMRK)) { /* Prepend 0xff 0x00 0x.. */ ob[2] = c; ol = 3; quote = 1; } else { ob[0] = c; ol = 1; } print: /* See if we can store this on the input queue. */ if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0) { if (CMP_FLAG(i, IMAXBEL)) ttyoutq_write_nofrag(&tp->t_outq, "\a", 1); /* * Prevent a deadlock here. It may be possible that a * user has entered so much data, there is no data * available to read(), but the buffers are full anyway. * * Only enter the high watermark if the device driver * can actually transmit something. */ if (ttyinq_bytescanonicalized(&tp->t_inq) == 0) return (0); tty_hiwat_in_block(tp); return (-1); } /* * In raw mode, we canonicalize after receiving a single * character. Otherwise, we canonicalize when we receive a * newline, VEOL or VEOF, but only when it isn't quoted. */ if (!CMP_FLAG(l, ICANON) || (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c)))) { ttyinq_canonicalize(&tp->t_inq); } ttydisc_echo(tp, c, quote); return (0); } size_t ttydisc_rint_simple(struct tty *tp, const void *buf, size_t len) { const char *cbuf; if (ttydisc_can_bypass(tp)) return (ttydisc_rint_bypass(tp, buf, len)); for (cbuf = buf; len-- > 0; cbuf++) { if (ttydisc_rint(tp, *cbuf, 0) != 0) break; } return (cbuf - (const char *)buf); } size_t ttydisc_rint_bypass(struct tty *tp, const void *buf, size_t len) { size_t ret; tty_lock_assert(tp, MA_OWNED); MPASS(tp->t_flags & TF_BYPASS); atomic_add_long(&tty_nin, len); if (ttyhook_hashook(tp, rint_bypass)) return ttyhook_rint_bypass(tp, buf, len); ret = ttyinq_write(&tp->t_inq, buf, len, 0); ttyinq_canonicalize(&tp->t_inq); if (ret < len) tty_hiwat_in_block(tp); return (ret); } void ttydisc_rint_done(struct tty *tp) { tty_lock_assert(tp, MA_OWNED); if (ttyhook_hashook(tp, rint_done)) ttyhook_rint_done(tp); /* Wake up readers. */ tty_wakeup(tp, FREAD); /* Wake up driver for echo. */ ttydevsw_outwakeup(tp); } size_t ttydisc_rint_poll(struct tty *tp) { size_t l; tty_lock_assert(tp, MA_OWNED); if (ttyhook_hashook(tp, rint_poll)) return ttyhook_rint_poll(tp); /* * XXX: Still allow character input when there's no space in the * buffers, but we haven't entered the high watermark. This is * to allow backspace characters to be inserted when in * canonical mode. */ l = ttyinq_bytesleft(&tp->t_inq); if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0) return (1); return (l); } static void ttydisc_wakeup_watermark(struct tty *tp) { size_t c; c = ttyoutq_bytesleft(&tp->t_outq); if (tp->t_flags & TF_HIWAT_OUT) { /* Only allow us to run when we're below the watermark. */ if (c < tp->t_outlow) return; /* Reset the watermark. */ tp->t_flags &= ~TF_HIWAT_OUT; } else { /* Only run when we have data at all. */ if (c == 0) return; } tty_wakeup(tp, FWRITE); } size_t ttydisc_getc(struct tty *tp, void *buf, size_t len) { tty_lock_assert(tp, MA_OWNED); if (tp->t_flags & TF_STOPPED) return (0); if (ttyhook_hashook(tp, getc_inject)) return ttyhook_getc_inject(tp, buf, len); len = ttyoutq_read(&tp->t_outq, buf, len); if (ttyhook_hashook(tp, getc_capture)) ttyhook_getc_capture(tp, buf, len); ttydisc_wakeup_watermark(tp); atomic_add_long(&tty_nout, len); return (len); } int ttydisc_getc_uio(struct tty *tp, struct uio *uio) { int error = 0; ssize_t obytes = uio->uio_resid; size_t len; char buf[TTY_STACKBUF]; tty_lock_assert(tp, MA_OWNED); if (tp->t_flags & TF_STOPPED) return (0); /* * When a TTY hook is attached, we cannot perform unbuffered * copying to userspace. Just call ttydisc_getc() and * temporarily store data in a shadow buffer. */ if (ttyhook_hashook(tp, getc_capture) || ttyhook_hashook(tp, getc_inject)) { while (uio->uio_resid > 0) { /* Read to shadow buffer. */ len = ttydisc_getc(tp, buf, MIN(uio->uio_resid, sizeof buf)); if (len == 0) break; /* Copy to userspace. */ tty_unlock(tp); error = uiomove(buf, len, uio); tty_lock(tp); if (error != 0) break; } } else { error = ttyoutq_read_uio(&tp->t_outq, tp, uio); ttydisc_wakeup_watermark(tp); atomic_add_long(&tty_nout, obytes - uio->uio_resid); } return (error); } size_t ttydisc_getc_poll(struct tty *tp) { tty_lock_assert(tp, MA_OWNED); if (tp->t_flags & TF_STOPPED) return (0); if (ttyhook_hashook(tp, getc_poll)) return ttyhook_getc_poll(tp); return ttyoutq_bytesused(&tp->t_outq); } /* * XXX: not really related to the TTYDISC, but we'd better put * tty_putchar() here, because we need to perform proper output * processing. */ int -tty_putchar(struct tty *tp, char c) +tty_putstrn(struct tty *tp, const char *p, size_t n) { + size_t i; + tty_lock_assert(tp, MA_OWNED); if (tty_gone(tp)) return (-1); - ttydisc_echo_force(tp, c, 0); + for (i = 0; i < n; i++) + ttydisc_echo_force(tp, p[i], 0); + tp->t_writepos = tp->t_column; ttyinq_reprintpos_set(&tp->t_inq); ttydevsw_outwakeup(tp); return (0); +} + +int +tty_putchar(struct tty *tp, char c) +{ + return (tty_putstrn(tp, &c, 1)); } Index: head/sys/sys/cons.h =================================================================== --- head/sys/sys/cons.h (revision 339467) +++ head/sys/sys/cons.h (revision 339468) @@ -1,152 +1,153 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988 University of Utah. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)cons.h 7.2 (Berkeley) 5/9/91 * $FreeBSD$ */ #ifndef _MACHINE_CONS_H_ #define _MACHINE_CONS_H_ struct consdev; struct tty; typedef void cn_probe_t(struct consdev *); typedef void cn_init_t(struct consdev *); typedef void cn_term_t(struct consdev *); typedef void cn_grab_t(struct consdev *); typedef void cn_ungrab_t(struct consdev *); typedef int cn_getc_t(struct consdev *); typedef void cn_putc_t(struct consdev *, int); struct consdev_ops { cn_probe_t *cn_probe; /* probe hardware and fill in consdev info */ cn_init_t *cn_init; /* turn on as console */ cn_term_t *cn_term; /* turn off as console */ cn_getc_t *cn_getc; /* kernel getchar interface */ cn_putc_t *cn_putc; /* kernel putchar interface */ cn_grab_t *cn_grab; /* grab console for exclusive kernel use */ cn_ungrab_t *cn_ungrab; /* ungrab console */ cn_init_t *cn_resume; /* set up console after sleep, optional */ }; struct consdev { const struct consdev_ops *cn_ops; /* console device operations. */ short cn_pri; /* pecking order; the higher the better */ void *cn_arg; /* drivers method argument */ int cn_flags; /* capabilities of this console */ char cn_name[SPECNAMELEN + 1]; /* console (device) name */ }; /* values for cn_pri - reflect our policy for console selection */ #define CN_DEAD 0 /* device doesn't exist */ #define CN_LOW 1 /* device is a last restort only */ #define CN_NORMAL 2 /* device exists but is nothing special */ #define CN_INTERNAL 3 /* "internal" bit-mapped display */ #define CN_REMOTE 4 /* serial interface with remote bit set */ /* Values for cn_flags. */ #define CN_FLAG_NODEBUG 0x00000001 /* Not supported with debugger. */ #define CN_FLAG_NOAVAIL 0x00000002 /* Temporarily not available. */ /* Visibility of characters in cngets() */ #define GETS_NOECHO 0 /* Disable echoing of characters. */ #define GETS_ECHO 1 /* Enable echoing of characters. */ #define GETS_ECHOPASS 2 /* Print a * for every character. */ #ifdef _KERNEL extern struct msgbuf consmsgbuf; /* Message buffer for constty. */ extern struct tty *constty; /* Temporary virtual console. */ #define CONSOLE_DEVICE(name, ops, arg) \ static struct consdev name = { \ .cn_ops = &ops, \ .cn_arg = (arg), \ }; \ DATA_SET(cons_set, name) #define CONSOLE_DRIVER(name, ...) \ static const struct consdev_ops name##_consdev_ops = { \ /* Mandatory methods. */ \ .cn_probe = name##_cnprobe, \ .cn_init = name##_cninit, \ .cn_term = name##_cnterm, \ .cn_getc = name##_cngetc, \ .cn_putc = name##_cnputc, \ .cn_grab = name##_cngrab, \ .cn_ungrab = name##_cnungrab, \ /* Optional fields. */ \ __VA_ARGS__ \ }; \ CONSOLE_DEVICE(name##_consdev, name##_consdev_ops, NULL) /* Other kernel entry points. */ void cninit(void); void cninit_finish(void); int cnadd(struct consdev *); void cnavailable(struct consdev *, int); void cnremove(struct consdev *); void cnselect(struct consdev *); void cngrab(void); void cnungrab(void); void cnresume(void); int cncheckc(void); int cngetc(void); void cngets(char *, size_t, int); void cnputc(int); void cnputs(char *); +void cnputsn(const char *, size_t); int cnunavailable(void); void constty_set(struct tty *tp); void constty_clear(void); /* sc(4) / vt(4) coexistence shim */ #define VTY_SC 0x01 #define VTY_VT 0x02 int vty_enabled(unsigned int); void vty_set_preferred(unsigned int); #endif /* _KERNEL */ #endif /* !_MACHINE_CONS_H_ */ Index: head/sys/sys/systm.h =================================================================== --- head/sys/sys/systm.h (revision 339467) +++ head/sys/sys/systm.h (revision 339468) @@ -1,549 +1,548 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)systm.h 8.7 (Berkeley) 3/29/95 * $FreeBSD$ */ #ifndef _SYS_SYSTM_H_ #define _SYS_SYSTM_H_ #include #include #include #include #include #include /* for people using printf mainly */ __NULLABILITY_PRAGMA_PUSH extern int cold; /* nonzero if we are doing a cold boot */ extern int suspend_blocked; /* block suspend due to pending shutdown */ extern int rebooting; /* kern_reboot() has been called. */ extern const char *panicstr; /* panic message */ extern char version[]; /* system version */ extern char compiler_version[]; /* compiler version */ extern char copyright[]; /* system copyright */ extern int kstack_pages; /* number of kernel stack pages */ extern u_long pagesizes[]; /* supported page sizes */ extern long physmem; /* physical memory */ extern long realmem; /* 'real' memory */ extern char *rootdevnames[2]; /* names of possible root devices */ extern int boothowto; /* reboot flags, from console subsystem */ extern int bootverbose; /* nonzero to print verbose messages */ extern int maxusers; /* system tune hint */ extern int ngroups_max; /* max # of supplemental groups */ extern int vm_guest; /* Running as virtual machine guest? */ /* * Detected virtual machine guest types. The intention is to expand * and/or add to the VM_GUEST_VM type if specific VM functionality is * ever implemented (e.g. vendor-specific paravirtualization features). * Keep in sync with vm_guest_sysctl_names[]. */ enum VM_GUEST { VM_GUEST_NO = 0, VM_GUEST_VM, VM_GUEST_XEN, VM_GUEST_HV, VM_GUEST_VMWARE, VM_GUEST_KVM, VM_GUEST_BHYVE, VM_LAST }; /* * These functions need to be declared before the KASSERT macro is invoked in * !KASSERT_PANIC_OPTIONAL builds, so their declarations are sort of out of * place compared to other function definitions in this header. On the other * hand, this header is a bit disorganized anyway. */ void panic(const char *, ...) __dead2 __printflike(1, 2); void vpanic(const char *, __va_list) __dead2 __printflike(1, 0); #if defined(WITNESS) || defined(INVARIANT_SUPPORT) #ifdef KASSERT_PANIC_OPTIONAL void kassert_panic(const char *fmt, ...) __printflike(1, 2); #else #define kassert_panic panic #endif #endif #ifdef INVARIANTS /* The option is always available */ #define KASSERT(exp,msg) do { \ if (__predict_false(!(exp))) \ kassert_panic msg; \ } while (0) #define VNASSERT(exp, vp, msg) do { \ if (__predict_false(!(exp))) { \ vn_printf(vp, "VNASSERT failed\n"); \ kassert_panic msg; \ } \ } while (0) #else #define KASSERT(exp,msg) do { \ } while (0) #define VNASSERT(exp, vp, msg) do { \ } while (0) #endif #ifndef CTASSERT /* Allow lint to override */ #define CTASSERT(x) _Static_assert(x, "compile-time assertion failed") #endif #if defined(_KERNEL) #include /* MAXCPU */ #include /* curthread */ #include #endif /* * Assert that a pointer can be loaded from memory atomically. * * This assertion enforces stronger alignment than necessary. For example, * on some architectures, atomicity for unaligned loads will depend on * whether or not the load spans multiple cache lines. */ #define ASSERT_ATOMIC_LOAD_PTR(var, msg) \ KASSERT(sizeof(var) == sizeof(void *) && \ ((uintptr_t)&(var) & (sizeof(void *) - 1)) == 0, msg) /* * Assert that a thread is in critical(9) section. */ #define CRITICAL_ASSERT(td) \ KASSERT((td)->td_critnest >= 1, ("Not in critical section")); /* * If we have already panic'd and this is the thread that called * panic(), then don't block on any mutexes but silently succeed. * Otherwise, the kernel will deadlock since the scheduler isn't * going to run the thread that holds any lock we need. */ #define SCHEDULER_STOPPED_TD(td) ({ \ MPASS((td) == curthread); \ __predict_false((td)->td_stopsched); \ }) #define SCHEDULER_STOPPED() SCHEDULER_STOPPED_TD(curthread) /* * Align variables. */ #define __read_mostly __section(".data.read_mostly") #define __read_frequently __section(".data.read_frequently") #define __exclusive_cache_line __aligned(CACHE_LINE_SIZE) \ __section(".data.exclusive_cache_line") /* * XXX the hints declarations are even more misplaced than most declarations * in this file, since they are needed in one file (per arch) and only used * in two files. * XXX most of these variables should be const. */ extern int osreldate; extern bool dynamic_kenv; extern struct mtx kenv_lock; extern char *kern_envp; extern char *md_envp; extern char static_env[]; extern char static_hints[]; /* by config for now */ extern char **kenvp; extern const void *zero_region; /* address space maps to a zeroed page */ extern int unmapped_buf_allowed; #ifdef __LP64__ #define IOSIZE_MAX iosize_max() #define DEVFS_IOSIZE_MAX devfs_iosize_max() #else #define IOSIZE_MAX SSIZE_MAX #define DEVFS_IOSIZE_MAX SSIZE_MAX #endif /* * General function declarations. */ struct inpcb; struct lock_object; struct malloc_type; struct mtx; struct proc; struct socket; struct thread; struct tty; struct ucred; struct uio; struct _jmp_buf; struct trapframe; struct eventtimer; int setjmp(struct _jmp_buf *) __returns_twice; void longjmp(struct _jmp_buf *, int) __dead2; int dumpstatus(vm_offset_t addr, off_t count); int nullop(void); int eopnotsupp(void); int ureadc(int, struct uio *); void hashdestroy(void *, struct malloc_type *, u_long); void *hashinit(int count, struct malloc_type *type, u_long *hashmask); void *hashinit_flags(int count, struct malloc_type *type, u_long *hashmask, int flags); #define HASH_NOWAIT 0x00000001 #define HASH_WAITOK 0x00000002 void *phashinit(int count, struct malloc_type *type, u_long *nentries); void *phashinit_flags(int count, struct malloc_type *type, u_long *nentries, int flags); void g_waitidle(void); void cpu_boot(int); void cpu_flush_dcache(void *, size_t); void cpu_rootconf(void); void critical_enter_KBI(void); void critical_exit_KBI(void); void critical_exit_preempt(void); void init_param1(void); void init_param2(long physpages); void init_static_kenv(char *, size_t); void tablefull(const char *); #if defined(KLD_MODULE) || defined(KTR_CRITICAL) || !defined(_KERNEL) || defined(GENOFFSET) #define critical_enter() critical_enter_KBI() #define critical_exit() critical_exit_KBI() #else static __inline void critical_enter(void) { struct thread_lite *td; td = (struct thread_lite *)curthread; td->td_critnest++; __compiler_membar(); } static __inline void critical_exit(void) { struct thread_lite *td; td = (struct thread_lite *)curthread; KASSERT(td->td_critnest != 0, ("critical_exit: td_critnest == 0")); __compiler_membar(); td->td_critnest--; __compiler_membar(); if (__predict_false(td->td_owepreempt)) critical_exit_preempt(); } #endif #ifdef EARLY_PRINTF typedef void early_putc_t(int ch); extern early_putc_t *early_putc; #endif int kvprintf(char const *, void (*)(int, void*), void *, int, __va_list) __printflike(1, 0); void log(int, const char *, ...) __printflike(2, 3); void log_console(struct uio *); void vlog(int, const char *, __va_list) __printflike(2, 0); int asprintf(char **ret, struct malloc_type *mtp, const char *format, ...) __printflike(3, 4); int printf(const char *, ...) __printflike(1, 2); int snprintf(char *, size_t, const char *, ...) __printflike(3, 4); int sprintf(char *buf, const char *, ...) __printflike(2, 3); int uprintf(const char *, ...) __printflike(1, 2); int vprintf(const char *, __va_list) __printflike(1, 0); int vasprintf(char **ret, struct malloc_type *mtp, const char *format, __va_list ap) __printflike(3, 0); int vsnprintf(char *, size_t, const char *, __va_list) __printflike(3, 0); int vsnrprintf(char *, size_t, int, const char *, __va_list) __printflike(4, 0); int vsprintf(char *buf, const char *, __va_list) __printflike(2, 0); -int ttyprintf(struct tty *, const char *, ...) __printflike(2, 3); int sscanf(const char *, char const * _Nonnull, ...) __scanflike(2, 3); int vsscanf(const char * _Nonnull, char const * _Nonnull, __va_list) __scanflike(2, 0); long strtol(const char *, char **, int); u_long strtoul(const char *, char **, int); quad_t strtoq(const char *, char **, int); u_quad_t strtouq(const char *, char **, int); void tprintf(struct proc *p, int pri, const char *, ...) __printflike(3, 4); void vtprintf(struct proc *, int, const char *, __va_list) __printflike(3, 0); void hexdump(const void *ptr, int length, const char *hdr, int flags); #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 #define HD_OMIT_COUNT (1 << 16) #define HD_OMIT_HEX (1 << 17) #define HD_OMIT_CHARS (1 << 18) #define ovbcopy(f, t, l) bcopy((f), (t), (l)) void bcopy(const void * _Nonnull from, void * _Nonnull to, size_t len); #define bcopy(from, to, len) __builtin_memmove((to), (from), (len)) void bzero(void * _Nonnull buf, size_t len); #define bzero(buf, len) __builtin_memset((buf), 0, (len)) void explicit_bzero(void * _Nonnull, size_t); int bcmp(const void *b1, const void *b2, size_t len); #define bcmp(b1, b2, len) __builtin_memcmp((b1), (b2), (len)) void *memset(void * _Nonnull buf, int c, size_t len); #define memset(buf, c, len) __builtin_memset((buf), (c), (len)) void *memcpy(void * _Nonnull to, const void * _Nonnull from, size_t len); #define memcpy(to, from, len) __builtin_memcpy((to), (from), (len)) void *memmove(void * _Nonnull dest, const void * _Nonnull src, size_t n); #define memmove(dest, src, n) __builtin_memmove((dest), (src), (n)) int memcmp(const void *b1, const void *b2, size_t len); #define memcmp(b1, b2, len) __builtin_memcmp((b1), (b2), (len)) void *memset_early(void * _Nonnull buf, int c, size_t len); #define bzero_early(buf, len) memset_early((buf), 0, (len)) void *memcpy_early(void * _Nonnull to, const void * _Nonnull from, size_t len); void *memmove_early(void * _Nonnull dest, const void * _Nonnull src, size_t n); #define bcopy_early(from, to, len) memmove_early((to), (from), (len)) int copystr(const void * _Nonnull __restrict kfaddr, void * _Nonnull __restrict kdaddr, size_t len, size_t * __restrict lencopied); int copyinstr(const void * __restrict udaddr, void * _Nonnull __restrict kaddr, size_t len, size_t * __restrict lencopied); int copyin(const void * __restrict udaddr, void * _Nonnull __restrict kaddr, size_t len); int copyin_nofault(const void * __restrict udaddr, void * _Nonnull __restrict kaddr, size_t len); int copyout(const void * _Nonnull __restrict kaddr, void * __restrict udaddr, size_t len); int copyout_nofault(const void * _Nonnull __restrict kaddr, void * __restrict udaddr, size_t len); int fubyte(volatile const void *base); long fuword(volatile const void *base); int fuword16(volatile const void *base); int32_t fuword32(volatile const void *base); int64_t fuword64(volatile const void *base); int fueword(volatile const void *base, long *val); int fueword32(volatile const void *base, int32_t *val); int fueword64(volatile const void *base, int64_t *val); int subyte(volatile void *base, int byte); int suword(volatile void *base, long word); int suword16(volatile void *base, int word); int suword32(volatile void *base, int32_t word); int suword64(volatile void *base, int64_t word); uint32_t casuword32(volatile uint32_t *base, uint32_t oldval, uint32_t newval); u_long casuword(volatile u_long *p, u_long oldval, u_long newval); int casueword32(volatile uint32_t *base, uint32_t oldval, uint32_t *oldvalp, uint32_t newval); int casueword(volatile u_long *p, u_long oldval, u_long *oldvalp, u_long newval); void realitexpire(void *); int sysbeep(int hertz, int period); void hardclock(int cnt, int usermode); void hardclock_sync(int cpu); void softclock(void *); void statclock(int cnt, int usermode); void profclock(int cnt, int usermode, uintfptr_t pc); int hardclockintr(void); void startprofclock(struct proc *); void stopprofclock(struct proc *); void cpu_startprofclock(void); void cpu_stopprofclock(void); void suspendclock(void); void resumeclock(void); sbintime_t cpu_idleclock(void); void cpu_activeclock(void); void cpu_new_callout(int cpu, sbintime_t bt, sbintime_t bt_opt); void cpu_et_frequency(struct eventtimer *et, uint64_t newfreq); extern int cpu_disable_c2_sleep; extern int cpu_disable_c3_sleep; char *kern_getenv(const char *name); void freeenv(char *env); int getenv_int(const char *name, int *data); int getenv_uint(const char *name, unsigned int *data); int getenv_long(const char *name, long *data); int getenv_ulong(const char *name, unsigned long *data); int getenv_string(const char *name, char *data, int size); int getenv_int64(const char *name, int64_t *data); int getenv_uint64(const char *name, uint64_t *data); int getenv_quad(const char *name, quad_t *data); int kern_setenv(const char *name, const char *value); int kern_unsetenv(const char *name); int testenv(const char *name); int getenv_array(const char *name, void *data, int size, int *psize, int type_size, bool allow_signed); #define GETENV_UNSIGNED false /* negative numbers not allowed */ #define GETENV_SIGNED true /* negative numbers allowed */ typedef uint64_t (cpu_tick_f)(void); void set_cputicker(cpu_tick_f *func, uint64_t freq, unsigned var); extern cpu_tick_f *cpu_ticks; uint64_t cpu_tickrate(void); uint64_t cputick2usec(uint64_t tick); #ifdef APM_FIXUP_CALLTODO struct timeval; void adjust_timeout_calltodo(struct timeval *time_change); #endif /* APM_FIXUP_CALLTODO */ #include /* Initialize the world */ void consinit(void); void cpu_initclocks(void); void cpu_initclocks_bsp(void); void cpu_initclocks_ap(void); void usrinfoinit(void); /* Finalize the world */ void kern_reboot(int) __dead2; void shutdown_nice(int); /* Timeouts */ typedef void timeout_t(void *); /* timeout function type */ #define CALLOUT_HANDLE_INITIALIZER(handle) \ { NULL } void callout_handle_init(struct callout_handle *); struct callout_handle timeout(timeout_t *, void *, int); void untimeout(timeout_t *, void *, struct callout_handle); /* Stubs for obsolete functions that used to be for interrupt management */ static __inline intrmask_t splbio(void) { return 0; } static __inline intrmask_t splcam(void) { return 0; } static __inline intrmask_t splclock(void) { return 0; } static __inline intrmask_t splhigh(void) { return 0; } static __inline intrmask_t splimp(void) { return 0; } static __inline intrmask_t splnet(void) { return 0; } static __inline intrmask_t spltty(void) { return 0; } static __inline void splx(intrmask_t ipl __unused) { return; } /* * Common `proc' functions are declared here so that proc.h can be included * less often. */ int _sleep(void * _Nonnull chan, struct lock_object *lock, int pri, const char *wmesg, sbintime_t sbt, sbintime_t pr, int flags); #define msleep(chan, mtx, pri, wmesg, timo) \ _sleep((chan), &(mtx)->lock_object, (pri), (wmesg), \ tick_sbt * (timo), 0, C_HARDCLOCK) #define msleep_sbt(chan, mtx, pri, wmesg, bt, pr, flags) \ _sleep((chan), &(mtx)->lock_object, (pri), (wmesg), (bt), (pr), \ (flags)) int msleep_spin_sbt(void * _Nonnull chan, struct mtx *mtx, const char *wmesg, sbintime_t sbt, sbintime_t pr, int flags); #define msleep_spin(chan, mtx, wmesg, timo) \ msleep_spin_sbt((chan), (mtx), (wmesg), tick_sbt * (timo), \ 0, C_HARDCLOCK) int pause_sbt(const char *wmesg, sbintime_t sbt, sbintime_t pr, int flags); #define pause(wmesg, timo) \ pause_sbt((wmesg), tick_sbt * (timo), 0, C_HARDCLOCK) #define pause_sig(wmesg, timo) \ pause_sbt((wmesg), tick_sbt * (timo), 0, C_HARDCLOCK | C_CATCH) #define tsleep(chan, pri, wmesg, timo) \ _sleep((chan), NULL, (pri), (wmesg), tick_sbt * (timo), \ 0, C_HARDCLOCK) #define tsleep_sbt(chan, pri, wmesg, bt, pr, flags) \ _sleep((chan), NULL, (pri), (wmesg), (bt), (pr), (flags)) void wakeup(void * chan); void wakeup_one(void * chan); /* * Common `struct cdev *' stuff are declared here to avoid #include poisoning */ struct cdev; dev_t dev2udev(struct cdev *x); const char *devtoname(struct cdev *cdev); #ifdef __LP64__ size_t devfs_iosize_max(void); size_t iosize_max(void); #endif int poll_no_poll(int events); /* XXX: Should be void nanodelay(u_int nsec); */ void DELAY(int usec); /* Root mount holdback API */ struct root_hold_token; struct root_hold_token *root_mount_hold(const char *identifier); void root_mount_rel(struct root_hold_token *h); int root_mounted(void); /* * Unit number allocation API. (kern/subr_unit.c) */ struct unrhdr; struct unrhdr *new_unrhdr(int low, int high, struct mtx *mutex); void init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex); void delete_unrhdr(struct unrhdr *uh); void clear_unrhdr(struct unrhdr *uh); void clean_unrhdr(struct unrhdr *uh); void clean_unrhdrl(struct unrhdr *uh); int alloc_unr(struct unrhdr *uh); int alloc_unr_specific(struct unrhdr *uh, u_int item); int alloc_unrl(struct unrhdr *uh); void free_unr(struct unrhdr *uh, u_int item); void intr_prof_stack_use(struct thread *td, struct trapframe *frame); void counted_warning(unsigned *counter, const char *msg); /* * APIs to manage deprecation and obsolescence. */ struct device; void _gone_in(int major, const char *msg); void _gone_in_dev(struct device *dev, int major, const char *msg); #ifdef NO_OBSOLETE_CODE #define __gone_ok(m, msg) \ _Static_assert(m < P_OSREL_MAJOR(__FreeBSD_version)), \ "Obsolete code" msg); #else #define __gone_ok(m, msg) #endif #define gone_in(major, msg) __gone_ok(major, msg) _gone_in(major, msg) #define gone_in_dev(dev, major, msg) __gone_ok(major, msg) _gone_in_dev(dev, major, msg) __NULLABILITY_PRAGMA_POP #endif /* !_SYS_SYSTM_H_ */ Index: head/sys/sys/tty.h =================================================================== --- head/sys/sys/tty.h (revision 339467) +++ head/sys/sys/tty.h (revision 339468) @@ -1,230 +1,238 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Ed Schouten * All rights reserved. * * Portions of this software were developed under sponsorship from Snow * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_TTY_H_ #define _SYS_TTY_H_ #include #include #include #include #include #include #include #include #include struct cdev; struct file; struct pgrp; struct session; struct ucred; struct ttydevsw; /* * Per-TTY structure, containing buffers, etc. * * List of locks * (t) locked by t_mtx * (l) locked by tty_list_sx * (c) const until freeing */ struct tty { struct mtx *t_mtx; /* TTY lock. */ struct mtx t_mtxobj; /* Per-TTY lock (when not borrowing). */ TAILQ_ENTRY(tty) t_list; /* (l) TTY list entry. */ int t_drainwait; /* (t) TIOCDRAIN timeout seconds. */ unsigned int t_flags; /* (t) Terminal option flags. */ /* Keep flags in sync with db_show_tty and pstat(8). */ #define TF_NOPREFIX 0x00001 /* Don't prepend "tty" to device name. */ #define TF_INITLOCK 0x00002 /* Create init/lock state devices. */ #define TF_CALLOUT 0x00004 /* Create "cua" devices. */ #define TF_OPENED_IN 0x00008 /* "tty" node is in use. */ #define TF_OPENED_OUT 0x00010 /* "cua" node is in use. */ #define TF_OPENED_CONS 0x00020 /* Device in use as console. */ #define TF_OPENED (TF_OPENED_IN|TF_OPENED_OUT|TF_OPENED_CONS) #define TF_GONE 0x00040 /* Device node is gone. */ #define TF_OPENCLOSE 0x00080 /* Device is in open()/close(). */ #define TF_ASYNC 0x00100 /* Asynchronous I/O enabled. */ #define TF_LITERAL 0x00200 /* Accept the next character literally. */ #define TF_HIWAT_IN 0x00400 /* We've reached the input watermark. */ #define TF_HIWAT_OUT 0x00800 /* We've reached the output watermark. */ #define TF_HIWAT (TF_HIWAT_IN|TF_HIWAT_OUT) #define TF_STOPPED 0x01000 /* Output flow control - stopped. */ #define TF_EXCLUDE 0x02000 /* Exclusive access. */ #define TF_BYPASS 0x04000 /* Optimized input path. */ #define TF_ZOMBIE 0x08000 /* Modem disconnect received. */ #define TF_HOOK 0x10000 /* TTY has hook attached. */ #define TF_BUSY_IN 0x20000 /* Process busy in read() -- not supported. */ #define TF_BUSY_OUT 0x40000 /* Process busy in write(). */ #define TF_BUSY (TF_BUSY_IN|TF_BUSY_OUT) unsigned int t_revokecnt; /* (t) revoke() count. */ /* Buffering mechanisms. */ struct ttyinq t_inq; /* (t) Input queue. */ size_t t_inlow; /* (t) Input low watermark. */ struct ttyoutq t_outq; /* (t) Output queue. */ size_t t_outlow; /* (t) Output low watermark. */ /* Sleeping mechanisms. */ struct cv t_inwait; /* (t) Input wait queue. */ struct cv t_outwait; /* (t) Output wait queue. */ struct cv t_outserwait; /* (t) Serial output wait queue. */ struct cv t_bgwait; /* (t) Background wait queue. */ struct cv t_dcdwait; /* (t) Carrier Detect wait queue. */ /* Polling mechanisms. */ struct selinfo t_inpoll; /* (t) Input poll queue. */ struct selinfo t_outpoll; /* (t) Output poll queue. */ struct sigio *t_sigio; /* (t) Asynchronous I/O. */ struct termios t_termios; /* (t) I/O processing flags. */ struct winsize t_winsize; /* (t) Window size. */ unsigned int t_column; /* (t) Current cursor position. */ unsigned int t_writepos; /* (t) Where input was interrupted. */ int t_compatflags; /* (t) COMPAT_43TTY flags. */ /* Init/lock-state devices. */ struct termios t_termios_init_in; /* tty%s.init. */ struct termios t_termios_lock_in; /* tty%s.lock. */ struct termios t_termios_init_out; /* cua%s.init. */ struct termios t_termios_lock_out; /* cua%s.lock. */ struct ttydevsw *t_devsw; /* (c) Driver hooks. */ struct ttyhook *t_hook; /* (t) Capture/inject hook. */ /* Process signal delivery. */ struct pgrp *t_pgrp; /* (t) Foreground process group. */ struct session *t_session; /* (t) Associated session. */ unsigned int t_sessioncnt; /* (t) Backpointing sessions. */ void *t_devswsoftc; /* (c) Soft config, for drivers. */ void *t_hooksoftc; /* (t) Soft config, for hooks. */ struct cdev *t_dev; /* (c) Primary character device. */ + +#ifndef PRINTF_BUFR_SIZE +#define TTY_PRINTF_SIZE 256 +#else +#define TTY_PRINTF_SIZE PRINTF_BUFR_SIZE +#endif + char t_prbuf[TTY_PRINTF_SIZE]; /* (t) */ }; /* * Userland version of struct tty, for sysctl kern.ttys */ struct xtty { size_t xt_size; /* Structure size. */ size_t xt_insize; /* Input queue size. */ size_t xt_incc; /* Canonicalized characters. */ size_t xt_inlc; /* Input line charaters. */ size_t xt_inlow; /* Input low watermark. */ size_t xt_outsize; /* Output queue size. */ size_t xt_outcc; /* Output queue usage. */ size_t xt_outlow; /* Output low watermark. */ unsigned int xt_column; /* Current column position. */ pid_t xt_pgid; /* Foreground process group. */ pid_t xt_sid; /* Session. */ unsigned int xt_flags; /* Terminal option flags. */ uint32_t xt_dev; /* Userland device. XXXKIB truncated */ }; #ifdef _KERNEL /* Used to distinguish between normal, callout, lock and init devices. */ #define TTYUNIT_INIT 0x1 #define TTYUNIT_LOCK 0x2 #define TTYUNIT_CALLOUT 0x4 /* Allocation and deallocation. */ struct tty *tty_alloc(struct ttydevsw *tsw, void *softc); struct tty *tty_alloc_mutex(struct ttydevsw *tsw, void *softc, struct mtx *mtx); void tty_rel_pgrp(struct tty *tp, struct pgrp *pgrp); void tty_rel_sess(struct tty *tp, struct session *sess); void tty_rel_gone(struct tty *tp); #define tty_lock(tp) mtx_lock((tp)->t_mtx) #define tty_unlock(tp) mtx_unlock((tp)->t_mtx) #define tty_lock_owned(tp) mtx_owned((tp)->t_mtx) #define tty_lock_assert(tp,ma) mtx_assert((tp)->t_mtx, (ma)) #define tty_getlock(tp) ((tp)->t_mtx) /* Device node creation. */ int tty_makedevf(struct tty *tp, struct ucred *cred, int flags, const char *fmt, ...) __printflike(4, 5); #define TTYMK_CLONING 0x1 #define tty_makedev(tp, cred, fmt, ...) \ (void )tty_makedevf((tp), (cred), 0, (fmt), ## __VA_ARGS__) #define tty_makealias(tp,fmt,...) \ make_dev_alias((tp)->t_dev, fmt, ## __VA_ARGS__) /* Signalling processes. */ void tty_signal_sessleader(struct tty *tp, int signal); void tty_signal_pgrp(struct tty *tp, int signal); /* Waking up readers/writers. */ int tty_wait(struct tty *tp, struct cv *cv); int tty_wait_background(struct tty *tp, struct thread *td, int sig); int tty_timedwait(struct tty *tp, struct cv *cv, int timo); void tty_wakeup(struct tty *tp, int flags); /* System messages. */ int tty_checkoutq(struct tty *tp); int tty_putchar(struct tty *tp, char c); +int tty_putstrn(struct tty *tp, const char *p, size_t n); int tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td); int tty_ioctl_compat(struct tty *tp, u_long cmd, caddr_t data, int fflag, struct thread *td); void tty_set_winsize(struct tty *tp, const struct winsize *wsz); void tty_init_console(struct tty *tp, speed_t speed); void tty_flush(struct tty *tp, int flags); void tty_hiwat_in_block(struct tty *tp); void tty_hiwat_in_unblock(struct tty *tp); dev_t tty_udev(struct tty *tp); #define tty_opened(tp) ((tp)->t_flags & TF_OPENED) #define tty_gone(tp) ((tp)->t_flags & TF_GONE) #define tty_softc(tp) ((tp)->t_devswsoftc) #define tty_devname(tp) devtoname((tp)->t_dev) /* Status line printing. */ void tty_info(struct tty *tp); /* /dev/console selection. */ void ttyconsdev_select(const char *name); /* Pseudo-terminal hooks. */ int pts_alloc(int fflags, struct thread *td, struct file *fp); int pts_alloc_external(int fd, struct thread *td, struct file *fp, struct cdev *dev, const char *name); /* Drivers and line disciplines also need to call these. */ #include #include #include #endif /* _KERNEL */ #endif /* !_SYS_TTY_H_ */