/* main.c - A translator that emulates a terminal. Copyright (C) 1995, 1996, 1997, 2000, 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 "term.h" #include #include #include #include #include #include #include #include #include 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; /* The argument line options. */ char *tty_name; enum { T_NONE = 0, T_DEVICE, T_HURDIO, T_PTYMASTER, T_PTYSLAVE } tty_type; char *tty_arg; int 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"}, {0} }; static error_t parse_opt (int opt, char *arg, struct argp_state *state) { switch (opt) { default: return ARGP_ERR_UNKNOWN; case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: case ARGP_KEY_ERROR: break; case 'n': { char *start = arg; char *end; rdev = strtoul (start, &end, 0); if (*end == ',') { /* MAJOR,MINOR form. */ start = end; rdev = (rdev << 8) + strtoul (start, &end, 0); } if (end == start || *end != '\0') { argp_error (state, "%s: Invalid argument to --rdev", arg); return EINVAL; } } break; case ARGP_KEY_ARG: if (!tty_name) tty_name = arg; else if (!tty_type) { if (!strcmp (arg, "device")) tty_type = T_DEVICE; else if (!strcmp (arg, "hurdio")) tty_type = T_HURDIO; else if (!strcmp (arg, "pty-master")) tty_type = T_PTYMASTER; else if (!strcmp (arg, "pty-slave")) tty_type = T_PTYSLAVE; else { argp_error (state, "Invalid terminal type"); return EINVAL; } } else if (!tty_arg) tty_arg = arg; else argp_error (state, "Too many arguments"); break; case ARGP_KEY_END: if (!tty_name || !tty_type || !tty_arg) argp_error (state, "Too few arguments"); break; } return 0; } static struct argp term_argp = { options, parse_opt, "NAME TYPE ARG", "A translator that emulates a terminal.\v"\ "Possible values for TYPE:\n"\ " device Use Mach device ARG as bottom handler.\n"\ " hurdio Use file port ARG as bottom handler.\n"\ " pty-master Master for slave at ARG.\n"\ " pty-slave Slave for master at ARG.\n"\ "\n"\ "The filename of the node that the translator is attached to should be\n"\ "supplied in NAME.\n" }; 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; 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; break; case T_HURDIO: bottom = &hurdio_bottom; ourclass = tty_class; ourcntlclass = tty_cntl_class; ourcntl = &termctl; peerclass = 0; peercntlclass = 0; peercntl = 0; 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; 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; 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, 0, 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, peer_name); (*peercntl)->hook = peer_name; ports_port_deref (*peercntl); } memset (&termstate, 0, sizeof (termstate)); termflags = NO_CARRIER | NO_OWNER; mutex_init (&global_lock); /* 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"); 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); return 0; }