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