/* Copyright (C) 1995,96,98,99, 2002 Free Software Foundation, Inc. Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. The GNU Hurd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The GNU Hurd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include #include #include #include #include #include #include #include #include #ifdef TERM_DEFINE_EI #define TERM_EI #else #define TERM_EI __extern_inline #endif #undef MDMBUF #undef ECHO #undef TOSTOP #undef FLUSHO #undef PENDIN #undef NOFLSH #include #define CHAR_EOT '\004' /* C-d */ #define CHAR_DC1 '\021' /* C-q */ #define CHAR_DC2 '\022' /* C-r */ #define CHAR_DC3 '\023' /* C-s */ #define CHAR_USER_QUOTE '\377' /* break quoting, etc. */ /* This bit specifies control */ #define CTRL_BIT 0x40 /* XXX These belong in */ #ifdef IUCLC #define ILCASE IUCLC #else #define ILCASE (1 << 14) #endif #ifdef OLCUC #define OLCASE OLCUC #else #define OLCASE (1 << 9) #endif #define OTILDE (1 << 10) /* used in mdmctl device call */ #define MDMCTL_BIS 0 #define MDMCTL_BIC 1 #define MDMCTL_SET 2 /* Directly user-visible state */ struct termios termstate; /* Other state with the following bits: */ long termflags; #define USER_OUTPUT_SUSP 0x00000001 /* user has suspended output */ #define TTY_OPEN 0x00000002 /* someone has us open */ #define LAST_SLASH 0x00000004 /* last input char was \ */ #define LAST_LNEXT 0x00000008 /* last input char was VLNEXT */ #define INSIDE_HDERASE 0x00000010 /* inside \.../ hardcopy erase pair */ #define SENT_VSTOP 0x00000020 /* we've sent VSTOP to IXOFF peer */ #define FLUSH_OUTPUT 0x00000040 /* user wants output flushed */ #define NO_CARRIER 0x00000080 /* carrier is absent */ #define EXCL_USE 0x00000100 /* user accessible exclusive use */ #define NO_OWNER 0x00000200 /* there is no foreground_id */ #define ICKY_ASYNC 0x00000400 /* some user has set O_ASYNC */ /* Use a high watermark that allows about as much input as once as other operating systems do. Using something just a bit smaller than a power of 2 helps to make maximum use of the buffer and avoid reallocation for just a few bytes. */ #define QUEUE_LOWAT 200 #define QUEUE_HIWAT 8100 /* Global lock */ pthread_mutex_t global_lock; /* Wakeup when NO_CARRIER turns off */ pthread_cond_t carrier_alert; /* Wakeup for select */ pthread_cond_t select_alert; /* Wakeup for pty select, if not null */ pthread_cond_t *pty_select_alert; /* Bucket for all our ports. */ struct port_bucket *term_bucket; /* Port class for tty control ports */ struct port_class *tty_cntl_class; /* Port class for tty I/O ports */ struct port_class *tty_class; /* Port class for ctty ID ports */ struct port_class *cttyid_class; /* Port class for pty master ports */ struct port_class *pty_class; /* Port class for pty control ports */ struct port_class *pty_cntl_class; /* Trivfs control structure for the tty */ struct trivfs_control *termctl; /* Trivfs control structure for the pty */ struct trivfs_control *ptyctl; /* The queues we use */ struct queue *inputq, *rawq, *outputq; /* Plain pass-through input */ int remote_input_mode; /* External processing mode */ int external_processing; /* Terminal owner */ uid_t term_owner; /* Terminal group */ uid_t term_group; /* Terminal mode */ mode_t term_mode; /* XXX Including or leads to "ECHO undeclared" errors in munge.c or users.c. */ struct winsize; /* Functions a bottom half defines */ struct bottomhalf { enum term_bottom_type type; error_t (*init) (void); error_t (*fini) (void); error_t (*gwinsz) (struct winsize *size); error_t (*start_output) (void); error_t (*set_break) (void); error_t (*clear_break) (void); error_t (*abandon_physical_output) (void); error_t (*suspend_physical_output) (void); int (*pending_output_size) (void); error_t (*notice_input_flushed) (void); error_t (*assert_dtr) (void); error_t (*desert_dtr) (void); error_t (*set_bits) (struct termios *state); error_t (*mdmctl) (int how, int bits); error_t (*mdmstate) (int *state); }; const struct bottomhalf *bottom; extern const struct bottomhalf devio_bottom, hurdio_bottom, ptyio_bottom; /* Character queues */ #define QUEUE_QUOTE_MARK 0xf000 typedef short quoted_char; struct queue { int susp; int lowat; int hiwat; short *cs, *ce; int arraylen; pthread_cond_t *wait; quoted_char array[0]; }; struct queue *create_queue (int size, int lowat, int hiwat); extern int qsize (struct queue *q); extern int qavail (struct queue *q); extern void clear_queue (struct queue *q); extern quoted_char dequeue_quote (struct queue *q); extern char dequeue (struct queue *q); extern void enqueue_internal (struct queue **qp, quoted_char c); extern void enqueue (struct queue **qp, char c); extern void enqueue_quote (struct queue **qp, char c); extern char unquote_char (quoted_char c); extern int char_quoted_p (quoted_char c); extern short queue_erase (struct queue *q); #if defined(__USE_EXTERN_INLINES) || defined(TERM_DEFINE_EI) /* Return the number of characters in Q. */ TERM_EI int qsize (struct queue *q) { return q->ce - q->cs; } /* Return nonzero if characters can be added to Q. */ TERM_EI int qavail (struct queue *q) { return !q->susp; } /* Flush all the characters from Q. */ TERM_EI void clear_queue (struct queue *q) { q->susp = 0; q->cs = q->ce = q->array; pthread_cond_broadcast (q->wait); pthread_cond_broadcast (&select_alert); if (q == inputq && pty_select_alert != NULL) pthread_cond_broadcast (pty_select_alert); } #endif /* Use extern inlines. */ /* Should be below, but inlines need it. */ void call_asyncs (int dir); #if defined(__USE_EXTERN_INLINES) || defined(TERM_DEFINE_EI) /* Return the next character off Q; leave the quoting bit on. */ TERM_EI quoted_char dequeue_quote (struct queue *q) { int beep = 0; assert (qsize (q)); if (q->susp && (qsize (q) < q->lowat)) { q->susp = 0; beep = 1; } if (qsize (q) == 1) beep = 1; if (beep) { pthread_cond_broadcast (q->wait); pthread_cond_broadcast (&select_alert); if (q == inputq && pty_select_alert != NULL) pthread_cond_broadcast (pty_select_alert); else if (q == outputq) call_asyncs (O_WRITE); } return *q->cs++; } /* Return the next character off Q. */ TERM_EI char dequeue (struct queue *q) { return dequeue_quote (q) & ~QUEUE_QUOTE_MARK; } #endif /* Use extern inlines. */ struct queue *reallocate_queue (struct queue *); #if defined(__USE_EXTERN_INLINES) || defined(TERM_DEFINE_EI) /* Add C to *QP. */ TERM_EI void enqueue_internal (struct queue **qp, quoted_char c) { struct queue *q = *qp; if (q->ce - q->array == q->arraylen) q = *qp = reallocate_queue (q); *q->ce++ = c; if (qsize (q) == 1) { pthread_cond_broadcast (q->wait); pthread_cond_broadcast (&select_alert); if (q == inputq) { if (pty_select_alert != NULL) pthread_cond_broadcast (pty_select_alert); call_asyncs (O_READ); } } if (!q->susp && (qsize (q) > q->hiwat)) q->susp = 1; } /* Add C to *QP. */ TERM_EI void enqueue (struct queue **qp, char c) { enqueue_internal (qp, c); } /* Add C to *QP, marking it with a quote. */ TERM_EI void enqueue_quote (struct queue **qp, char c) { enqueue_internal (qp, c | QUEUE_QUOTE_MARK); } /* Return the unquoted version of a quoted_char. */ TERM_EI char unquote_char (quoted_char c) { return c & ~QUEUE_QUOTE_MARK; } /* Tell if a quoted_char is actually quoted. */ TERM_EI int char_quoted_p (quoted_char c) { return c & QUEUE_QUOTE_MARK; } /* Remove the most recently enqueue character from Q; leaving the quote mark on. */ TERM_EI short queue_erase (struct queue *q) { short answer; int beep = 0; assert (qsize (q)); answer = *--q->ce; if (q->susp && (qsize (q) < q->lowat)) { q->susp = 0; beep = 1; } if (qsize (q) == 0) beep = 1; if (beep) { pthread_cond_broadcast (q->wait); pthread_cond_broadcast (&select_alert); if (q == inputq && pty_select_alert != NULL) pthread_cond_broadcast (pty_select_alert); } return answer; } #endif /* Use extern inlines. */ /* Functions devio is supposed to call */ int input_character (int); void report_carrier_on (void); void report_carrier_off (void); void report_carrier_error (error_t); /* Other decls */ error_t drop_output (void); void send_signal (int); error_t drain_output (); void output_character (int); void copy_rawq (void); void rescan_inputq (void); void write_character (int); void init_users (void); extern char *tty_arg; extern dev_t rdev; /* kludge--these are pty versions of trivfs_S_io_* functions called by the real functions in users.c to do work for ptys. */ error_t pty_io_write (struct trivfs_protid *, char *, mach_msg_type_number_t, mach_msg_type_number_t *); error_t pty_io_read (struct trivfs_protid *, char **, mach_msg_type_number_t *, mach_msg_type_number_t); error_t pty_io_readable (size_t *); error_t pty_io_select (struct trivfs_protid *, mach_port_t, int *); error_t pty_open_hook (struct trivfs_control *, struct iouser *, int); error_t pty_po_create_hook (struct trivfs_peropen *); error_t pty_po_destroy_hook (struct trivfs_peropen *);