1/*-
2 * Copyright (c) 1999 Brian Somers <brian@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
87struct 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
106unsigned
107tty_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 */
116static void
117tty_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
180static void
181tty_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
195static int
196tty_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
220static int
221isngtty(struct ttydevice *dev)
222{
223  return dev->real.fd != -1;
224}
225
226static void
227tty_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
252static int
253LoadLineDiscipline(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
373failed:
374  ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
375  close(ds);
376  close(cs);
377
378  return 0;
379}
380
381static void
382UnloadLineDiscipline(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
406static ssize_t
407tty_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
417static ssize_t
418tty_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
431static int
432tty_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
473static void
474tty_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
492static void
493tty_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
514static void
515tty_StopTimer(struct physical *p)
516{
517  struct ttydevice *dev = device2tty(p->handler);
518
519  timer_Stop(&dev->Timer);
520}
521
522static void
523tty_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
531static unsigned
532tty_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
542static const char *
543tty_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
557static int
558tty_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
574static void
575tty_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
610static 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
631struct device *
632tty_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
674struct device *
675tty_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