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