freebsd-nq/usr.sbin/ppp/physical.c
Brian Somers 6815097bf7 Allow `host:port/udp'' devices and support `host:port/tcp'' as
being the same as the previous (still supported) ``host:port''
syntax for tcp socket devices.

A udp device uses synchronous ppp rather than async, and avoids
the double-retransmit overhead that comes with ppp over tcp (it's
usually a bad idea to transport IP over a reliable transport that
itself is using an unreliable transport).  PPP over UDP provides
througput of ** 1.5Mb per second ** with all compression disabled,
maxing out a PPro/200 when running ppp twice, back-to-back.

This proves that PPPoE is plausable in userland....

This change adds a few more handler functions to struct device and
allows derivations of struct device (which may contain their own
data etc) to pass themselves through the unix domain socket for MP.
** At last **, struct physical has lost all the tty crud !

iov2physical() is now smart enough to restore the correct stack of
layers so that MP servers will work again.

The version number has bumped as our MP link transfer contents have
changed (they now may contain a `struct device').

Don't extract the protocol twice in MP mode (resulting in protocol
rejects for every MP packet).  This was broken with my original
layering changes.

Add ``Physical'' and ``Sync'' log levels for logging the relevent
raw packets and add protocol-tracking LogDEBUG stuff in various
LayerPush & LayerPull functions.

Assign our physical device name for incoming tcp connections by
calling getpeername().

Assign our physical device name for incoming udp connections from
the address retrieved by the first recvfrom().
1999-05-12 09:49:12 +00:00

875 lines
22 KiB
C

/*
* Written by Eivind Eklund <eivind@yes.no>
* for Yes Interactive
*
* Copyright (C) 1998, Yes Interactive. All rights reserved.
*
* Redistribution and use in any form is permitted. Redistribution in
* source form should include the above copyright and this set of
* conditions, because large sections american law seems to have been
* created by a bunch of jerks on drugs that are now illegal, forcing
* me to include this copyright-stuff instead of placing this in the
* public domain. The name of of 'Yes Interactive' or 'Eivind Eklund'
* may not be used to endorse or promote products derived from this
* software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id: physical.c,v 1.10 1999/05/09 20:13:51 brian Exp $
*
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/tty.h> /* TIOCOUTQ */
#include <sys/uio.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <utmp.h>
#if defined(__OpenBSD__) || defined(__NetBSD__)
#include <sys/ioctl.h>
#include <util.h>
#else
#include <libutil.h>
#endif
#include "layer.h"
#ifndef NOALIAS
#include "alias_cmd.h"
#endif
#include "proto.h"
#include "acf.h"
#include "vjcomp.h"
#include "defs.h"
#include "command.h"
#include "mbuf.h"
#include "log.h"
#include "id.h"
#include "timer.h"
#include "fsm.h"
#include "lqr.h"
#include "hdlc.h"
#include "lcp.h"
#include "throughput.h"
#include "sync.h"
#include "async.h"
#include "iplist.h"
#include "slcompress.h"
#include "ipcp.h"
#include "filter.h"
#include "descriptor.h"
#include "ccp.h"
#include "link.h"
#include "physical.h"
#include "mp.h"
#ifndef NORADIUS
#include "radius.h"
#endif
#include "bundle.h"
#include "prompt.h"
#include "chat.h"
#include "auth.h"
#include "chap.h"
#include "cbcp.h"
#include "datalink.h"
#include "tcp.h"
#include "udp.h"
#include "exec.h"
#include "tty.h"
static int physical_DescriptorWrite(struct descriptor *, struct bundle *,
const fd_set *);
static void physical_DescriptorRead(struct descriptor *, struct bundle *,
const fd_set *);
struct {
struct device *(*create)(struct physical *);
struct device *(*iov2device)(int, struct physical *, struct iovec *iov,
int *niov, int maxiov);
} devices[] = {
{ tty_Create, tty_iov2device },
{ tcp_Create, tcp_iov2device },
{ udp_Create, udp_iov2device },
{ exec_Create, exec_iov2device }
};
#define NDEVICES (sizeof devices / sizeof devices[0])
static int
physical_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
int *n)
{
return physical_doUpdateSet(d, r, w, e, n, 0);
}
struct physical *
physical_Create(struct datalink *dl, int type)
{
struct physical *p;
p = (struct physical *)malloc(sizeof(struct physical));
if (!p)
return NULL;
p->link.type = PHYSICAL_LINK;
p->link.name = dl->name;
p->link.len = sizeof *p;
throughput_init(&p->link.throughput);
memset(p->link.Queue, '\0', sizeof p->link.Queue);
memset(p->link.proto_in, '\0', sizeof p->link.proto_in);
memset(p->link.proto_out, '\0', sizeof p->link.proto_out);
link_EmptyStack(&p->link);
p->handler = NULL;
p->desc.type = PHYSICAL_DESCRIPTOR;
p->desc.UpdateSet = physical_UpdateSet;
p->desc.IsSet = physical_IsSet;
p->desc.Read = physical_DescriptorRead;
p->desc.Write = physical_DescriptorWrite;
p->type = type;
hdlc_Init(&p->hdlc, &p->link.lcp);
async_Init(&p->async);
p->fd = -1;
p->out = NULL;
p->connect_count = 0;
p->dl = dl;
p->input.sz = 0;
*p->name.full = '\0';
p->name.base = p->name.full;
p->Utmp = 0;
p->session_owner = (pid_t)-1;
p->cfg.rts_cts = MODEM_CTSRTS;
p->cfg.speed = MODEM_SPEED;
p->cfg.parity = CS8;
memcpy(p->cfg.devlist, MODEM_LIST, sizeof MODEM_LIST);
p->cfg.ndev = NMODEMS;
p->cfg.cd.required = 0;
p->cfg.cd.delay = DEF_CDDELAY;
lcp_Init(&p->link.lcp, dl->bundle, &p->link, &dl->fsmp);
ccp_Init(&p->link.ccp, dl->bundle, &p->link, &dl->fsmp);
return p;
}
static const struct parity {
const char *name;
const char *name1;
int set;
} validparity[] = {
{ "even", "P_EVEN", CS7 | PARENB },
{ "odd", "P_ODD", CS7 | PARENB | PARODD },
{ "none", "P_ZERO", CS8 },
{ NULL, 0 },
};
static int
GetParityValue(const char *str)
{
const struct parity *pp;
for (pp = validparity; pp->name; pp++) {
if (strcasecmp(pp->name, str) == 0 ||
strcasecmp(pp->name1, str) == 0) {
return pp->set;
}
}
return (-1);
}
int
physical_SetParity(struct physical *p, const char *str)
{
struct termios rstio;
int val;
val = GetParityValue(str);
if (val > 0) {
p->cfg.parity = val;
if (p->fd >= 0) {
tcgetattr(p->fd, &rstio);
rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
rstio.c_cflag |= val;
tcsetattr(p->fd, TCSADRAIN, &rstio);
}
return 0;
}
log_Printf(LogWARN, "%s: %s: Invalid parity\n", p->link.name, str);
return -1;
}
int
physical_GetSpeed(struct physical *p)
{
if (p->handler && p->handler->speed)
return (*p->handler->speed)(p);
return 115200;
}
int
physical_SetSpeed(struct physical *p, int speed)
{
if (IntToSpeed(speed) != B0) {
p->cfg.speed = speed;
return 1;
}
return 0;
}
int
physical_Raw(struct physical *p)
{
if (p->handler && p->handler->raw)
return (*p->handler->raw)(p);
return 1;
}
void
physical_Offline(struct physical *p)
{
if (p->handler && p->handler->offline)
(*p->handler->offline)(p);
log_Printf(LogPHASE, "%s: Disconnected!\n", p->link.name);
}
static void
physical_ReallyClose(struct physical *p)
{
int newsid;
log_Printf(LogDEBUG, "%s: Really close %d\n", p->link.name, p->fd);
if (p->fd >= 0) {
physical_StopDeviceTimer(p);
if (p->Utmp) {
ID0logout(p->name.base);
p->Utmp = 0;
}
newsid = tcgetpgrp(p->fd) == getpgrp();
close(p->fd);
p->fd = -1;
log_SetTtyCommandMode(p->dl);
throughput_stop(&p->link.throughput);
throughput_log(&p->link.throughput, LogPHASE, p->link.name);
if (p->session_owner != (pid_t)-1) {
ID0kill(p->session_owner, SIGHUP);
p->session_owner = (pid_t)-1;
}
if (newsid)
bundle_setsid(p->dl->bundle, 0);
if (p->handler && p->handler->destroy)
(*p->handler->destroy)(p);
p->handler = NULL;
}
*p->name.full = '\0';
p->name.base = p->name.full;
}
void
physical_Close(struct physical *p)
{
if (p->fd < 0)
return;
log_Printf(LogDEBUG, "%s: Close\n", p->link.name);
if (p->handler && p->handler->cooked)
(*p->handler->cooked)(p);
physical_ReallyClose(p);
}
void
physical_Destroy(struct physical *p)
{
physical_Close(p);
free(p);
}
static int
physical_DescriptorWrite(struct descriptor *d, struct bundle *bundle,
const fd_set *fdset)
{
struct physical *p = descriptor2physical(d);
int nw, result = 0;
if (p->out == NULL)
p->out = link_Dequeue(&p->link);
if (p->out) {
nw = physical_Write(p, MBUF_CTOP(p->out), p->out->cnt);
log_Printf(LogDEBUG, "%s: DescriptorWrite: wrote %d(%d) to %d\n",
p->link.name, nw, p->out->cnt, p->fd);
if (nw > 0) {
p->out->cnt -= nw;
p->out->offset += nw;
if (p->out->cnt == 0)
p->out = mbuf_FreeSeg(p->out);
result = 1;
} else if (nw < 0) {
if (errno != EAGAIN) {
log_Printf(LogPHASE, "%s: write (%d): %s\n", p->link.name,
p->fd, strerror(errno));
datalink_Down(p->dl, CLOSE_NORMAL);
}
result = 1;
}
/* else we shouldn't really have been called ! select() is broken ! */
}
return result;
}
int
physical_ShowStatus(struct cmdargs const *arg)
{
struct physical *p = arg->cx->physical;
const char *dev;
int n;
prompt_Printf(arg->prompt, "Name: %s\n", p->link.name);
prompt_Printf(arg->prompt, " State: ");
if (p->fd < 0)
prompt_Printf(arg->prompt, "closed\n");
else if (p->handler && p->handler->openinfo)
prompt_Printf(arg->prompt, "open (%s)\n", (*p->handler->openinfo)(p));
else
prompt_Printf(arg->prompt, "open\n");
prompt_Printf(arg->prompt, " Device: %s",
*p->name.full ? p->name.full :
p->type == PHYS_DIRECT ? "unknown" : "N/A");
if (p->session_owner != (pid_t)-1)
prompt_Printf(arg->prompt, " (session owner: %d)", (int)p->session_owner);
prompt_Printf(arg->prompt, "\n Link Type: %s\n", mode2Nam(p->type));
prompt_Printf(arg->prompt, " Connect Count: %d\n", p->connect_count);
#ifdef TIOCOUTQ
if (p->fd >= 0 && ioctl(p->fd, TIOCOUTQ, &n) >= 0)
prompt_Printf(arg->prompt, " Physical outq: %d\n", n);
#endif
prompt_Printf(arg->prompt, " Queued Packets: %d\n",
link_QueueLen(&p->link));
prompt_Printf(arg->prompt, " Phone Number: %s\n", arg->cx->phone.chosen);
prompt_Printf(arg->prompt, "\nDefaults:\n");
prompt_Printf(arg->prompt, " Device List: ");
dev = p->cfg.devlist;
for (n = 0; n < p->cfg.ndev; n++) {
if (n)
prompt_Printf(arg->prompt, ", ");
prompt_Printf(arg->prompt, "\"%s\"", dev);
dev += strlen(dev) + 1;
}
prompt_Printf(arg->prompt, "\n Characteristics: ");
if (physical_IsSync(arg->cx->physical))
prompt_Printf(arg->prompt, "sync");
else
prompt_Printf(arg->prompt, "%dbps", p->cfg.speed);
switch (p->cfg.parity & CSIZE) {
case CS7:
prompt_Printf(arg->prompt, ", cs7");
break;
case CS8:
prompt_Printf(arg->prompt, ", cs8");
break;
}
if (p->cfg.parity & PARENB) {
if (p->cfg.parity & PARODD)
prompt_Printf(arg->prompt, ", odd parity");
else
prompt_Printf(arg->prompt, ", even parity");
} else
prompt_Printf(arg->prompt, ", no parity");
prompt_Printf(arg->prompt, ", CTS/RTS %s\n", (p->cfg.rts_cts ? "on" : "off"));
prompt_Printf(arg->prompt, " CD check delay: %d second%s",
p->cfg.cd.delay, p->cfg.cd.delay == 1 ? "" : "s");
if (p->cfg.cd.required)
prompt_Printf(arg->prompt, " (required!)\n\n");
else
prompt_Printf(arg->prompt, "\n\n");
throughput_disp(&p->link.throughput, arg->prompt);
return 0;
}
static void
physical_DescriptorRead(struct descriptor *d, struct bundle *bundle,
const fd_set *fdset)
{
struct physical *p = descriptor2physical(d);
u_char *rbuff;
int n, found;
rbuff = p->input.buf + p->input.sz;
/* something to read */
n = physical_Read(p, rbuff, sizeof p->input.buf - p->input.sz);
log_Printf(LogDEBUG, "%s: DescriptorRead: read %d/%d from %d\n",
p->link.name, n, (int)(sizeof p->input.buf - p->input.sz), p->fd);
if (n <= 0) {
if (n < 0)
log_Printf(LogPHASE, "%s: read (%d): %s\n", p->link.name, p->fd,
strerror(errno));
else
log_Printf(LogPHASE, "%s: read (%d): Got zero bytes\n",
p->link.name, p->fd);
datalink_Down(p->dl, CLOSE_NORMAL);
return;
}
rbuff -= p->input.sz;
n += p->input.sz;
if (p->link.lcp.fsm.state <= ST_CLOSED) {
if (p->type != PHYS_DEDICATED) {
found = hdlc_Detect((u_char const **)&rbuff, n, physical_IsSync(p));
if (rbuff != p->input.buf)
log_WritePrompts(p->dl, "%.*s", (int)(rbuff - p->input.buf),
p->input.buf);
p->input.sz = n - (rbuff - p->input.buf);
if (found) {
/* LCP packet is detected. Turn ourselves into packet mode */
log_Printf(LogPHASE, "%s: PPP packet detected, coming up\n",
p->link.name);
log_SetTtyCommandMode(p->dl);
datalink_Up(p->dl, 0, 1);
link_PullPacket(&p->link, rbuff, p->input.sz, bundle);
p->input.sz = 0;
} else
bcopy(rbuff, p->input.buf, p->input.sz);
} else
/* In -dedicated mode, we just discard input until LCP is started */
p->input.sz = 0;
} else if (n > 0)
link_PullPacket(&p->link, rbuff, n, bundle);
}
struct physical *
iov2physical(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
int fd)
{
struct physical *p;
int len, h, type;
p = (struct physical *)iov[(*niov)++].iov_base;
p->link.name = dl->name;
throughput_init(&p->link.throughput);
memset(p->link.Queue, '\0', sizeof p->link.Queue);
p->desc.UpdateSet = physical_UpdateSet;
p->desc.IsSet = physical_IsSet;
p->desc.Read = physical_DescriptorRead;
p->desc.Write = physical_DescriptorWrite;
p->type = PHYS_DIRECT;
p->dl = dl;
len = strlen(_PATH_DEV);
p->name.base = strncmp(p->name.full, _PATH_DEV, len) ?
p->name.full : p->name.full + len;
p->out = NULL;
p->connect_count = 1;
p->link.lcp.fsm.bundle = dl->bundle;
p->link.lcp.fsm.link = &p->link;
memset(&p->link.lcp.fsm.FsmTimer, '\0', sizeof p->link.lcp.fsm.FsmTimer);
memset(&p->link.lcp.fsm.OpenTimer, '\0', sizeof p->link.lcp.fsm.OpenTimer);
memset(&p->link.lcp.fsm.StoppedTimer, '\0',
sizeof p->link.lcp.fsm.StoppedTimer);
p->link.lcp.fsm.parent = &dl->fsmp;
lcp_SetupCallbacks(&p->link.lcp);
p->link.ccp.fsm.bundle = dl->bundle;
p->link.ccp.fsm.link = &p->link;
/* Our in.state & out.state are NULL (no link-level ccp yet) */
memset(&p->link.ccp.fsm.FsmTimer, '\0', sizeof p->link.ccp.fsm.FsmTimer);
memset(&p->link.ccp.fsm.OpenTimer, '\0', sizeof p->link.ccp.fsm.OpenTimer);
memset(&p->link.ccp.fsm.StoppedTimer, '\0',
sizeof p->link.ccp.fsm.StoppedTimer);
p->link.ccp.fsm.parent = &dl->fsmp;
ccp_SetupCallbacks(&p->link.ccp);
p->hdlc.lqm.owner = &p->link.lcp;
p->hdlc.ReportTimer.state = TIMER_STOPPED;
p->hdlc.lqm.timer.state = TIMER_STOPPED;
p->fd = fd;
if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
lqr_reStart(&p->link.lcp);
hdlc_StartTimer(&p->hdlc);
throughput_start(&p->link.throughput, "physical throughput",
Enabled(dl->bundle, OPT_THROUGHPUT));
type = (long)p->handler;
for (h = 0; h < NDEVICES && p->handler == NULL; h++)
p->handler = (*devices[h].iov2device)(type, p, iov, niov, maxiov);
if (p->handler == NULL)
physical_SetupStack(p, PHYSICAL_NOFORCE);
return p;
}
int
physical2iov(struct physical *p, struct iovec *iov, int *niov, int maxiov,
pid_t newpid)
{
if (p) {
hdlc_StopTimer(&p->hdlc);
lqr_StopTimer(p);
timer_Stop(&p->link.lcp.fsm.FsmTimer);
timer_Stop(&p->link.ccp.fsm.FsmTimer);
timer_Stop(&p->link.lcp.fsm.OpenTimer);
timer_Stop(&p->link.ccp.fsm.OpenTimer);
timer_Stop(&p->link.lcp.fsm.StoppedTimer);
timer_Stop(&p->link.ccp.fsm.StoppedTimer);
if (p->handler) {
if (p->handler->device2iov)
(*p->handler->device2iov)(p, iov, niov, maxiov, newpid);
p->handler = (struct device *)(long)p->handler->type;
}
if (tcgetpgrp(p->fd) == getpgrp())
p->session_owner = getpid(); /* So I'll eventually get HUP'd */
timer_Stop(&p->link.throughput.Timer);
physical_ChangedPid(p, newpid);
}
if (*niov >= maxiov) {
log_Printf(LogERROR, "physical2iov: No room for physical !\n");
if (p)
free(p);
return -1;
}
iov[*niov].iov_base = p ? p : malloc(sizeof *p);
iov[*niov].iov_len = sizeof *p;
(*niov)++;
return p ? p->fd : 0;
}
void
physical_ChangedPid(struct physical *p, pid_t newpid)
{
if (p->fd >= 0 && p->type != PHYS_DIRECT) {
int res;
if ((res = ID0uu_lock_txfr(p->name.base, newpid)) != UU_LOCK_OK)
log_Printf(LogPHASE, "uu_lock_txfr: %s\n", uu_lockerr(res));
}
}
int
physical_IsSync(struct physical *p)
{
return p->cfg.speed == 0;
}
const char *physical_GetDevice(struct physical *p)
{
return p->name.full;
}
void
physical_SetDeviceList(struct physical *p, int argc, const char *const *argv)
{
int f, pos;
p->cfg.devlist[sizeof p->cfg.devlist - 1] = '\0';
for (f = 0, pos = 0; f < argc && pos < sizeof p->cfg.devlist - 1; f++) {
if (pos)
p->cfg.devlist[pos++] = '\0';
strncpy(p->cfg.devlist + pos, argv[f], sizeof p->cfg.devlist - pos - 1);
pos += strlen(p->cfg.devlist + pos);
}
p->cfg.ndev = f;
}
void
physical_SetSync(struct physical *p)
{
p->cfg.speed = 0;
}
int
physical_SetRtsCts(struct physical *p, int enable)
{
p->cfg.rts_cts = enable ? 1 : 0;
return 1;
}
ssize_t
physical_Read(struct physical *p, void *buf, size_t nbytes)
{
ssize_t ret;
if (p->handler && p->handler->read)
ret = (*p->handler->read)(p, buf, nbytes);
else
ret = read(p->fd, buf, nbytes);
log_DumpBuff(LogPHYSICAL, "read", buf, ret);
return ret;
}
ssize_t
physical_Write(struct physical *p, const void *buf, size_t nbytes)
{
log_DumpBuff(LogPHYSICAL, "write", buf, nbytes);
if (p->handler && p->handler->write)
return (*p->handler->write)(p, buf, nbytes);
return write(p->fd, buf, nbytes);
}
int
physical_doUpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
int *n, int force)
{
struct physical *p = descriptor2physical(d);
int sets;
sets = 0;
if (p->fd >= 0) {
if (r) {
FD_SET(p->fd, r);
log_Printf(LogTIMER, "%s: fdset(r) %d\n", p->link.name, p->fd);
sets++;
}
if (e) {
FD_SET(p->fd, e);
log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, p->fd);
sets++;
}
if (w && (force || link_QueueLen(&p->link) || p->out)) {
FD_SET(p->fd, w);
log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, p->fd);
sets++;
}
if (sets && *n < p->fd + 1)
*n = p->fd + 1;
}
return sets;
}
int
physical_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
{
int sets;
sets = 0;
if (p->fd >= 0) {
if (r && FD_ISSET(p->fd, r)) {
FD_CLR(p->fd, r);
log_Printf(LogTIMER, "%s: fdunset(r) %d\n", p->link.name, p->fd);
sets++;
}
if (e && FD_ISSET(p->fd, e)) {
FD_CLR(p->fd, e);
log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, p->fd);
sets++;
}
if (w && FD_ISSET(p->fd, w)) {
FD_CLR(p->fd, w);
log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, p->fd);
sets++;
}
}
return sets;
}
int
physical_IsSet(struct descriptor *d, const fd_set *fdset)
{
struct physical *p = descriptor2physical(d);
return p->fd >= 0 && FD_ISSET(p->fd, fdset);
}
void
physical_Login(struct physical *p, const char *name)
{
if (p->type == PHYS_DIRECT && *p->name.base && !p->Utmp) {
struct utmp ut;
const char *connstr;
memset(&ut, 0, sizeof ut);
time(&ut.ut_time);
strncpy(ut.ut_name, name, sizeof ut.ut_name);
strncpy(ut.ut_line, p->name.base, sizeof ut.ut_line);
if ((connstr = getenv("CONNECT")))
/* mgetty sets this to the connection speed */
strncpy(ut.ut_host, connstr, sizeof ut.ut_host);
ID0login(&ut);
p->Utmp = 1;
}
}
int
physical_SetMode(struct physical *p, int mode)
{
if ((p->type & (PHYS_DIRECT|PHYS_DEDICATED) ||
mode & (PHYS_DIRECT|PHYS_DEDICATED)) &&
(!(p->type & PHYS_DIRECT) || !(mode & PHYS_BACKGROUND))) {
log_Printf(LogWARN, "%s: Cannot change mode %s to %s\n", p->link.name,
mode2Nam(p->type), mode2Nam(mode));
return 0;
}
p->type = mode;
return 1;
}
void
physical_DeleteQueue(struct physical *p)
{
if (p->out) {
mbuf_Free(p->out);
p->out = NULL;
}
link_DeleteQueue(&p->link);
}
void
physical_SetDevice(struct physical *p, const char *name)
{
int len = strlen(_PATH_DEV);
strncpy(p->name.full, name, sizeof p->name.full - 1);
p->name.full[sizeof p->name.full - 1] = '\0';
p->name.base = *p->name.full == '!' ? p->name.full + 1 :
strncmp(p->name.full, _PATH_DEV, len) ?
p->name.full : p->name.full + len;
}
static void
physical_Found(struct physical *p)
{
throughput_start(&p->link.throughput, "physical throughput",
Enabled(p->dl->bundle, OPT_THROUGHPUT));
p->connect_count++;
p->input.sz = 0;
log_Printf(LogPHASE, "%s: Connected!\n", p->link.name);
}
int
physical_Open(struct physical *p, struct bundle *bundle)
{
int devno, h;
char *dev;
if (p->fd >= 0)
log_Printf(LogDEBUG, "%s: Open: Modem is already open!\n", p->link.name);
/* We're going back into "term" mode */
else if (p->type == PHYS_DIRECT) {
physical_SetDevice(p, "");
p->fd = STDIN_FILENO;
for (h = 0; h < NDEVICES && p->handler == NULL && p->fd >= 0; h++)
p->handler = (*devices[h].create)(p);
if (p->fd >= 0) {
if (p->handler == NULL)
physical_SetupStack(p, PHYSICAL_NOFORCE);
physical_Found(p);
if (p->handler == NULL)
log_Printf(LogDEBUG, "%s: stdin is unidentified\n",
p->link.name);
}
} else {
dev = p->cfg.devlist;
devno = 0;
while (devno < p->cfg.ndev && p->fd < 0) {
physical_SetDevice(p, dev);
if (*p->name.full == '/')
p->fd = ID0open(p->name.full, O_RDWR | O_NONBLOCK);
for (h = 0; h < NDEVICES && p->handler == NULL; h++)
p->handler = (*devices[h].create)(p);
if (p->fd < 0)
log_Printf(LogWARN, "%s: Device (%s) must begin with a '/',"
" a '!' or be a host:port pair\n", p->link.name,
p->name.full);
else
physical_Found(p);
dev += strlen(dev) + 1;
devno++;
}
}
return p->fd;
}
void
physical_SetupStack(struct physical *p, int how)
{
link_EmptyStack(&p->link);
if (how == PHYSICAL_FORCE_SYNC ||
(how == PHYSICAL_NOFORCE && physical_IsSync(p)))
link_Stack(&p->link, &synclayer);
else {
link_Stack(&p->link, &asynclayer);
link_Stack(&p->link, &hdlclayer);
}
link_Stack(&p->link, &acflayer);
link_Stack(&p->link, &protolayer);
link_Stack(&p->link, &lqrlayer);
link_Stack(&p->link, &ccplayer);
link_Stack(&p->link, &vjlayer);
#ifndef NOALIAS
link_Stack(&p->link, &aliaslayer);
#endif
if (how == PHYSICAL_FORCE_ASYNC && physical_IsSync(p)) {
log_Printf(LogWARN, "Sync device setting ignored for ``%s'' device\n",
p->handler ? p->handler->name : "unknown");
p->cfg.speed = MODEM_SPEED;
} else if (how == PHYSICAL_FORCE_SYNC && !physical_IsSync(p)) {
log_Printf(LogWARN, "Async device setting ignored for ``%s'' device\n",
p->handler ? p->handler->name : "unknown");
physical_SetSync(p);
}
}
void
physical_StopDeviceTimer(struct physical *p)
{
if (p->handler && p->handler->stoptimer)
(*p->handler->stoptimer)(p);
}