diff options
Diffstat (limited to 'term/main.c')
-rw-r--r-- | term/main.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/term/main.c b/term/main.c new file mode 100644 index 00000000..9cc32d4d --- /dev/null +++ b/term/main.c @@ -0,0 +1,451 @@ +/* main.c - A translator that emulates a terminal. + Copyright (C) 1995,96,97,2000,02 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 "term.h" +#include <hurd.h> +#include <fcntl.h> +#include <hurd/trivfs.h> +#include <stdio.h> +#include <argp.h> +#include <hurd/fsys.h> +#include <string.h> +#include <error.h> +#include <inttypes.h> +#include <argz.h> + +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (term); + +int trivfs_fstype = FSTYPE_TERM; +int trivfs_fsid = 0; +int trivfs_support_read = 1; +int trivfs_support_write = 1; +int trivfs_support_exec = 0; + +int trivfs_allow_open = O_READ|O_WRITE; + +enum tty_type { T_NONE = 0, T_DEVICE, T_HURDIO, T_PTYMASTER, T_PTYSLAVE }; +static const char *const tty_type_names[] = +{ + [T_DEVICE] = "device", + [T_HURDIO] = "hurdio", + [T_PTYMASTER] = "pty-master", + [T_PTYSLAVE] = "pty-slave", +}; + + +/* The argument line options. */ +char *tty_name; +enum tty_type tty_type; +char *tty_arg; +dev_t rdev; + +int +demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + extern int term_server (mach_msg_header_t *, mach_msg_header_t *); + extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *); + extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *); + + return (trivfs_demuxer (inp, outp) + || term_server (inp, outp) + || tioctl_server (inp, outp) + || device_reply_server (inp, outp)); +} + +static struct argp_option options[] = +{ + {"rdev", 'n', "ID", 0, + "The stat rdev number for this node; may be either a" + " single integer, or of the form MAJOR,MINOR"}, + {"name", 'N', "NAME", 0, + "The name of this node, to be returned by term_get_nodename."}, + {"type", 'T', "TYPE", 0, + "Backend type, see below. This determines the meaning of the argument."}, + {0} +}; + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + struct + { + dev_t rdev; + int rdev_set; + enum tty_type type; + char *name; + char *arg; + } *const v = state->hook; + + switch (opt) + { + default: + return ARGP_ERR_UNKNOWN; + + case ARGP_KEY_INIT: + state->hook = calloc (1, sizeof *v); + break; + case ARGP_KEY_FINI: + free (v); + state->hook = 0; + break; + + case 'n': + { + char *start = arg; + char *end; + + v->rdev = strtoumax (start, &end, 0); + if (*end == ',') + { + /* MAJOR,MINOR form. */ + start = end; + v->rdev = (rdev << 8) + strtoul (start, &end, 0); + } + + if (end == start || *end != '\0') + { + argp_error (state, "%s: Invalid argument to --rdev", arg); + return EINVAL; + } + + v->rdev_set = 1; + } + break; + + case 'N': + v->name = arg; + break; + + case ARGP_KEY_ARG: + if (!v->name && state->input == 0) + v->name = arg; + else if (!v->type && state->input == 0) + { + case 'T': + if (!strcmp (arg, "device")) + v->type = T_DEVICE; + else if (!strcmp (arg, "hurdio")) + v->type = T_HURDIO; + else if (!strcmp (arg, "pty-master")) + v->type = T_PTYMASTER; + else if (!strcmp (arg, "pty-slave")) + v->type = T_PTYSLAVE; + else + { + argp_error (state, "Invalid terminal type"); + return EINVAL; + } + } + else if (!v->arg) + v->arg = arg; + else + { + argp_error (state, "Too many arguments"); + return EINVAL; + } + break; + + case ARGP_KEY_END: + if ((v->type && v->type != T_HURDIO && v->arg == 0) + || (state->input == 0 && v->name == 0)) + { + argp_error (state, "Too few arguments"); + return EINVAL; + } + break; + + case ARGP_KEY_SUCCESS: + /* Apply the values we've collected. */ + if (v->rdev_set) + rdev = v->rdev; + if (v->name) + { + free (tty_name); + tty_name = strdup (v->name); + } + if (state->input == 0) /* This is startup time. */ + { + tty_type = v->type ?: T_HURDIO; + tty_arg = v->arg ? strdup (v->arg) : 0; + } + else if (v->type || v->arg) + { + /* Dynamic backend switch. */ + if (!v->type) + v->type = T_HURDIO; + switch (v->type) + { + case T_PTYMASTER: + case T_PTYSLAVE: + /* Cannot dynamically switch to pty flavors. */ + return EINVAL; + default: + break; + } + switch (tty_type) + { + case T_PTYMASTER: + case T_PTYSLAVE: + /* Cannot dynamically switch from pty flavors either. */ + return EINVAL; + default: + break; + } + + pthread_mutex_lock (&global_lock); + (*bottom->fini) (); + + tty_type = v->type; + switch (tty_type) + { + case T_DEVICE: + bottom = &devio_bottom; + break; + case T_HURDIO: + bottom = &hurdio_bottom; + break; + default: + assert (! "impossible type"); + break; + } + free (tty_arg); + tty_arg = strdup (v->arg); + error_t err = (*bottom->init) (); + if (err == 0 && (termflags & TTY_OPEN)) + err = (*bottom->assert_dtr) (); + pthread_mutex_unlock (&global_lock); + return err; + } + break; + + case ARGP_KEY_ERROR: + break; + } + return 0; +} + +static struct argp term_argp = + { options, parse_opt, "NAME TYPE ARG", + "A translator that implements POSIX termios discipline.\v" + "Possible values for TYPE:\n" + " device Use Mach device ARG for underlying i/o.\n" + " hurdio Use file ARG for i/o, underlying node if no ARG.\n" + " pty-master Master for slave at ARG.\n"\ + " pty-slave Slave for master at ARG.\n"\ + "\n" + "The default type is `hurdio', so no arguments uses the underlying node.\n" + "The filename of the node that the translator is attached to should be\n" + "supplied in NAME.\n" + }; + +struct argp *trivfs_runtime_argp = &term_argp; + +error_t +trivfs_append_args (struct trivfs_control *fsys, + char **argz, size_t *argz_len) +{ + error_t err = 0; + + if (rdev) + { + char buf[64]; + snprintf (buf, sizeof buf, "--rdev=%#jx", (uintmax_t)rdev); + err = argz_add (argz, argz_len, buf); + } + + if (!err && tty_name) + err = argz_add (argz, argz_len, "--name") + ?: argz_add (argz, argz_len, tty_name); + + if (!err && tty_type != T_HURDIO) + err = argz_add (argz, argz_len, "--type") + ?: argz_add (argz, argz_len, tty_type_names[tty_type]); + + if (!err && tty_arg) + err = argz_add (argz, argz_len, tty_arg); + + return err; +} + +int +main (int argc, char **argv) +{ + struct port_class *ourclass, *ourcntlclass; + struct port_class *peerclass, *peercntlclass; + struct trivfs_control **ourcntl, **peercntl; + mach_port_t bootstrap, right; + struct stat st; + error_t err; + int openmode; + + term_bucket = ports_create_bucket (); + + trivfs_add_control_port_class (&tty_cntl_class); + trivfs_add_control_port_class (&pty_cntl_class); + trivfs_add_protid_port_class (&tty_class); + trivfs_add_protid_port_class (&pty_class); + + cttyid_class = ports_create_class (0, 0); + + init_users (); + + argp_parse (&term_argp, argc, argv, 0, 0, 0); + + switch (tty_type) + { + case T_DEVICE: + bottom = &devio_bottom; + ourclass = tty_class; + ourcntlclass = tty_cntl_class; + ourcntl = &termctl; + peerclass = 0; + peercntlclass = 0; + peercntl = 0; + openmode = 0; + break; + + case T_HURDIO: + bottom = &hurdio_bottom; + ourclass = tty_class; + ourcntlclass = tty_cntl_class; + ourcntl = &termctl; + peerclass = 0; + peercntlclass = 0; + peercntl = 0; + + /* We don't want to have a writable peropen on the underlying node + when we'll never use it. Ideally, we shouldn't open one until we + do need it, in case it has an affect on the underlying node (like + keeping DTR high and such). */ + openmode = O_RDWR; + break; + + case T_PTYMASTER: + bottom = &ptyio_bottom; + ourclass = pty_class; + ourcntlclass = pty_cntl_class; + ourcntl = &ptyctl; + peerclass = tty_class; + peercntlclass = tty_cntl_class; + peercntl = &termctl; + openmode = 0; + break; + + case T_PTYSLAVE: + bottom = &ptyio_bottom; + ourclass = tty_class; + ourcntlclass = tty_cntl_class; + ourcntl = &termctl; + peerclass = pty_class; + peercntlclass = pty_cntl_class; + peercntl = &ptyctl; + openmode = 0; + break; + + default: + /* Should not happen. */ + error (1, 0, "Unknown terminal type"); + /*NOTREACHED*/ + return 1; + } + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + /* Set our node. */ + err = trivfs_startup (bootstrap, openmode, + ourcntlclass, term_bucket, ourclass, term_bucket, + ourcntl); + if (err) + error (1, err, "Starting translator"); + + /* For ptys, the nodename depends on which half is used. For now just use + the hook to store the nodename. */ + (*ourcntl)->hook = tty_name; + + /* Set peer. */ + if (peerclass) + { + char *peer_name = tty_arg; + file_t file = file_name_lookup (peer_name, O_CREAT|O_NOTRANS, 0666); + + if (file == MACH_PORT_NULL) + err = errno; + + if (! err) + err = trivfs_create_control (file, peercntlclass, term_bucket, + peerclass, term_bucket, peercntl); + if (! err) + { + right = ports_get_send_right (*peercntl); + err = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET, + 0, 0, 0, right, MACH_MSG_TYPE_COPY_SEND); + mach_port_deallocate (mach_task_self (), right); + } + + if (err) + error (1, err, "%s", peer_name); + + (*peercntl)->hook = peer_name; + ports_port_deref (*peercntl); + } + + memset (&termstate, 0, sizeof (termstate)); + termflags = NO_CARRIER | NO_OWNER; + pthread_mutex_init (&global_lock, NULL); + + /* Initialize status from underlying node. */ + err = io_stat ((*ourcntl)->underlying, &st); + if (err) + { + /* We cannot stat the underlying node. Fallback to the defaults. */ + term_owner = term_group = 0; + term_mode = (bottom == &ptyio_bottom ? DEFFILEMODE : S_IRUSR | S_IWUSR); + } + else + { + term_owner = st.st_uid; + term_group = st.st_gid; + term_mode = (st.st_mode & ACCESSPERMS); + } + term_mode |= S_IFCHR | S_IROOT; + + inputq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT); + + rawq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT); + + outputq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT); + + err = (*bottom->init) (); + if (err) + error (1, err, "Initializing bottom handler"); + + pthread_cond_init (&carrier_alert, NULL); + pthread_cond_init (&select_alert, NULL); + + /* Launch. */ + ports_manage_port_operations_multithread (term_bucket, demuxer, 0, 0, 0); + + return 0; +} |