aboutsummaryrefslogtreecommitdiff
path: root/term/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'term/main.c')
-rw-r--r--term/main.c428
1 files changed, 340 insertions, 88 deletions
diff --git a/term/main.c b/term/main.c
index d0702e83..405e7cd8 100644
--- a/term/main.c
+++ b/term/main.c
@@ -1,5 +1,5 @@
-/*
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+/* 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.
@@ -23,21 +23,40 @@
#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; /* pid?? */
+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;
-struct port_class *trivfs_protid_portclasses[2];
-struct port_class *trivfs_cntl_portclasses[2];
-int trivfs_protid_nportclasses = 2;
-int trivfs_cntl_nportclasses = 2;
+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)
@@ -52,41 +71,248 @@ demuxer (mach_msg_header_t *inp, mach_msg_header_t *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;
+ }
+
+ 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) ();
+ 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;
- enum {T_DEVICE, T_PTYMASTER, T_PTYSLAVE} type;
+ mach_port_t bootstrap, right;
+ struct stat st;
+ error_t err;
+ int openmode;
term_bucket = ports_create_bucket ();
-
- tty_cntl_class = ports_create_class (trivfs_clean_cntl, 0);
- pty_cntl_class = ports_create_class (trivfs_clean_cntl, 0);
- tty_class = ports_create_class (trivfs_clean_protid, 0);
- pty_class = ports_create_class (trivfs_clean_protid, 0);
+
+ 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);
-
- trivfs_protid_portclasses[0] = tty_class;
- trivfs_protid_portclasses[1] = pty_class;
- trivfs_cntl_portclasses[0] = tty_cntl_class;
- trivfs_cntl_portclasses[1] = pty_cntl_class;
init_users ();
- task_get_bootstrap_port (mach_task_self (), &bootstrap);
-
- if (argc != 4)
- {
- fprintf (stderr, "Usage: term ttyname type arg\n");
- exit (1);
- }
+ argp_parse (&term_argp, argc, argv, 0, 0, 0);
- if (!strcmp (argv[2], "device"))
+ switch (tty_type)
{
- type = T_DEVICE;
+ case T_DEVICE:
bottom = &devio_bottom;
ourclass = tty_class;
ourcntlclass = tty_cntl_class;
@@ -94,11 +320,26 @@ main (int argc, char **argv)
peerclass = 0;
peercntlclass = 0;
peercntl = 0;
- pterm_name = argv[3];
- }
- else if (!strcmp (argv[2], "pty-master"))
- {
- type = T_PTYMASTER;
+ 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;
@@ -106,10 +347,10 @@ main (int argc, char **argv)
peerclass = tty_class;
peercntlclass = tty_cntl_class;
peercntl = &termctl;
- }
- else if (!strcmp (argv[2], "pty-slave"))
- {
- type = T_PTYSLAVE;
+ openmode = 0;
+ break;
+
+ case T_PTYSLAVE:
bottom = &ptyio_bottom;
ourclass = tty_class;
ourcntlclass = tty_cntl_class;
@@ -117,85 +358,96 @@ main (int argc, char **argv)
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;
}
- else
- {
- fprintf (stderr,
- "Allowable types are device, pty-master, and pty-slave.\n");
- exit (1);
- }
-
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
if (bootstrap == MACH_PORT_NULL)
- {
- fprintf (stderr, "Must be started as a translator\n");
- exit (1);
- }
+ error (1, 0, "Must be started as a translator");
- /* Set our node */
- errno = trivfs_startup (bootstrap, 0,
- ourcntlclass, term_bucket, ourclass, term_bucket,
- ourcntl);
- if (errno)
- {
- perror ("Starting translator");
- exit (1);
- }
+ /* 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 = argv[1];
+ (*ourcntl)->hook = tty_name;
- /* Set peer */
+ /* Set peer. */
if (peerclass)
{
- char *peer_name = argv[3];
+ char *peer_name = tty_arg;
file_t file = file_name_lookup (peer_name, O_CREAT|O_NOTRANS, 0666);
- if (file != MACH_PORT_NULL)
- errno = 0;
-
- if (! errno)
- errno = trivfs_create_control (file, peercntlclass, term_bucket,
- peerclass, term_bucket, peercntl);
- if (! errno)
- errno = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
- 0, 0, 0,
- ports_get_right (*peercntl),
- MACH_MSG_TYPE_MAKE_SEND);
+ if (file == MACH_PORT_NULL)
+ err = errno;
- if (errno)
+ if (! err)
+ err = trivfs_create_control (file, peercntlclass, term_bucket,
+ peerclass, term_bucket, peercntl);
+ if (! err)
{
- perror (peer_name);
- exit (1);
+ 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);
}
- bzero (&termstate, sizeof (termstate));
+ memset (&termstate, 0, sizeof (termstate));
termflags = NO_CARRIER | NO_OWNER;
mutex_init (&global_lock);
- term_owner = term_group = 0;
- term_mode = (bottom == &ptyio_bottom ? 0666 : 0600) | S_IFCHR;
+ /* 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, 100, 300);
- rawq = create_queue (256, 100, 300);
- outputq = create_queue (256, 100, 300);
-
- if (bottom == &ptyio_bottom)
- ptyio_init ();
+ 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");
condition_init (&carrier_alert);
condition_init (&select_alert);
condition_implies (inputq->wait, &select_alert);
condition_implies (outputq->wait, &select_alert);
- /* Launch */
- ports_manage_port_operations_multithread (term_bucket, demuxer, 0, 0,
- 0, MACH_PORT_NULL);
+ /* Launch. */
+ ports_manage_port_operations_multithread (term_bucket, demuxer, 0, 0, 0);
return 0;
-}
-
+}