physical.c revision 47878
1189251Ssam/*
2189251Ssam * Written by Eivind Eklund <eivind@yes.no>
3189251Ssam *    for Yes Interactive
4189251Ssam *
5189251Ssam * Copyright (C) 1998, Yes Interactive.  All rights reserved.
6189251Ssam *
7189251Ssam * Redistribution and use in any form is permitted.  Redistribution in
8189251Ssam * source form should include the above copyright and this set of
9189251Ssam * conditions, because large sections american law seems to have been
10189251Ssam * created by a bunch of jerks on drugs that are now illegal, forcing
11189251Ssam * me to include this copyright-stuff instead of placing this in the
12189251Ssam * public domain.  The name of of 'Yes Interactive' or 'Eivind Eklund'
13189251Ssam * may not be used to endorse or promote products derived from this
14189251Ssam * software without specific prior written permission.
15189251Ssam * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16189251Ssam * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17189251Ssam * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18189251Ssam *
19189251Ssam *  $Id: physical.c,v 1.17 1999/06/05 21:35:51 brian Exp $
20189251Ssam *
21189251Ssam */
22189251Ssam
23189251Ssam#include <sys/param.h>
24189251Ssam#include <sys/socket.h>
25189251Ssam#include <netinet/in.h>
26189251Ssam#include <arpa/inet.h>
27189251Ssam#include <netdb.h>
28189251Ssam#include <netinet/in_systm.h>
29189251Ssam#include <netinet/ip.h>
30189251Ssam#include <sys/un.h>
31189251Ssam
32189251Ssam#include <errno.h>
33189251Ssam#include <fcntl.h>
34189251Ssam#include <paths.h>
35189251Ssam#include <stdio.h>
36189251Ssam#include <stdlib.h>
37189251Ssam#include <string.h>
38189251Ssam#include <sys/tty.h>	/* TIOCOUTQ */
39189251Ssam#include <sys/uio.h>
40189251Ssam#include <sys/wait.h>
41189251Ssam#include <time.h>
42189251Ssam#include <unistd.h>
43189251Ssam#include <utmp.h>
44189251Ssam#if defined(__OpenBSD__) || defined(__NetBSD__)
45189251Ssam#include <sys/ioctl.h>
46189251Ssam#include <util.h>
47189251Ssam#else
48189251Ssam#include <libutil.h>
49189251Ssam#endif
50189251Ssam
51189251Ssam#include "layer.h"
52189251Ssam#ifndef NOALIAS
53189251Ssam#include "alias_cmd.h"
54189251Ssam#endif
55189251Ssam#include "proto.h"
56189251Ssam#include "acf.h"
57189251Ssam#include "vjcomp.h"
58189251Ssam#include "defs.h"
59189251Ssam#include "command.h"
60189251Ssam#include "mbuf.h"
61189251Ssam#include "log.h"
62189251Ssam#include "id.h"
63189251Ssam#include "timer.h"
64189251Ssam#include "fsm.h"
65189251Ssam#include "lqr.h"
66189251Ssam#include "hdlc.h"
67189251Ssam#include "lcp.h"
68189251Ssam#include "throughput.h"
69189251Ssam#include "sync.h"
70189251Ssam#include "async.h"
71189251Ssam#include "iplist.h"
72189251Ssam#include "slcompress.h"
73189251Ssam#include "ipcp.h"
74189251Ssam#include "filter.h"
75189251Ssam#include "descriptor.h"
76189251Ssam#include "ccp.h"
77189251Ssam#include "link.h"
78189251Ssam#include "physical.h"
79189251Ssam#include "mp.h"
80189251Ssam#ifndef NORADIUS
81189251Ssam#include "radius.h"
82189251Ssam#endif
83189251Ssam#include "bundle.h"
84189251Ssam#include "prompt.h"
85189251Ssam#include "chat.h"
86189251Ssam#include "auth.h"
87189251Ssam#include "chap.h"
88189251Ssam#include "cbcp.h"
89189251Ssam#include "datalink.h"
90189251Ssam#include "tcp.h"
91189251Ssam#include "udp.h"
92189251Ssam#include "exec.h"
93189251Ssam#include "tty.h"
94189251Ssam
95189251Ssam
96189251Ssamstatic int physical_DescriptorWrite(struct descriptor *, struct bundle *,
97189251Ssam                                    const fd_set *);
98189251Ssamstatic void physical_DescriptorRead(struct descriptor *, struct bundle *,
99189251Ssam                                    const fd_set *);
100189251Ssam
101189251Ssamstatic int
102189251Ssamphysical_DeviceSize(void)
103189251Ssam{
104189251Ssam  return sizeof(struct device);
105189251Ssam}
106189251Ssam
107189251Ssamstruct {
108189251Ssam  struct device *(*create)(struct physical *);
109189251Ssam  struct device *(*iov2device)(int, struct physical *, struct iovec *iov,
110189251Ssam                               int *niov, int maxiov);
111189251Ssam  int (*DeviceSize)(void);
112189251Ssam} devices[] = {
113189251Ssam  { tty_Create, tty_iov2device, tty_DeviceSize },
114189251Ssam  { tcp_Create, tcp_iov2device, tcp_DeviceSize },
115189251Ssam  { udp_Create, udp_iov2device, udp_DeviceSize },
116189251Ssam  { exec_Create, exec_iov2device, exec_DeviceSize }
117189251Ssam};
118189251Ssam
119189251Ssam#define NDEVICES (sizeof devices / sizeof devices[0])
120189251Ssam
121189251Ssamstatic int
122189251Ssamphysical_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
123189251Ssam                   int *n)
124189251Ssam{
125189251Ssam  return physical_doUpdateSet(d, r, w, e, n, 0);
126189251Ssam}
127189251Ssam
128189251Ssamstruct physical *
129189251Ssamphysical_Create(struct datalink *dl, int type)
130189251Ssam{
131189251Ssam  struct physical *p;
132189251Ssam
133189251Ssam  p = (struct physical *)malloc(sizeof(struct physical));
134189251Ssam  if (!p)
135189251Ssam    return NULL;
136189251Ssam
137189251Ssam  p->link.type = PHYSICAL_LINK;
138189251Ssam  p->link.name = dl->name;
139189251Ssam  p->link.len = sizeof *p;
140189251Ssam  throughput_init(&p->link.throughput);
141189251Ssam
142189251Ssam  memset(p->link.Queue, '\0', sizeof p->link.Queue);
143189251Ssam  memset(p->link.proto_in, '\0', sizeof p->link.proto_in);
144189251Ssam  memset(p->link.proto_out, '\0', sizeof p->link.proto_out);
145189251Ssam  link_EmptyStack(&p->link);
146189251Ssam
147189251Ssam  p->handler = NULL;
148189251Ssam  p->desc.type = PHYSICAL_DESCRIPTOR;
149189251Ssam  p->desc.UpdateSet = physical_UpdateSet;
150189251Ssam  p->desc.IsSet = physical_IsSet;
151189251Ssam  p->desc.Read = physical_DescriptorRead;
152189251Ssam  p->desc.Write = physical_DescriptorWrite;
153189251Ssam  p->type = type;
154189251Ssam
155189251Ssam  hdlc_Init(&p->hdlc, &p->link.lcp);
156189251Ssam  async_Init(&p->async);
157189251Ssam
158189251Ssam  p->fd = -1;
159189251Ssam  p->out = NULL;
160189251Ssam  p->connect_count = 0;
161189251Ssam  p->dl = dl;
162189251Ssam  p->input.sz = 0;
163189251Ssam  *p->name.full = '\0';
164189251Ssam  p->name.base = p->name.full;
165189251Ssam
166189251Ssam  p->Utmp = 0;
167189251Ssam  p->session_owner = (pid_t)-1;
168189251Ssam
169189251Ssam  p->cfg.rts_cts = MODEM_CTSRTS;
170189251Ssam  p->cfg.speed = MODEM_SPEED;
171189251Ssam  p->cfg.parity = CS8;
172189251Ssam  memcpy(p->cfg.devlist, MODEM_LIST, sizeof MODEM_LIST);
173189251Ssam  p->cfg.ndev = NMODEMS;
174189251Ssam  p->cfg.cd.required = 0;
175189251Ssam  p->cfg.cd.delay = DEF_CDDELAY;
176189251Ssam
177189251Ssam  lcp_Init(&p->link.lcp, dl->bundle, &p->link, &dl->fsmp);
178189251Ssam  ccp_Init(&p->link.ccp, dl->bundle, &p->link, &dl->fsmp);
179189251Ssam
180189251Ssam  return p;
181189251Ssam}
182189251Ssam
183189251Ssamstatic const struct parity {
184189251Ssam  const char *name;
185189251Ssam  const char *name1;
186189251Ssam  int set;
187189251Ssam} validparity[] = {
188189251Ssam  { "even", "P_EVEN", CS7 | PARENB },
189189251Ssam  { "odd", "P_ODD", CS7 | PARENB | PARODD },
190189251Ssam  { "none", "P_ZERO", CS8 },
191189251Ssam  { NULL, 0 },
192189251Ssam};
193189251Ssam
194189251Ssamstatic int
195189251SsamGetParityValue(const char *str)
196189251Ssam{
197189251Ssam  const struct parity *pp;
198189251Ssam
199189251Ssam  for (pp = validparity; pp->name; pp++) {
200189251Ssam    if (strcasecmp(pp->name, str) == 0 ||
201189251Ssam	strcasecmp(pp->name1, str) == 0) {
202189251Ssam      return pp->set;
203189251Ssam    }
204189251Ssam  }
205189251Ssam  return (-1);
206189251Ssam}
207189251Ssam
208189251Ssamint
209189251Ssamphysical_SetParity(struct physical *p, const char *str)
210189251Ssam{
211189251Ssam  struct termios rstio;
212189251Ssam  int val;
213189251Ssam
214189251Ssam  val = GetParityValue(str);
215189251Ssam  if (val > 0) {
216189251Ssam    p->cfg.parity = val;
217189251Ssam    if (p->fd >= 0) {
218189251Ssam      tcgetattr(p->fd, &rstio);
219189251Ssam      rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
220189251Ssam      rstio.c_cflag |= val;
221189251Ssam      tcsetattr(p->fd, TCSADRAIN, &rstio);
222189251Ssam    }
223189251Ssam    return 0;
224189251Ssam  }
225189251Ssam  log_Printf(LogWARN, "%s: %s: Invalid parity\n", p->link.name, str);
226189251Ssam  return -1;
227189251Ssam}
228189251Ssam
229189251Ssamint
230189251Ssamphysical_GetSpeed(struct physical *p)
231189251Ssam{
232189251Ssam  if (p->handler && p->handler->speed)
233189251Ssam    return (*p->handler->speed)(p);
234189251Ssam
235189251Ssam  return 115200;
236189251Ssam}
237189251Ssam
238189251Ssamint
239189251Ssamphysical_SetSpeed(struct physical *p, int speed)
240189251Ssam{
241189251Ssam  if (IntToSpeed(speed) != B0) {
242189251Ssam      p->cfg.speed = speed;
243189251Ssam      return 1;
244189251Ssam  }
245189251Ssam
246189251Ssam  return 0;
247189251Ssam}
248189251Ssam
249189251Ssamint
250189251Ssamphysical_Raw(struct physical *p)
251189251Ssam{
252189251Ssam  if (p->handler && p->handler->raw)
253189251Ssam    return (*p->handler->raw)(p);
254189251Ssam
255189251Ssam  return 1;
256189251Ssam}
257189251Ssam
258189251Ssamvoid
259189251Ssamphysical_Offline(struct physical *p)
260189251Ssam{
261189251Ssam  if (p->handler && p->handler->offline)
262189251Ssam    (*p->handler->offline)(p);
263189251Ssam  log_Printf(LogPHASE, "%s: Disconnected!\n", p->link.name);
264189251Ssam}
265189251Ssam
266189251Ssamstatic int
267189251Ssamphysical_Lock(struct physical *p)
268189251Ssam{
269189251Ssam  int res;
270189251Ssam
271189251Ssam  if (*p->name.full == '/' && p->type != PHYS_DIRECT &&
272189251Ssam      (res = ID0uu_lock(p->name.base)) != UU_LOCK_OK) {
273189251Ssam    if (res == UU_LOCK_INUSE)
274189251Ssam      log_Printf(LogPHASE, "%s: %s is in use\n", p->link.name, p->name.full);
275189251Ssam    else
276189251Ssam      log_Printf(LogPHASE, "%s: %s is in use: uu_lock: %s\n",
277189251Ssam                 p->link.name, p->name.full, uu_lockerr(res));
278189251Ssam    return 0;
279189251Ssam  }
280189251Ssam
281189251Ssam  return 1;
282189251Ssam}
283189251Ssam
284189251Ssamstatic void
285189251Ssamphysical_Unlock(struct physical *p)
286189251Ssam{
287189251Ssam  char fn[MAXPATHLEN];
288189251Ssam  if (*p->name.full == '/' && p->type != PHYS_DIRECT &&
289189251Ssam      ID0uu_unlock(p->name.base) == -1)
290189251Ssam    log_Printf(LogALERT, "%s: Can't uu_unlock %s\n", p->link.name, fn);
291189251Ssam}
292189251Ssam
293189251Ssamvoid
294189251Ssamphysical_Close(struct physical *p)
295189251Ssam{
296189251Ssam  int newsid;
297189251Ssam  char fn[MAXPATHLEN];
298189251Ssam
299189251Ssam  if (p->fd < 0)
300189251Ssam    return;
301189251Ssam
302189251Ssam  log_Printf(LogDEBUG, "%s: Close\n", p->link.name);
303189251Ssam
304189251Ssam  if (p->handler && p->handler->cooked)
305189251Ssam    (*p->handler->cooked)(p);
306189251Ssam
307189251Ssam  physical_StopDeviceTimer(p);
308189251Ssam  if (p->Utmp) {
309189251Ssam    ID0logout(p->name.base);
310189251Ssam    p->Utmp = 0;
311189251Ssam  }
312189251Ssam  newsid = tcgetpgrp(p->fd) == getpgrp();
313189251Ssam  close(p->fd);
314189251Ssam  p->fd = -1;
315189251Ssam  log_SetTtyCommandMode(p->dl);
316189251Ssam
317189251Ssam  throughput_stop(&p->link.throughput);
318189251Ssam  throughput_log(&p->link.throughput, LogPHASE, p->link.name);
319189251Ssam
320189251Ssam  if (p->session_owner != (pid_t)-1) {
321189251Ssam    ID0kill(p->session_owner, SIGHUP);
322189251Ssam    p->session_owner = (pid_t)-1;
323189251Ssam  }
324189251Ssam
325189251Ssam  if (newsid)
326189251Ssam    bundle_setsid(p->dl->bundle, 0);
327189251Ssam
328189251Ssam  if (*p->name.full == '/') {
329189251Ssam    snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, p->name.base);
330189251Ssam#ifndef RELEASE_CRUNCH
331189251Ssam    if (ID0unlink(fn) == -1)
332189251Ssam      log_Printf(LogALERT, "%s: Can't remove %s: %s\n",
333189251Ssam                 p->link.name, fn, strerror(errno));
334189251Ssam#else
335189251Ssam    ID0unlink(fn);
336189251Ssam#endif
337189251Ssam  }
338189251Ssam  physical_Unlock(p);
339189251Ssam  if (p->handler && p->handler->destroy)
340189251Ssam    (*p->handler->destroy)(p);
341189251Ssam  p->handler = NULL;
342189251Ssam  p->name.base = p->name.full;
343189251Ssam  *p->name.full = '\0';
344189251Ssam}
345189251Ssam
346189251Ssamvoid
347189251Ssamphysical_Destroy(struct physical *p)
348189251Ssam{
349189251Ssam  physical_Close(p);
350189251Ssam  free(p);
351189251Ssam}
352189251Ssam
353189251Ssamstatic int
354189251Ssamphysical_DescriptorWrite(struct descriptor *d, struct bundle *bundle,
355189251Ssam                         const fd_set *fdset)
356189251Ssam{
357189251Ssam  struct physical *p = descriptor2physical(d);
358189251Ssam  int nw, result = 0;
359189251Ssam
360189251Ssam  if (p->out == NULL)
361189251Ssam    p->out = link_Dequeue(&p->link);
362189251Ssam
363189251Ssam  if (p->out) {
364189251Ssam    nw = physical_Write(p, MBUF_CTOP(p->out), p->out->cnt);
365189251Ssam    log_Printf(LogDEBUG, "%s: DescriptorWrite: wrote %d(%d) to %d\n",
366189251Ssam               p->link.name, nw, p->out->cnt, p->fd);
367189251Ssam    if (nw > 0) {
368189251Ssam      p->out->cnt -= nw;
369189251Ssam      p->out->offset += nw;
370189251Ssam      if (p->out->cnt == 0)
371189251Ssam	p->out = mbuf_FreeSeg(p->out);
372189251Ssam      result = 1;
373189251Ssam    } else if (nw < 0) {
374189251Ssam      if (errno != EAGAIN) {
375189251Ssam	log_Printf(LogPHASE, "%s: write (%d): %s\n", p->link.name,
376189251Ssam                   p->fd, strerror(errno));
377189251Ssam        datalink_Down(p->dl, CLOSE_NORMAL);
378189251Ssam      }
379189251Ssam      result = 1;
380189251Ssam    }
381189251Ssam    /* else we shouldn't really have been called !  select() is broken ! */
382189251Ssam  }
383189251Ssam
384189251Ssam  return result;
385189251Ssam}
386189251Ssam
387189251Ssamint
388189251Ssamphysical_ShowStatus(struct cmdargs const *arg)
389189251Ssam{
390189251Ssam  struct physical *p = arg->cx->physical;
391189251Ssam  const char *dev;
392189251Ssam  int n;
393189251Ssam
394189251Ssam  prompt_Printf(arg->prompt, "Name: %s\n", p->link.name);
395189251Ssam  prompt_Printf(arg->prompt, " State:           ");
396189251Ssam  if (p->fd < 0)
397189251Ssam    prompt_Printf(arg->prompt, "closed\n");
398189251Ssam  else if (p->handler && p->handler->openinfo)
399189251Ssam    prompt_Printf(arg->prompt, "open (%s)\n", (*p->handler->openinfo)(p));
400189251Ssam  else
401189251Ssam    prompt_Printf(arg->prompt, "open\n");
402189251Ssam
403189251Ssam  prompt_Printf(arg->prompt, " Device:          %s",
404189251Ssam                *p->name.full ?  p->name.full :
405189251Ssam                p->type == PHYS_DIRECT ? "unknown" : "N/A");
406189251Ssam  if (p->session_owner != (pid_t)-1)
407189251Ssam    prompt_Printf(arg->prompt, " (session owner: %d)", (int)p->session_owner);
408189251Ssam
409189251Ssam  prompt_Printf(arg->prompt, "\n Link Type:       %s\n", mode2Nam(p->type));
410189251Ssam  prompt_Printf(arg->prompt, " Connect Count:   %d\n", p->connect_count);
411189251Ssam#ifdef TIOCOUTQ
412189251Ssam  if (p->fd >= 0 && ioctl(p->fd, TIOCOUTQ, &n) >= 0)
413189251Ssam      prompt_Printf(arg->prompt, " Physical outq:   %d\n", n);
414189251Ssam#endif
415189251Ssam
416189251Ssam  prompt_Printf(arg->prompt, " Queued Packets:  %d\n",
417189251Ssam                link_QueueLen(&p->link));
418189251Ssam  prompt_Printf(arg->prompt, " Phone Number:    %s\n", arg->cx->phone.chosen);
419189251Ssam
420189251Ssam  prompt_Printf(arg->prompt, "\nDefaults:\n");
421189251Ssam
422189251Ssam  prompt_Printf(arg->prompt, " Device List:     ");
423189251Ssam  dev = p->cfg.devlist;
424189251Ssam  for (n = 0; n < p->cfg.ndev; n++) {
425189251Ssam    if (n)
426189251Ssam      prompt_Printf(arg->prompt, ", ");
427189251Ssam    prompt_Printf(arg->prompt, "\"%s\"", dev);
428189251Ssam    dev += strlen(dev) + 1;
429189251Ssam  }
430189251Ssam
431189251Ssam  prompt_Printf(arg->prompt, "\n Characteristics: ");
432  if (physical_IsSync(arg->cx->physical))
433    prompt_Printf(arg->prompt, "sync");
434  else
435    prompt_Printf(arg->prompt, "%dbps", p->cfg.speed);
436
437  switch (p->cfg.parity & CSIZE) {
438  case CS7:
439    prompt_Printf(arg->prompt, ", cs7");
440    break;
441  case CS8:
442    prompt_Printf(arg->prompt, ", cs8");
443    break;
444  }
445  if (p->cfg.parity & PARENB) {
446    if (p->cfg.parity & PARODD)
447      prompt_Printf(arg->prompt, ", odd parity");
448    else
449      prompt_Printf(arg->prompt, ", even parity");
450  } else
451    prompt_Printf(arg->prompt, ", no parity");
452
453  prompt_Printf(arg->prompt, ", CTS/RTS %s\n", (p->cfg.rts_cts ? "on" : "off"));
454
455  prompt_Printf(arg->prompt, " CD check delay:  %d second%s",
456                p->cfg.cd.delay, p->cfg.cd.delay == 1 ? "" : "s");
457  if (p->cfg.cd.required)
458    prompt_Printf(arg->prompt, " (required!)\n\n");
459  else
460    prompt_Printf(arg->prompt, "\n\n");
461
462  throughput_disp(&p->link.throughput, arg->prompt);
463
464  return 0;
465}
466
467static void
468physical_DescriptorRead(struct descriptor *d, struct bundle *bundle,
469                     const fd_set *fdset)
470{
471  struct physical *p = descriptor2physical(d);
472  u_char *rbuff;
473  int n, found;
474
475  rbuff = p->input.buf + p->input.sz;
476
477  /* something to read */
478  n = physical_Read(p, rbuff, sizeof p->input.buf - p->input.sz);
479  log_Printf(LogDEBUG, "%s: DescriptorRead: read %d/%d from %d\n",
480             p->link.name, n, (int)(sizeof p->input.buf - p->input.sz), p->fd);
481  if (n <= 0) {
482    if (n < 0)
483      log_Printf(LogPHASE, "%s: read (%d): %s\n", p->link.name, p->fd,
484                 strerror(errno));
485    else
486      log_Printf(LogPHASE, "%s: read (%d): Got zero bytes\n",
487                 p->link.name, p->fd);
488    datalink_Down(p->dl, CLOSE_NORMAL);
489    return;
490  }
491
492  rbuff -= p->input.sz;
493  n += p->input.sz;
494
495  if (p->link.lcp.fsm.state <= ST_CLOSED) {
496    if (p->type != PHYS_DEDICATED) {
497      found = hdlc_Detect((u_char const **)&rbuff, n, physical_IsSync(p));
498      if (rbuff != p->input.buf)
499        log_WritePrompts(p->dl, "%.*s", (int)(rbuff - p->input.buf),
500                         p->input.buf);
501      p->input.sz = n - (rbuff - p->input.buf);
502
503      if (found) {
504        /* LCP packet is detected. Turn ourselves into packet mode */
505        log_Printf(LogPHASE, "%s: PPP packet detected, coming up\n",
506                   p->link.name);
507        log_SetTtyCommandMode(p->dl);
508        datalink_Up(p->dl, 0, 1);
509        link_PullPacket(&p->link, rbuff, p->input.sz, bundle);
510        p->input.sz = 0;
511      } else
512        bcopy(rbuff, p->input.buf, p->input.sz);
513    } else
514      /* In -dedicated mode, we just discard input until LCP is started */
515      p->input.sz = 0;
516  } else if (n > 0)
517    link_PullPacket(&p->link, rbuff, n, bundle);
518}
519
520struct physical *
521iov2physical(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
522             int fd)
523{
524  struct physical *p;
525  int len, h, type;
526
527  p = (struct physical *)iov[(*niov)++].iov_base;
528  p->link.name = dl->name;
529  throughput_init(&p->link.throughput);
530  memset(p->link.Queue, '\0', sizeof p->link.Queue);
531
532  p->desc.UpdateSet = physical_UpdateSet;
533  p->desc.IsSet = physical_IsSet;
534  p->desc.Read = physical_DescriptorRead;
535  p->desc.Write = physical_DescriptorWrite;
536  p->type = PHYS_DIRECT;
537  p->dl = dl;
538  len = strlen(_PATH_DEV);
539  p->out = NULL;
540  p->connect_count = 1;
541
542  physical_SetDevice(p, p->name.full);
543
544  p->link.lcp.fsm.bundle = dl->bundle;
545  p->link.lcp.fsm.link = &p->link;
546  memset(&p->link.lcp.fsm.FsmTimer, '\0', sizeof p->link.lcp.fsm.FsmTimer);
547  memset(&p->link.lcp.fsm.OpenTimer, '\0', sizeof p->link.lcp.fsm.OpenTimer);
548  memset(&p->link.lcp.fsm.StoppedTimer, '\0',
549         sizeof p->link.lcp.fsm.StoppedTimer);
550  p->link.lcp.fsm.parent = &dl->fsmp;
551  lcp_SetupCallbacks(&p->link.lcp);
552
553  p->link.ccp.fsm.bundle = dl->bundle;
554  p->link.ccp.fsm.link = &p->link;
555  /* Our in.state & out.state are NULL (no link-level ccp yet) */
556  memset(&p->link.ccp.fsm.FsmTimer, '\0', sizeof p->link.ccp.fsm.FsmTimer);
557  memset(&p->link.ccp.fsm.OpenTimer, '\0', sizeof p->link.ccp.fsm.OpenTimer);
558  memset(&p->link.ccp.fsm.StoppedTimer, '\0',
559         sizeof p->link.ccp.fsm.StoppedTimer);
560  p->link.ccp.fsm.parent = &dl->fsmp;
561  ccp_SetupCallbacks(&p->link.ccp);
562
563  p->hdlc.lqm.owner = &p->link.lcp;
564  p->hdlc.ReportTimer.state = TIMER_STOPPED;
565  p->hdlc.lqm.timer.state = TIMER_STOPPED;
566
567  p->fd = fd;
568
569  type = (long)p->handler;
570  p->handler = NULL;
571  for (h = 0; h < NDEVICES && p->handler == NULL; h++)
572    p->handler = (*devices[h].iov2device)(type, p, iov, niov, maxiov);
573
574  if (p->handler == NULL) {
575    log_Printf(LogPHASE, "%s: Device %s, unknown link type\n",
576               p->link.name, p->name.full);
577    free(iov[(*niov)++].iov_base);
578    physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE);
579  } else
580    log_Printf(LogPHASE, "%s: Device %s, link type is %s\n",
581               p->link.name, p->name.full, p->handler->name);
582
583  if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
584    lqr_reStart(&p->link.lcp);
585  hdlc_StartTimer(&p->hdlc);
586
587  throughput_start(&p->link.throughput, "physical throughput",
588                   Enabled(dl->bundle, OPT_THROUGHPUT));
589
590  return p;
591}
592
593int
594physical_MaxDeviceSize()
595{
596  int biggest, sz, n;
597
598  biggest = sizeof(struct device);
599  for (sz = n = 0; n < NDEVICES; n++)
600    if (devices[n].DeviceSize) {
601      sz = (*devices[n].DeviceSize)();
602      if (biggest < sz)
603        biggest = sz;
604    }
605
606  return biggest;
607}
608
609int
610physical2iov(struct physical *p, struct iovec *iov, int *niov, int maxiov,
611             pid_t newpid)
612{
613  struct device *h;
614  int sz;
615
616  h = NULL;
617  if (p) {
618    hdlc_StopTimer(&p->hdlc);
619    lqr_StopTimer(p);
620    timer_Stop(&p->link.lcp.fsm.FsmTimer);
621    timer_Stop(&p->link.ccp.fsm.FsmTimer);
622    timer_Stop(&p->link.lcp.fsm.OpenTimer);
623    timer_Stop(&p->link.ccp.fsm.OpenTimer);
624    timer_Stop(&p->link.lcp.fsm.StoppedTimer);
625    timer_Stop(&p->link.ccp.fsm.StoppedTimer);
626    if (p->handler) {
627      if (p->handler->device2iov)
628        h = p->handler;
629      p->handler = (struct device *)(long)p->handler->type;
630    }
631
632    if (Enabled(p->dl->bundle, OPT_KEEPSESSION) ||
633        tcgetpgrp(p->fd) == getpgrp())
634      p->session_owner = getpid();      /* So I'll eventually get HUP'd */
635    else
636      p->session_owner = (pid_t)-1;
637    timer_Stop(&p->link.throughput.Timer);
638    physical_ChangedPid(p, newpid);
639  }
640
641  if (*niov + 1 >= maxiov) {
642    log_Printf(LogERROR, "physical2iov: No room for physical + device !\n");
643    if (p)
644      free(p);
645    return -1;
646  }
647
648  iov[*niov].iov_base = p ? (void *)p : malloc(sizeof *p);
649  iov[*niov].iov_len = sizeof *p;
650  (*niov)++;
651
652  sz = physical_MaxDeviceSize();
653  if (p) {
654    if (h)
655      (*h->device2iov)(h, iov, niov, maxiov, newpid);
656    else {
657      iov[*niov].iov_base = malloc(sz);
658      if (p->handler)
659        memcpy(iov[*niov].iov_base, p->handler, sizeof *p->handler);
660      iov[*niov].iov_len = sz;
661      (*niov)++;
662    }
663  } else {
664    iov[*niov].iov_base = malloc(sz);
665    iov[*niov].iov_len = sz;
666    (*niov)++;
667  }
668
669  return p ? p->fd : 0;
670}
671
672void
673physical_ChangedPid(struct physical *p, pid_t newpid)
674{
675  if (p->fd >= 0 && *p->name.full == '/' && p->type != PHYS_DIRECT) {
676    int res;
677
678    if ((res = ID0uu_lock_txfr(p->name.base, newpid)) != UU_LOCK_OK)
679      log_Printf(LogPHASE, "uu_lock_txfr: %s\n", uu_lockerr(res));
680  }
681}
682
683int
684physical_IsSync(struct physical *p)
685{
686   return p->cfg.speed == 0;
687}
688
689const char *physical_GetDevice(struct physical *p)
690{
691   return p->name.full;
692}
693
694void
695physical_SetDeviceList(struct physical *p, int argc, const char *const *argv)
696{
697  int f, pos;
698
699  p->cfg.devlist[sizeof p->cfg.devlist - 1] = '\0';
700  for (f = 0, pos = 0; f < argc && pos < sizeof p->cfg.devlist - 1; f++) {
701    if (pos)
702      p->cfg.devlist[pos++] = '\0';
703    strncpy(p->cfg.devlist + pos, argv[f], sizeof p->cfg.devlist - pos - 1);
704    pos += strlen(p->cfg.devlist + pos);
705  }
706  p->cfg.ndev = f;
707}
708
709void
710physical_SetSync(struct physical *p)
711{
712   p->cfg.speed = 0;
713}
714
715int
716physical_SetRtsCts(struct physical *p, int enable)
717{
718   p->cfg.rts_cts = enable ? 1 : 0;
719   return 1;
720}
721
722ssize_t
723physical_Read(struct physical *p, void *buf, size_t nbytes)
724{
725  ssize_t ret;
726
727  if (p->handler && p->handler->read)
728    ret = (*p->handler->read)(p, buf, nbytes);
729  else
730    ret = read(p->fd, buf, nbytes);
731
732  log_DumpBuff(LogPHYSICAL, "read", buf, ret);
733
734  return ret;
735}
736
737ssize_t
738physical_Write(struct physical *p, const void *buf, size_t nbytes)
739{
740  log_DumpBuff(LogPHYSICAL, "write", buf, nbytes);
741
742  if (p->handler && p->handler->write)
743    return (*p->handler->write)(p, buf, nbytes);
744
745  return write(p->fd, buf, nbytes);
746}
747
748int
749physical_doUpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
750                     int *n, int force)
751{
752  struct physical *p = descriptor2physical(d);
753  int sets;
754
755  sets = 0;
756  if (p->fd >= 0) {
757    if (r) {
758      FD_SET(p->fd, r);
759      log_Printf(LogTIMER, "%s: fdset(r) %d\n", p->link.name, p->fd);
760      sets++;
761    }
762    if (e) {
763      FD_SET(p->fd, e);
764      log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, p->fd);
765      sets++;
766    }
767    if (w && (force || link_QueueLen(&p->link) || p->out)) {
768      FD_SET(p->fd, w);
769      log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, p->fd);
770      sets++;
771    }
772    if (sets && *n < p->fd + 1)
773      *n = p->fd + 1;
774  }
775
776  return sets;
777}
778
779int
780physical_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
781{
782  int sets;
783
784  sets = 0;
785  if (p->fd >= 0) {
786    if (r && FD_ISSET(p->fd, r)) {
787      FD_CLR(p->fd, r);
788      log_Printf(LogTIMER, "%s: fdunset(r) %d\n", p->link.name, p->fd);
789      sets++;
790    }
791    if (e && FD_ISSET(p->fd, e)) {
792      FD_CLR(p->fd, e);
793      log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, p->fd);
794      sets++;
795    }
796    if (w && FD_ISSET(p->fd, w)) {
797      FD_CLR(p->fd, w);
798      log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, p->fd);
799      sets++;
800    }
801  }
802
803  return sets;
804}
805
806int
807physical_IsSet(struct descriptor *d, const fd_set *fdset)
808{
809  struct physical *p = descriptor2physical(d);
810  return p->fd >= 0 && FD_ISSET(p->fd, fdset);
811}
812
813void
814physical_Login(struct physical *p, const char *name)
815{
816  if (p->type == PHYS_DIRECT && *p->name.base && !p->Utmp) {
817    struct utmp ut;
818    const char *connstr;
819
820    memset(&ut, 0, sizeof ut);
821    time(&ut.ut_time);
822    strncpy(ut.ut_name, name, sizeof ut.ut_name);
823    strncpy(ut.ut_line, p->name.base, sizeof ut.ut_line);
824    if ((connstr = getenv("CONNECT")))
825      /* mgetty sets this to the connection speed */
826      strncpy(ut.ut_host, connstr, sizeof ut.ut_host);
827    ID0login(&ut);
828    p->Utmp = 1;
829  }
830}
831
832int
833physical_SetMode(struct physical *p, int mode)
834{
835  if ((p->type & (PHYS_DIRECT|PHYS_DEDICATED) ||
836       mode & (PHYS_DIRECT|PHYS_DEDICATED)) &&
837      (!(p->type & PHYS_DIRECT) || !(mode & PHYS_BACKGROUND))) {
838    log_Printf(LogWARN, "%s: Cannot change mode %s to %s\n", p->link.name,
839               mode2Nam(p->type), mode2Nam(mode));
840    return 0;
841  }
842  p->type = mode;
843  return 1;
844}
845
846void
847physical_DeleteQueue(struct physical *p)
848{
849  if (p->out) {
850    mbuf_Free(p->out);
851    p->out = NULL;
852  }
853  link_DeleteQueue(&p->link);
854}
855
856void
857physical_SetDevice(struct physical *p, const char *name)
858{
859  int len = strlen(_PATH_DEV);
860
861  if (name != p->name.full) {
862    strncpy(p->name.full, name, sizeof p->name.full - 1);
863    p->name.full[sizeof p->name.full - 1] = '\0';
864  }
865  p->name.base = *p->name.full == '!' ?  p->name.full + 1 :
866                 strncmp(p->name.full, _PATH_DEV, len) ?
867                 p->name.full : p->name.full + len;
868}
869
870static void
871physical_Found(struct physical *p)
872{
873  FILE *lockfile;
874  char fn[MAXPATHLEN];
875
876  if (*p->name.full == '/') {
877    snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, p->name.base);
878    lockfile = ID0fopen(fn, "w");
879    if (lockfile != NULL) {
880      fprintf(lockfile, "%s%d\n", TUN_NAME, p->dl->bundle->unit);
881      fclose(lockfile);
882    }
883#ifndef RELEASE_CRUNCH
884    else
885      log_Printf(LogALERT, "%s: Can't create %s: %s\n",
886                 p->link.name, fn, strerror(errno));
887#endif
888  }
889
890  throughput_start(&p->link.throughput, "physical throughput",
891                   Enabled(p->dl->bundle, OPT_THROUGHPUT));
892  p->connect_count++;
893  p->input.sz = 0;
894
895  log_Printf(LogPHASE, "%s: Connected!\n", p->link.name);
896}
897
898int
899physical_Open(struct physical *p, struct bundle *bundle)
900{
901  int devno, h, wasopen, err;
902  char *dev;
903
904  if (p->fd >= 0)
905    log_Printf(LogDEBUG, "%s: Open: Modem is already open!\n", p->link.name);
906    /* We're going back into "term" mode */
907  else if (p->type == PHYS_DIRECT) {
908    physical_SetDevice(p, "");
909    p->fd = STDIN_FILENO;
910    for (h = 0; h < NDEVICES && p->handler == NULL && p->fd >= 0; h++)
911        p->handler = (*devices[h].create)(p);
912    if (p->fd >= 0) {
913      if (p->handler == NULL) {
914        physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE);
915        log_Printf(LogDEBUG, "%s: stdin is unidentified\n", p->link.name);
916      }
917      physical_Found(p);
918    }
919  } else {
920    dev = p->cfg.devlist;
921    devno = 0;
922    while (devno < p->cfg.ndev && p->fd < 0) {
923      physical_SetDevice(p, dev);
924      if (physical_Lock(p)) {
925        err = 0;
926
927        if (*p->name.full == '/') {
928          p->fd = ID0open(p->name.full, O_RDWR | O_NONBLOCK);
929          if (p->fd < 0)
930            err = errno;
931        }
932
933        wasopen = p->fd >= 0;
934        for (h = 0; h < NDEVICES && p->handler == NULL; h++)
935          if ((p->handler = (*devices[h].create)(p)) == NULL &&
936              wasopen && p->fd == -1)
937            break;
938
939        if (p->fd < 0) {
940          if (h == NDEVICES) {
941            if (err)
942	      log_Printf(LogWARN, "%s: %s: %s\n", p->link.name, p->name.full,
943                         strerror(errno));
944            else
945	      log_Printf(LogWARN, "%s: Device (%s) must begin with a '/',"
946                         " a '!' or be a host:port pair\n", p->link.name,
947                         p->name.full);
948          }
949          physical_Unlock(p);
950        } else
951          physical_Found(p);
952      }
953      dev += strlen(dev) + 1;
954      devno++;
955    }
956  }
957
958  return p->fd;
959}
960
961void
962physical_SetupStack(struct physical *p, const char *who, int how)
963{
964  link_EmptyStack(&p->link);
965  if (how == PHYSICAL_FORCE_SYNC ||
966      (how == PHYSICAL_NOFORCE && physical_IsSync(p)))
967    link_Stack(&p->link, &synclayer);
968  else {
969    link_Stack(&p->link, &asynclayer);
970    link_Stack(&p->link, &hdlclayer);
971  }
972  link_Stack(&p->link, &acflayer);
973  link_Stack(&p->link, &protolayer);
974  link_Stack(&p->link, &lqrlayer);
975  link_Stack(&p->link, &ccplayer);
976  link_Stack(&p->link, &vjlayer);
977#ifndef NOALIAS
978  link_Stack(&p->link, &aliaslayer);
979#endif
980  if (how == PHYSICAL_FORCE_ASYNC && physical_IsSync(p)) {
981    log_Printf(LogWARN, "Sync device setting ignored for ``%s'' device\n", who);
982    p->cfg.speed = MODEM_SPEED;
983  } else if (how == PHYSICAL_FORCE_SYNC && !physical_IsSync(p)) {
984    log_Printf(LogWARN, "Async device setting ignored for ``%s'' device\n",
985               who);
986    physical_SetSync(p);
987  }
988}
989
990void
991physical_StopDeviceTimer(struct physical *p)
992{
993  if (p->handler && p->handler->stoptimer)
994    (*p->handler->stoptimer)(p);
995}
996