Home | History | Annotate | Download | only in src
      1 /*-
      2  * Copyright (c) 1999 Brian Somers <brian (at) Awfulhak.org>
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  *
     26  * $FreeBSD: src/usr.sbin/ppp/tty.c,v 1.32.10.1.4.1 2010/12/21 17:10:29 kensmith Exp $
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <sys/un.h>
     31 #if defined(__OpenBSD__) || defined(__NetBSD__)
     32 #include <sys/ioctl.h>
     33 #endif
     34 
     35 #include <errno.h>
     36 #include <fcntl.h>
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <sysexits.h>
     41 #include <sys/uio.h>
     42 #include <termios.h>
     43 #include <ttyent.h>
     44 #include <unistd.h>
     45 #ifndef NONETGRAPH
     46 #include <netgraph.h>
     47 #include <netgraph/ng_async.h>
     48 #include <netgraph/ng_message.h>
     49 #include <netgraph/ng_ppp.h>
     50 #include <netgraph/ng_tty.h>
     51 #endif
     52 
     53 #include "layer.h"
     54 #include "defs.h"
     55 #include "mbuf.h"
     56 #include "log.h"
     57 #include "timer.h"
     58 #include "lqr.h"
     59 #include "hdlc.h"
     60 #include "throughput.h"
     61 #include "fsm.h"
     62 #include "lcp.h"
     63 #include "ccp.h"
     64 #include "link.h"
     65 #include "async.h"
     66 #include "descriptor.h"
     67 #include "physical.h"
     68 #include "mp.h"
     69 #include "chat.h"
     70 #include "auth.h"
     71 #include "chap.h"
     72 #include "cbcp.h"
     73 #include "datalink.h"
     74 #include "main.h"
     75 #include "id.h"
     76 #include "tty.h"
     77 
     78 #if defined(__mac68k__) || defined(__macppc__)
     79 #undef	CRTS_IFLOW
     80 #undef	CCTS_OFLOW
     81 #define	CRTS_IFLOW	CDTRCTS
     82 #define	CCTS_OFLOW	CDTRCTS
     83 #endif
     84 
     85 #define	Online(dev)	((dev)->mbits & TIOCM_CD)
     86 
     87 struct ttydevice {
     88   struct device dev;		/* What struct physical knows about */
     89   struct pppTimer Timer;	/* CD checks */
     90   int mbits;			/* Current DCD status */
     91   int carrier_seconds;		/* seconds before CD is *required* */
     92 #ifndef NONETGRAPH
     93   struct {
     94     unsigned speed;		/* Pre-line-discipline speed */
     95     int fd;			/* Pre-line-discipline fd */
     96     int disc;			/* Old line-discipline */
     97   } real;
     98   char hook[sizeof NG_ASYNC_HOOK_SYNC]; /* our ng_socket hook */
     99   int cs;			/* A netgraph control socket (maybe) */
    100 #endif
    101   struct termios ios;		/* To be able to reset from raw mode */
    102 };
    103 
    104 #define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
    105 
    106 unsigned
    107 tty_DeviceSize(void)
    108 {
    109   return sizeof(struct ttydevice);
    110 }
    111 
    112 /*
    113  * tty_Timeout() watches the DCD signal and mentions it if it's status
    114  * changes.
    115  */
    116 static void
    117 tty_Timeout(void *data)
    118 {
    119   struct physical *p = data;
    120   struct ttydevice *dev = device2tty(p->handler);
    121   int ombits, change;
    122 
    123   timer_Stop(&dev->Timer);
    124   dev->Timer.load = SECTICKS;		/* Once a second please */
    125   timer_Start(&dev->Timer);
    126   ombits = dev->mbits;
    127 
    128   if (p->fd >= 0) {
    129     if (ioctl(p->fd, TIOCMGET, &dev->mbits) < 0) {
    130       /* we must be a pty ? */
    131       if (p->cfg.cd.necessity != CD_DEFAULT)
    132         log_Printf(LogWARN, "%s: Carrier ioctl not supported, "
    133                    "using ``set cd off''\n", p->link.name);
    134       timer_Stop(&dev->Timer);
    135       dev->mbits = TIOCM_CD;
    136       return;
    137     }
    138   } else
    139     dev->mbits = 0;
    140 
    141   if (ombits == -1) {
    142     /* First time looking for carrier */
    143     if (Online(dev))
    144       log_Printf(LogPHASE, "%s: %s: CD detected\n", p->link.name, p->name.full);
    145     else if (++dev->carrier_seconds >= dev->dev.cd.delay) {
    146       if (dev->dev.cd.necessity == CD_REQUIRED)
    147         log_Printf(LogPHASE, "%s: %s: Required CD not detected\n",
    148                    p->link.name, p->name.full);
    149       else {
    150         log_Printf(LogPHASE, "%s: %s doesn't support CD\n",
    151                    p->link.name, p->name.full);
    152         dev->mbits = TIOCM_CD;		/* Dodgy null-modem cable ? */
    153       }
    154       timer_Stop(&dev->Timer);
    155       /* tty_AwaitCarrier() will notice */
    156     } else {
    157       /* Keep waiting */
    158       log_Printf(LogDEBUG, "%s: %s: Still no carrier (%d/%d)\n",
    159                  p->link.name, p->name.full, dev->carrier_seconds,
    160                  dev->dev.cd.delay);
    161       dev->mbits = -1;
    162     }
    163   } else {
    164     change = ombits ^ dev->mbits;
    165     if (change & TIOCM_CD) {
    166       if (dev->mbits & TIOCM_CD)
    167         log_Printf(LogDEBUG, "%s: offline -> online\n", p->link.name);
    168       else {
    169         log_Printf(LogDEBUG, "%s: online -> offline\n", p->link.name);
    170         log_Printf(LogPHASE, "%s: Carrier lost\n", p->link.name);
    171         datalink_Down(p->dl, CLOSE_NORMAL);
    172         timer_Stop(&dev->Timer);
    173       }
    174     } else
    175       log_Printf(LogDEBUG, "%s: Still %sline\n", p->link.name,
    176                  Online(dev) ? "on" : "off");
    177   }
    178 }
    179 
    180 static void
    181 tty_StartTimer(struct physical *p)
    182 {
    183   struct ttydevice *dev = device2tty(p->handler);
    184 
    185   timer_Stop(&dev->Timer);
    186   dev->Timer.load = SECTICKS;
    187   dev->Timer.func = tty_Timeout;
    188   dev->Timer.name = "tty CD";
    189   dev->Timer.arg = p;
    190   log_Printf(LogDEBUG, "%s: Using tty_Timeout [%p]\n",
    191              p->link.name, tty_Timeout);
    192   timer_Start(&dev->Timer);
    193 }
    194 
    195 static int
    196 tty_AwaitCarrier(struct physical *p)
    197 {
    198   struct ttydevice *dev = device2tty(p->handler);
    199 
    200   if (dev->dev.cd.necessity == CD_NOTREQUIRED || physical_IsSync(p))
    201     return CARRIER_OK;
    202 
    203   if (dev->mbits == -1) {
    204     if (dev->Timer.state == TIMER_STOPPED) {
    205       dev->carrier_seconds = 0;
    206       tty_StartTimer(p);
    207     }
    208     return CARRIER_PENDING;			/* Not yet ! */
    209   }
    210 
    211   return Online(dev) ? CARRIER_OK : CARRIER_LOST;
    212 }
    213 
    214 #ifdef NONETGRAPH
    215 #define tty_SetAsyncParams	NULL
    216 #define tty_Write		NULL
    217 #define tty_Read		NULL
    218 #else
    219 
    220 static int
    221 isngtty(struct ttydevice *dev)
    222 {
    223   return dev->real.fd != -1;
    224 }
    225 
    226 static void
    227 tty_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
    228 {
    229   struct ttydevice *dev = device2tty(p->handler);
    230   char asyncpath[NG_PATHSIZ];
    231   struct ng_async_cfg cfg;
    232 
    233   if (isngtty(dev)) {
    234     /* Configure the async converter node */
    235 
    236     snprintf(asyncpath, sizeof asyncpath, ".:%s", dev->hook);
    237     memset(&cfg, 0, sizeof cfg);
    238     cfg.enabled = 1;
    239     cfg.accm = mymap | hismap;
    240     cfg.amru = MAX_MTU;
    241     cfg.smru = MAX_MRU;
    242     log_Printf(LogDEBUG, "Configure async node at %s\n", asyncpath);
    243     if (NgSendMsg(dev->cs, asyncpath, NGM_ASYNC_COOKIE,
    244                   NGM_ASYNC_CMD_SET_CONFIG, &cfg, sizeof cfg) < 0)
    245       log_Printf(LogWARN, "%s: Can't configure async node at %s\n",
    246                  p->link.name, asyncpath);
    247   } else
    248     /* No netgraph node, just config the async layer */
    249     async_SetLinkParams(&p->async, mymap, hismap);
    250 }
    251 
    252 static int
    253 LoadLineDiscipline(struct physical *p)
    254 {
    255   struct ttydevice *dev = device2tty(p->handler);
    256   u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
    257   struct ng_mesg *reply;
    258   struct nodeinfo *info;
    259   char ttypath[NG_NODESIZ];
    260   struct ngm_mkpeer ngm;
    261   struct ngm_connect ngc;
    262   int ldisc, cs, ds, hot;
    263   unsigned speed;
    264 
    265   /*
    266    * Don't use the netgraph line discipline for now.  Using it works, but
    267    * carrier cannot be detected via TIOCMGET and the device doesn't become
    268    * selectable with 0 bytes to read when carrier is lost :(
    269    */
    270   return 0;
    271 
    272   reply = (struct ng_mesg *)rbuf;
    273   info = (struct nodeinfo *)reply->data;
    274 
    275   loadmodules(LOAD_VERBOSLY, "netgraph", "ng_tty", "ng_async", "ng_socket",
    276               NULL);
    277 
    278   /* Get the speed before loading the line discipline */
    279   speed = physical_GetSpeed(p);
    280 
    281   if (ioctl(p->fd, TIOCGETD, &dev->real.disc) < 0) {
    282     log_Printf(LogDEBUG, "%s: Couldn't get tty line discipline\n",
    283                p->link.name);
    284     return 0;
    285   }
    286   ldisc = NETGRAPHDISC;
    287   if (ID0ioctl(p->fd, TIOCSETD, &ldisc) < 0) {
    288     log_Printf(LogDEBUG, "%s: Couldn't set NETGRAPHDISC line discipline\n",
    289                p->link.name);
    290     return 0;
    291   }
    292 
    293   /* Get the name of the tty node */
    294   if (ioctl(p->fd, NGIOCGINFO, info) < 0) {
    295     log_Printf(LogWARN, "%s: ioctl(NGIOCGINFO): %s\n", p->link.name,
    296                strerror(errno));
    297     ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
    298     return 0;
    299   }
    300   snprintf(ttypath, sizeof ttypath, "%s:", info->name);
    301 
    302   /* Create a socket node for our endpoint (and to send messages via) */
    303   if (ID0NgMkSockNode(NULL, &cs, &ds) == -1) {
    304     log_Printf(LogWARN, "%s: NgMkSockNode: %s\n", p->link.name,
    305                strerror(errno));
    306     ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
    307     return 0;
    308   }
    309 
    310   /* Set the ``hot char'' on the TTY node */
    311   hot = HDLC_SYN;
    312   log_Printf(LogDEBUG, "%s: Set tty hotchar to 0x%02x\n", p->link.name, hot);
    313   if (NgSendMsg(cs, ttypath, NGM_TTY_COOKIE,
    314       NGM_TTY_SET_HOTCHAR, &hot, sizeof hot) < 0) {
    315     log_Printf(LogWARN, "%s: Can't set hot char\n", p->link.name);
    316     goto failed;
    317   }
    318 
    319   /* Attach an async converter node */
    320   snprintf(ngm.type, sizeof ngm.type, "%s", NG_ASYNC_NODE_TYPE);
    321   snprintf(ngm.ourhook, sizeof ngm.ourhook, "%s", NG_TTY_HOOK);
    322   snprintf(ngm.peerhook, sizeof ngm.peerhook, "%s", NG_ASYNC_HOOK_ASYNC);
    323   log_Printf(LogDEBUG, "%s: Send mkpeer async:%s to %s:%s\n", p->link.name,
    324              ngm.peerhook, ttypath, ngm.ourhook);
    325   if (NgSendMsg(cs, ttypath, NGM_GENERIC_COOKIE,
    326       NGM_MKPEER, &ngm, sizeof ngm) < 0) {
    327     log_Printf(LogWARN, "%s: Can't create %s node\n", p->link.name,
    328                NG_ASYNC_NODE_TYPE);
    329     goto failed;
    330   }
    331 
    332   /* Connect the async node to our socket */
    333   snprintf(ngc.path, sizeof ngc.path, "%s%s", ttypath, NG_TTY_HOOK);
    334   snprintf(ngc.peerhook, sizeof ngc.peerhook, "%s", NG_ASYNC_HOOK_SYNC);
    335   memcpy(ngc.ourhook, ngc.peerhook, sizeof ngc.ourhook);
    336   log_Printf(LogDEBUG, "%s: Send connect %s:%s to .:%s\n", p->link.name,
    337              ngc.path, ngc.peerhook, ngc.ourhook);
    338   if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT,
    339       &ngc, sizeof ngc) < 0) {
    340     log_Printf(LogWARN, "%s: Can't connect .:%s -> %s.%s: %s\n",
    341                p->link.name, ngc.ourhook, ngc.path, ngc.peerhook,
    342                strerror(errno));
    343     goto failed;
    344   }
    345 
    346   /* Get the async node id */
    347   if (NgSendMsg(cs, ngc.path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) {
    348     log_Printf(LogWARN, "%s: Can't request async node info at %s: %s\n",
    349                p->link.name, ngc.path, strerror(errno));
    350     goto failed;
    351   }
    352   if (NgRecvMsg(cs, reply, sizeof rbuf, NULL) < 0) {
    353     log_Printf(LogWARN, "%s: Can't obtain async node info at %s: %s\n",
    354                p->link.name, ngc.path, strerror(errno));
    355     goto failed;
    356   }
    357 
    358   /* All done, set up our device state */
    359   snprintf(dev->hook, sizeof dev->hook, "%s", ngc.ourhook);
    360   dev->cs = cs;
    361   dev->real.fd = p->fd;
    362   p->fd = ds;
    363   dev->real.speed = speed;
    364   physical_SetSync(p);
    365 
    366   tty_SetAsyncParams(p, 0xffffffff, 0xffffffff);
    367   physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
    368   log_Printf(LogPHASE, "%s: Loaded netgraph tty line discipline\n",
    369              p->link.name);
    370 
    371   return 1;
    372 
    373 failed:
    374   ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
    375   close(ds);
    376   close(cs);
    377 
    378   return 0;
    379 }
    380 
    381 static void
    382 UnloadLineDiscipline(struct physical *p)
    383 {
    384   struct ttydevice *dev = device2tty(p->handler);
    385 
    386   if (isngtty(dev)) {
    387     if (!physical_SetSpeed(p, dev->real.speed))
    388       log_Printf(LogWARN, "Couldn't reset tty speed to %d\n", dev->real.speed);
    389     dev->real.speed = 0;
    390     close(p->fd);
    391     p->fd = dev->real.fd;
    392     dev->real.fd = -1;
    393     close(dev->cs);
    394     dev->cs = -1;
    395     *dev->hook = '\0';
    396     if (ID0ioctl(p->fd, TIOCSETD, &dev->real.disc) == 0) {
    397       physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
    398       log_Printf(LogPHASE, "%s: Unloaded netgraph tty line discipline\n",
    399                  p->link.name);
    400     } else
    401       log_Printf(LogWARN, "%s: Failed to unload netgraph tty line discipline\n",
    402                  p->link.name);
    403   }
    404 }
    405 
    406 static ssize_t
    407 tty_Write(struct physical *p, const void *v, size_t n)
    408 {
    409   struct ttydevice *dev = device2tty(p->handler);
    410 
    411   if (isngtty(dev))
    412     return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
    413   else
    414     return write(p->fd, v, n);
    415 }
    416 
    417 static ssize_t
    418 tty_Read(struct physical *p, void *v, size_t n)
    419 {
    420   struct ttydevice *dev = device2tty(p->handler);
    421   char hook[sizeof NG_ASYNC_HOOK_SYNC];
    422 
    423   if (isngtty(dev))
    424     return NgRecvData(p->fd, v, n, hook);
    425   else
    426     return read(p->fd, v, n);
    427 }
    428 
    429 #endif /* NETGRAPH */
    430 
    431 static int
    432 tty_Raw(struct physical *p)
    433 {
    434   struct ttydevice *dev = device2tty(p->handler);
    435   struct termios ios;
    436   int oldflag;
    437 
    438   log_Printf(LogDEBUG, "%s: Entering tty_Raw\n", p->link.name);
    439 
    440   if (p->type != PHYS_DIRECT && p->fd >= 0 && !Online(dev))
    441     log_Printf(LogDEBUG, "%s: Raw: descriptor = %d, mbits = %x\n",
    442               p->link.name, p->fd, dev->mbits);
    443 
    444   if (!physical_IsSync(p)) {
    445 #ifndef NONETGRAPH
    446     if (!LoadLineDiscipline(p))
    447 #endif
    448     {
    449       tcgetattr(p->fd, &ios);
    450       cfmakeraw(&ios);
    451       if (p->cfg.rts_cts)
    452         ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
    453       else
    454         ios.c_cflag |= CLOCAL;
    455 
    456       if (p->type != PHYS_DEDICATED)
    457         ios.c_cflag |= HUPCL;
    458 
    459       if (tcsetattr(p->fd, TCSANOW, &ios) == -1)
    460         log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
    461                    p->link.name);
    462     }
    463   }
    464 
    465   oldflag = fcntl(p->fd, F_GETFL, 0);
    466   if (oldflag < 0)
    467     return 0;
    468   fcntl(p->fd, F_SETFL, oldflag | O_NONBLOCK);
    469 
    470   return 1;
    471 }
    472 
    473 static void
    474 tty_Offline(struct physical *p)
    475 {
    476   struct ttydevice *dev = device2tty(p->handler);
    477 
    478   if (p->fd >= 0) {
    479     timer_Stop(&dev->Timer);
    480     dev->mbits &= ~TIOCM_DTR;	/* XXX: Hmm, what's this supposed to do ? */
    481     if (Online(dev)) {
    482       struct termios tio;
    483 
    484       tcgetattr(p->fd, &tio);
    485       if (cfsetspeed(&tio, B0) == -1 || tcsetattr(p->fd, TCSANOW, &tio) == -1)
    486         log_Printf(LogWARN, "%s: Unable to set physical to speed 0\n",
    487                    p->link.name);
    488     }
    489   }
    490 }
    491 
    492 static void
    493 tty_Cooked(struct physical *p)
    494 {
    495   struct ttydevice *dev = device2tty(p->handler);
    496   int oldflag;
    497 
    498   tty_Offline(p);	/* In case of emergency close()s */
    499 
    500   tcflush(p->fd, TCIOFLUSH);
    501 
    502   if (!physical_IsSync(p) && tcsetattr(p->fd, TCSAFLUSH, &dev->ios) == -1)
    503     log_Printf(LogWARN, "%s: tcsetattr: Unable to restore device settings\n",
    504                p->link.name);
    505 
    506 #ifndef NONETGRAPH
    507   UnloadLineDiscipline(p);
    508 #endif
    509 
    510   if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
    511     fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
    512 }
    513 
    514 static void
    515 tty_StopTimer(struct physical *p)
    516 {
    517   struct ttydevice *dev = device2tty(p->handler);
    518 
    519   timer_Stop(&dev->Timer);
    520 }
    521 
    522 static void
    523 tty_Free(struct physical *p)
    524 {
    525   struct ttydevice *dev = device2tty(p->handler);
    526 
    527   tty_Offline(p);	/* In case of emergency close()s */
    528   free(dev);
    529 }
    530 
    531 static unsigned
    532 tty_Speed(struct physical *p)
    533 {
    534   struct termios ios;
    535 
    536   if (tcgetattr(p->fd, &ios) == -1)
    537     return 0;
    538 
    539   return SpeedToUnsigned(cfgetispeed(&ios));
    540 }
    541 
    542 static const char *
    543 tty_OpenInfo(struct physical *p)
    544 {
    545   struct ttydevice *dev = device2tty(p->handler);
    546   static char buf[13];
    547 
    548   if (Online(dev))
    549     strcpy(buf, "with");
    550   else
    551     strcpy(buf, "no");
    552   strcat(buf, " carrier");
    553 
    554   return buf;
    555 }
    556 
    557 static int
    558 tty_Slot(struct physical *p)
    559 {
    560   struct ttyent *ttyp;
    561   int slot;
    562 
    563   setttyent();
    564   for (slot = 1; (ttyp = getttyent()); ++slot)
    565     if (!strcmp(ttyp->ty_name, p->name.base)) {
    566       endttyent();
    567       return slot;
    568     }
    569 
    570   endttyent();
    571   return -1;
    572 }
    573 
    574 static void
    575 tty_device2iov(struct device *d, struct iovec *iov, int *niov,
    576                int maxiov __unused,
    577 #ifndef NONETGRAPH
    578                int *auxfd, int *nauxfd
    579 #else
    580                int *auxfd __unused, int *nauxfd __unused
    581 #endif
    582                )
    583 {
    584   struct ttydevice *dev;
    585   int sz = physical_MaxDeviceSize();
    586 
    587   iov[*niov].iov_base = d = realloc(d, sz);
    588   if (d == NULL) {
    589     log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
    590     AbortProgram(EX_OSERR);
    591   }
    592   iov[*niov].iov_len = sz;
    593   (*niov)++;
    594 
    595   dev = device2tty(d);
    596 
    597 #ifndef NONETGRAPH
    598   if (dev->cs >= 0) {
    599     *auxfd = dev->cs;
    600     (*nauxfd)++;
    601   }
    602 #endif
    603 
    604   if (dev->Timer.state != TIMER_STOPPED) {
    605     timer_Stop(&dev->Timer);
    606     dev->Timer.state = TIMER_RUNNING;
    607   }
    608 }
    609 
    610 static struct device basettydevice = {
    611   TTY_DEVICE,
    612   "tty",
    613   0,
    614   { CD_VARIABLE, DEF_TTYCDDELAY },
    615   tty_AwaitCarrier,
    616   NULL,
    617   tty_Raw,
    618   tty_Offline,
    619   tty_Cooked,
    620   tty_SetAsyncParams,
    621   tty_StopTimer,
    622   tty_Free,
    623   tty_Read,
    624   tty_Write,
    625   tty_device2iov,
    626   tty_Speed,
    627   tty_OpenInfo,
    628   tty_Slot
    629 };
    630 
    631 struct device *
    632 tty_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
    633                int maxiov __unused,
    634 #ifndef NONETGRAPH
    635                int *auxfd, int *nauxfd
    636 #else
    637                int *auxfd __unused, int *nauxfd __unused
    638 #endif
    639                )
    640 {
    641   if (type == TTY_DEVICE) {
    642     struct ttydevice *dev = (struct ttydevice *)iov[(*niov)++].iov_base;
    643 
    644     dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
    645     if (dev == NULL) {
    646       log_Printf(LogALERT, "Failed to allocate memory: %d\n",
    647                  (int)(sizeof *dev));
    648       AbortProgram(EX_OSERR);
    649     }
    650 
    651 #ifndef NONETGRAPH
    652     if (*nauxfd) {
    653       dev->cs = *auxfd;
    654       (*nauxfd)--;
    655     } else
    656       dev->cs = -1;
    657 #endif
    658 
    659     /* Refresh function pointers etc */
    660     memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
    661 
    662     physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
    663     if (dev->Timer.state != TIMER_STOPPED) {
    664       dev->Timer.state = TIMER_STOPPED;
    665       p->handler = &dev->dev;		/* For the benefit of StartTimer */
    666       tty_StartTimer(p);
    667     }
    668     return &dev->dev;
    669   }
    670 
    671   return NULL;
    672 }
    673 
    674 struct device *
    675 tty_Create(struct physical *p)
    676 {
    677   struct ttydevice *dev;
    678   struct termios ios;
    679   int oldflag;
    680 
    681   if (p->fd < 0 || !isatty(p->fd))
    682     /* Don't want this */
    683     return NULL;
    684 
    685   if (*p->name.full == '\0') {
    686     physical_SetDevice(p, ttyname(p->fd));
    687     log_Printf(LogDEBUG, "%s: Input is a tty (%s)\n",
    688                p->link.name, p->name.full);
    689   } else
    690     log_Printf(LogDEBUG, "%s: Opened %s\n", p->link.name, p->name.full);
    691 
    692   /* We're gonna return a ttydevice (unless something goes horribly wrong) */
    693 
    694   if ((dev = malloc(sizeof *dev)) == NULL) {
    695     /* Complete failure - parent doesn't continue trying to ``create'' */
    696     close(p->fd);
    697     p->fd = -1;
    698     return NULL;
    699   }
    700 
    701   memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
    702   memset(&dev->Timer, '\0', sizeof dev->Timer);
    703   dev->mbits = -1;
    704 #ifndef NONETGRAPH
    705   dev->real.speed = 0;
    706   dev->real.fd = -1;
    707   dev->real.disc = -1;
    708   *dev->hook = '\0';
    709 #endif
    710   tcgetattr(p->fd, &ios);
    711   dev->ios = ios;
    712 
    713   if (p->cfg.cd.necessity != CD_DEFAULT)
    714     /* Any override is ok for the tty device */
    715     dev->dev.cd = p->cfg.cd;
    716 
    717   log_Printf(LogDEBUG, "%s: tty_Create: physical (get): fd = %d,"
    718              " iflag = %lx, oflag = %lx, cflag = %lx\n", p->link.name, p->fd,
    719              (u_long)ios.c_iflag, (u_long)ios.c_oflag, (u_long)ios.c_cflag);
    720 
    721   cfmakeraw(&ios);
    722   if (p->cfg.rts_cts)
    723     ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
    724   else {
    725     ios.c_cflag |= CLOCAL;
    726     ios.c_iflag |= IXOFF;
    727   }
    728   ios.c_iflag |= IXON;
    729   if (p->type != PHYS_DEDICATED)
    730     ios.c_cflag |= HUPCL;
    731 
    732   if (p->type != PHYS_DIRECT) {
    733       /* Change tty speed when we're not in -direct mode */
    734       ios.c_cflag &= ~(CSIZE | PARODD | PARENB);
    735       ios.c_cflag |= p->cfg.parity;
    736       if (cfsetspeed(&ios, UnsignedToSpeed(p->cfg.speed)) == -1)
    737 	log_Printf(LogWARN, "%s: %s: Unable to set speed to %d\n",
    738 		  p->link.name, p->name.full, p->cfg.speed);
    739   }
    740 
    741   if (tcsetattr(p->fd, TCSADRAIN, &ios) == -1) {
    742     log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
    743                p->link.name);
    744     if (p->type != PHYS_DIRECT && p->cfg.speed > 115200)
    745       log_Printf(LogWARN, "%.*s             Perhaps the speed is unsupported\n",
    746                  (int)strlen(p->link.name), "");
    747   }
    748 
    749   log_Printf(LogDEBUG, "%s: physical (put): iflag = %lx, oflag = %lx, "
    750             "cflag = %lx\n", p->link.name, (u_long)ios.c_iflag,
    751             (u_long)ios.c_oflag, (u_long)ios.c_cflag);
    752 
    753   oldflag = fcntl(p->fd, F_GETFL, 0);
    754   if (oldflag < 0) {
    755     /* Complete failure - parent doesn't continue trying to ``create'' */
    756 
    757     log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
    758                p->link.name, strerror(errno));
    759     tty_Cooked(p);
    760     close(p->fd);
    761     p->fd = -1;
    762     free(dev);
    763     return NULL;
    764   } else
    765     fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
    766 
    767   physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
    768 
    769   return &dev->dev;
    770 }
    771