1de7b4b805
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts. No functional change intended.
773 lines
21 KiB
C
773 lines
21 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/un.h>
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sysexits.h>
|
|
#include <sys/uio.h>
|
|
#include <termios.h>
|
|
#include <ttyent.h>
|
|
#include <unistd.h>
|
|
#ifndef NONETGRAPH
|
|
#include <netgraph.h>
|
|
#include <netgraph/ng_async.h>
|
|
#include <netgraph/ng_message.h>
|
|
#include <netgraph/ng_ppp.h>
|
|
#include <netgraph/ng_tty.h>
|
|
#endif
|
|
|
|
#include "layer.h"
|
|
#include "defs.h"
|
|
#include "mbuf.h"
|
|
#include "log.h"
|
|
#include "timer.h"
|
|
#include "lqr.h"
|
|
#include "hdlc.h"
|
|
#include "throughput.h"
|
|
#include "fsm.h"
|
|
#include "lcp.h"
|
|
#include "ccp.h"
|
|
#include "link.h"
|
|
#include "async.h"
|
|
#include "descriptor.h"
|
|
#include "physical.h"
|
|
#include "mp.h"
|
|
#include "chat.h"
|
|
#include "auth.h"
|
|
#include "chap.h"
|
|
#include "cbcp.h"
|
|
#include "datalink.h"
|
|
#include "main.h"
|
|
#include "id.h"
|
|
#include "tty.h"
|
|
|
|
#if defined(__mac68k__) || defined(__macppc__)
|
|
#undef CRTS_IFLOW
|
|
#undef CCTS_OFLOW
|
|
#define CRTS_IFLOW CDTRCTS
|
|
#define CCTS_OFLOW CDTRCTS
|
|
#endif
|
|
|
|
#define Online(dev) ((dev)->mbits & TIOCM_CD)
|
|
|
|
struct ttydevice {
|
|
struct device dev; /* What struct physical knows about */
|
|
struct pppTimer Timer; /* CD checks */
|
|
int mbits; /* Current DCD status */
|
|
int carrier_seconds; /* seconds before CD is *required* */
|
|
#ifndef NONETGRAPH
|
|
struct {
|
|
unsigned speed; /* Pre-line-discipline speed */
|
|
int fd; /* Pre-line-discipline fd */
|
|
int disc; /* Old line-discipline */
|
|
} real;
|
|
char hook[sizeof NG_ASYNC_HOOK_SYNC]; /* our ng_socket hook */
|
|
int cs; /* A netgraph control socket (maybe) */
|
|
#endif
|
|
struct termios ios; /* To be able to reset from raw mode */
|
|
};
|
|
|
|
#define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
|
|
|
|
unsigned
|
|
tty_DeviceSize(void)
|
|
{
|
|
return sizeof(struct ttydevice);
|
|
}
|
|
|
|
/*
|
|
* tty_Timeout() watches the DCD signal and mentions it if it's status
|
|
* changes.
|
|
*/
|
|
static void
|
|
tty_Timeout(void *data)
|
|
{
|
|
struct physical *p = data;
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
int ombits, change;
|
|
|
|
timer_Stop(&dev->Timer);
|
|
dev->Timer.load = SECTICKS; /* Once a second please */
|
|
timer_Start(&dev->Timer);
|
|
ombits = dev->mbits;
|
|
|
|
if (p->fd >= 0) {
|
|
if (ioctl(p->fd, TIOCMGET, &dev->mbits) < 0) {
|
|
/* we must be a pty ? */
|
|
if (p->cfg.cd.necessity != CD_DEFAULT)
|
|
log_Printf(LogWARN, "%s: Carrier ioctl not supported, "
|
|
"using ``set cd off''\n", p->link.name);
|
|
timer_Stop(&dev->Timer);
|
|
dev->mbits = TIOCM_CD;
|
|
return;
|
|
}
|
|
} else
|
|
dev->mbits = 0;
|
|
|
|
if (ombits == -1) {
|
|
/* First time looking for carrier */
|
|
if (Online(dev))
|
|
log_Printf(LogPHASE, "%s: %s: CD detected\n", p->link.name, p->name.full);
|
|
else if (++dev->carrier_seconds >= dev->dev.cd.delay) {
|
|
if (dev->dev.cd.necessity == CD_REQUIRED)
|
|
log_Printf(LogPHASE, "%s: %s: Required CD not detected\n",
|
|
p->link.name, p->name.full);
|
|
else {
|
|
log_Printf(LogPHASE, "%s: %s doesn't support CD\n",
|
|
p->link.name, p->name.full);
|
|
dev->mbits = TIOCM_CD; /* Dodgy null-modem cable ? */
|
|
}
|
|
timer_Stop(&dev->Timer);
|
|
/* tty_AwaitCarrier() will notice */
|
|
} else {
|
|
/* Keep waiting */
|
|
log_Printf(LogDEBUG, "%s: %s: Still no carrier (%d/%d)\n",
|
|
p->link.name, p->name.full, dev->carrier_seconds,
|
|
dev->dev.cd.delay);
|
|
dev->mbits = -1;
|
|
}
|
|
} else {
|
|
change = ombits ^ dev->mbits;
|
|
if (change & TIOCM_CD) {
|
|
if (dev->mbits & TIOCM_CD)
|
|
log_Printf(LogDEBUG, "%s: offline -> online\n", p->link.name);
|
|
else {
|
|
log_Printf(LogDEBUG, "%s: online -> offline\n", p->link.name);
|
|
log_Printf(LogPHASE, "%s: Carrier lost\n", p->link.name);
|
|
datalink_Down(p->dl, CLOSE_NORMAL);
|
|
timer_Stop(&dev->Timer);
|
|
}
|
|
} else
|
|
log_Printf(LogDEBUG, "%s: Still %sline\n", p->link.name,
|
|
Online(dev) ? "on" : "off");
|
|
}
|
|
}
|
|
|
|
static void
|
|
tty_StartTimer(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
timer_Stop(&dev->Timer);
|
|
dev->Timer.load = SECTICKS;
|
|
dev->Timer.func = tty_Timeout;
|
|
dev->Timer.name = "tty CD";
|
|
dev->Timer.arg = p;
|
|
log_Printf(LogDEBUG, "%s: Using tty_Timeout [%p]\n",
|
|
p->link.name, tty_Timeout);
|
|
timer_Start(&dev->Timer);
|
|
}
|
|
|
|
static int
|
|
tty_AwaitCarrier(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
if (dev->dev.cd.necessity == CD_NOTREQUIRED || physical_IsSync(p))
|
|
return CARRIER_OK;
|
|
|
|
if (dev->mbits == -1) {
|
|
if (dev->Timer.state == TIMER_STOPPED) {
|
|
dev->carrier_seconds = 0;
|
|
tty_StartTimer(p);
|
|
}
|
|
return CARRIER_PENDING; /* Not yet ! */
|
|
}
|
|
|
|
return Online(dev) ? CARRIER_OK : CARRIER_LOST;
|
|
}
|
|
|
|
#ifdef NONETGRAPH
|
|
#define tty_SetAsyncParams NULL
|
|
#define tty_Write NULL
|
|
#define tty_Read NULL
|
|
#else
|
|
|
|
static int
|
|
isngtty(struct ttydevice *dev)
|
|
{
|
|
return dev->real.fd != -1;
|
|
}
|
|
|
|
static void
|
|
tty_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
char asyncpath[NG_PATHSIZ];
|
|
struct ng_async_cfg cfg;
|
|
|
|
if (isngtty(dev)) {
|
|
/* Configure the async converter node */
|
|
|
|
snprintf(asyncpath, sizeof asyncpath, ".:%s", dev->hook);
|
|
memset(&cfg, 0, sizeof cfg);
|
|
cfg.enabled = 1;
|
|
cfg.accm = mymap | hismap;
|
|
cfg.amru = MAX_MTU;
|
|
cfg.smru = MAX_MRU;
|
|
log_Printf(LogDEBUG, "Configure async node at %s\n", asyncpath);
|
|
if (NgSendMsg(dev->cs, asyncpath, NGM_ASYNC_COOKIE,
|
|
NGM_ASYNC_CMD_SET_CONFIG, &cfg, sizeof cfg) < 0)
|
|
log_Printf(LogWARN, "%s: Can't configure async node at %s\n",
|
|
p->link.name, asyncpath);
|
|
} else
|
|
/* No netgraph node, just config the async layer */
|
|
async_SetLinkParams(&p->async, mymap, hismap);
|
|
}
|
|
|
|
static int
|
|
LoadLineDiscipline(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
|
|
struct ng_mesg *reply;
|
|
struct nodeinfo *info;
|
|
char ttypath[NG_NODESIZ];
|
|
struct ngm_mkpeer ngm;
|
|
struct ngm_connect ngc;
|
|
int ldisc, cs, ds, hot;
|
|
unsigned speed;
|
|
|
|
/*
|
|
* Don't use the netgraph line discipline for now. Using it works, but
|
|
* carrier cannot be detected via TIOCMGET and the device doesn't become
|
|
* selectable with 0 bytes to read when carrier is lost :(
|
|
*/
|
|
return 0;
|
|
|
|
reply = (struct ng_mesg *)rbuf;
|
|
info = (struct nodeinfo *)reply->data;
|
|
|
|
loadmodules(LOAD_VERBOSLY, "netgraph", "ng_tty", "ng_async", "ng_socket",
|
|
NULL);
|
|
|
|
/* Get the speed before loading the line discipline */
|
|
speed = physical_GetSpeed(p);
|
|
|
|
if (ioctl(p->fd, TIOCGETD, &dev->real.disc) < 0) {
|
|
log_Printf(LogDEBUG, "%s: Couldn't get tty line discipline\n",
|
|
p->link.name);
|
|
return 0;
|
|
}
|
|
ldisc = NETGRAPHDISC;
|
|
if (ID0ioctl(p->fd, TIOCSETD, &ldisc) < 0) {
|
|
log_Printf(LogDEBUG, "%s: Couldn't set NETGRAPHDISC line discipline\n",
|
|
p->link.name);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the name of the tty node */
|
|
if (ioctl(p->fd, NGIOCGINFO, info) < 0) {
|
|
log_Printf(LogWARN, "%s: ioctl(NGIOCGINFO): %s\n", p->link.name,
|
|
strerror(errno));
|
|
ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
|
|
return 0;
|
|
}
|
|
snprintf(ttypath, sizeof ttypath, "%s:", info->name);
|
|
|
|
/* Create a socket node for our endpoint (and to send messages via) */
|
|
if (ID0NgMkSockNode(NULL, &cs, &ds) == -1) {
|
|
log_Printf(LogWARN, "%s: NgMkSockNode: %s\n", p->link.name,
|
|
strerror(errno));
|
|
ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
|
|
return 0;
|
|
}
|
|
|
|
/* Set the ``hot char'' on the TTY node */
|
|
hot = HDLC_SYN;
|
|
log_Printf(LogDEBUG, "%s: Set tty hotchar to 0x%02x\n", p->link.name, hot);
|
|
if (NgSendMsg(cs, ttypath, NGM_TTY_COOKIE,
|
|
NGM_TTY_SET_HOTCHAR, &hot, sizeof hot) < 0) {
|
|
log_Printf(LogWARN, "%s: Can't set hot char\n", p->link.name);
|
|
goto failed;
|
|
}
|
|
|
|
/* Attach an async converter node */
|
|
snprintf(ngm.type, sizeof ngm.type, "%s", NG_ASYNC_NODE_TYPE);
|
|
snprintf(ngm.ourhook, sizeof ngm.ourhook, "%s", NG_TTY_HOOK);
|
|
snprintf(ngm.peerhook, sizeof ngm.peerhook, "%s", NG_ASYNC_HOOK_ASYNC);
|
|
log_Printf(LogDEBUG, "%s: Send mkpeer async:%s to %s:%s\n", p->link.name,
|
|
ngm.peerhook, ttypath, ngm.ourhook);
|
|
if (NgSendMsg(cs, ttypath, NGM_GENERIC_COOKIE,
|
|
NGM_MKPEER, &ngm, sizeof ngm) < 0) {
|
|
log_Printf(LogWARN, "%s: Can't create %s node\n", p->link.name,
|
|
NG_ASYNC_NODE_TYPE);
|
|
goto failed;
|
|
}
|
|
|
|
/* Connect the async node to our socket */
|
|
snprintf(ngc.path, sizeof ngc.path, "%s%s", ttypath, NG_TTY_HOOK);
|
|
snprintf(ngc.peerhook, sizeof ngc.peerhook, "%s", NG_ASYNC_HOOK_SYNC);
|
|
memcpy(ngc.ourhook, ngc.peerhook, sizeof ngc.ourhook);
|
|
log_Printf(LogDEBUG, "%s: Send connect %s:%s to .:%s\n", p->link.name,
|
|
ngc.path, ngc.peerhook, ngc.ourhook);
|
|
if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT,
|
|
&ngc, sizeof ngc) < 0) {
|
|
log_Printf(LogWARN, "%s: Can't connect .:%s -> %s.%s: %s\n",
|
|
p->link.name, ngc.ourhook, ngc.path, ngc.peerhook,
|
|
strerror(errno));
|
|
goto failed;
|
|
}
|
|
|
|
/* Get the async node id */
|
|
if (NgSendMsg(cs, ngc.path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) {
|
|
log_Printf(LogWARN, "%s: Can't request async node info at %s: %s\n",
|
|
p->link.name, ngc.path, strerror(errno));
|
|
goto failed;
|
|
}
|
|
if (NgRecvMsg(cs, reply, sizeof rbuf, NULL) < 0) {
|
|
log_Printf(LogWARN, "%s: Can't obtain async node info at %s: %s\n",
|
|
p->link.name, ngc.path, strerror(errno));
|
|
goto failed;
|
|
}
|
|
|
|
/* All done, set up our device state */
|
|
snprintf(dev->hook, sizeof dev->hook, "%s", ngc.ourhook);
|
|
dev->cs = cs;
|
|
dev->real.fd = p->fd;
|
|
p->fd = ds;
|
|
dev->real.speed = speed;
|
|
physical_SetSync(p);
|
|
|
|
tty_SetAsyncParams(p, 0xffffffff, 0xffffffff);
|
|
physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
|
|
log_Printf(LogPHASE, "%s: Loaded netgraph tty line discipline\n",
|
|
p->link.name);
|
|
|
|
return 1;
|
|
|
|
failed:
|
|
ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
|
|
close(ds);
|
|
close(cs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
UnloadLineDiscipline(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
if (isngtty(dev)) {
|
|
if (!physical_SetSpeed(p, dev->real.speed))
|
|
log_Printf(LogWARN, "Couldn't reset tty speed to %d\n", dev->real.speed);
|
|
dev->real.speed = 0;
|
|
close(p->fd);
|
|
p->fd = dev->real.fd;
|
|
dev->real.fd = -1;
|
|
close(dev->cs);
|
|
dev->cs = -1;
|
|
*dev->hook = '\0';
|
|
if (ID0ioctl(p->fd, TIOCSETD, &dev->real.disc) == 0) {
|
|
physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
|
|
log_Printf(LogPHASE, "%s: Unloaded netgraph tty line discipline\n",
|
|
p->link.name);
|
|
} else
|
|
log_Printf(LogWARN, "%s: Failed to unload netgraph tty line discipline\n",
|
|
p->link.name);
|
|
}
|
|
}
|
|
|
|
static ssize_t
|
|
tty_Write(struct physical *p, const void *v, size_t n)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
if (isngtty(dev))
|
|
return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
|
|
else
|
|
return write(p->fd, v, n);
|
|
}
|
|
|
|
static ssize_t
|
|
tty_Read(struct physical *p, void *v, size_t n)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
char hook[sizeof NG_ASYNC_HOOK_SYNC];
|
|
|
|
if (isngtty(dev))
|
|
return NgRecvData(p->fd, v, n, hook);
|
|
else
|
|
return read(p->fd, v, n);
|
|
}
|
|
|
|
#endif /* NETGRAPH */
|
|
|
|
static int
|
|
tty_Raw(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
struct termios ios;
|
|
int oldflag;
|
|
|
|
log_Printf(LogDEBUG, "%s: Entering tty_Raw\n", p->link.name);
|
|
|
|
if (p->type != PHYS_DIRECT && p->fd >= 0 && !Online(dev))
|
|
log_Printf(LogDEBUG, "%s: Raw: descriptor = %d, mbits = %x\n",
|
|
p->link.name, p->fd, dev->mbits);
|
|
|
|
if (!physical_IsSync(p)) {
|
|
#ifndef NONETGRAPH
|
|
if (!LoadLineDiscipline(p))
|
|
#endif
|
|
{
|
|
tcgetattr(p->fd, &ios);
|
|
cfmakeraw(&ios);
|
|
if (p->cfg.rts_cts)
|
|
ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
|
|
else
|
|
ios.c_cflag |= CLOCAL;
|
|
|
|
if (p->type != PHYS_DEDICATED)
|
|
ios.c_cflag |= HUPCL;
|
|
|
|
if (tcsetattr(p->fd, TCSANOW, &ios) == -1)
|
|
log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
|
|
p->link.name);
|
|
}
|
|
}
|
|
|
|
oldflag = fcntl(p->fd, F_GETFL, 0);
|
|
if (oldflag < 0)
|
|
return 0;
|
|
fcntl(p->fd, F_SETFL, oldflag | O_NONBLOCK);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
tty_Offline(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
if (p->fd >= 0) {
|
|
timer_Stop(&dev->Timer);
|
|
dev->mbits &= ~TIOCM_DTR; /* XXX: Hmm, what's this supposed to do ? */
|
|
if (Online(dev)) {
|
|
struct termios tio;
|
|
|
|
tcgetattr(p->fd, &tio);
|
|
if (cfsetspeed(&tio, B0) == -1 || tcsetattr(p->fd, TCSANOW, &tio) == -1)
|
|
log_Printf(LogWARN, "%s: Unable to set physical to speed 0\n",
|
|
p->link.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
tty_Cooked(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
int oldflag;
|
|
|
|
tty_Offline(p); /* In case of emergency close()s */
|
|
|
|
tcflush(p->fd, TCIOFLUSH);
|
|
|
|
if (!physical_IsSync(p) && tcsetattr(p->fd, TCSAFLUSH, &dev->ios) == -1)
|
|
log_Printf(LogWARN, "%s: tcsetattr: Unable to restore device settings\n",
|
|
p->link.name);
|
|
|
|
#ifndef NONETGRAPH
|
|
UnloadLineDiscipline(p);
|
|
#endif
|
|
|
|
if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
|
|
fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
|
|
}
|
|
|
|
static void
|
|
tty_StopTimer(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
timer_Stop(&dev->Timer);
|
|
}
|
|
|
|
static void
|
|
tty_Free(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
|
|
tty_Offline(p); /* In case of emergency close()s */
|
|
free(dev);
|
|
}
|
|
|
|
static unsigned
|
|
tty_Speed(struct physical *p)
|
|
{
|
|
struct termios ios;
|
|
|
|
if (tcgetattr(p->fd, &ios) == -1)
|
|
return 0;
|
|
|
|
return SpeedToUnsigned(cfgetispeed(&ios));
|
|
}
|
|
|
|
static const char *
|
|
tty_OpenInfo(struct physical *p)
|
|
{
|
|
struct ttydevice *dev = device2tty(p->handler);
|
|
static char buf[13];
|
|
|
|
if (Online(dev))
|
|
strcpy(buf, "with");
|
|
else
|
|
strcpy(buf, "no");
|
|
strcat(buf, " carrier");
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int
|
|
tty_Slot(struct physical *p)
|
|
{
|
|
struct ttyent *ttyp;
|
|
int slot;
|
|
|
|
setttyent();
|
|
for (slot = 1; (ttyp = getttyent()); ++slot)
|
|
if (!strcmp(ttyp->ty_name, p->name.base)) {
|
|
endttyent();
|
|
return slot;
|
|
}
|
|
|
|
endttyent();
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
tty_device2iov(struct device *d, struct iovec *iov, int *niov,
|
|
int maxiov __unused,
|
|
#ifndef NONETGRAPH
|
|
int *auxfd, int *nauxfd
|
|
#else
|
|
int *auxfd __unused, int *nauxfd __unused
|
|
#endif
|
|
)
|
|
{
|
|
struct ttydevice *dev;
|
|
int sz = physical_MaxDeviceSize();
|
|
|
|
iov[*niov].iov_base = d = realloc(d, sz);
|
|
if (d == NULL) {
|
|
log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
|
|
AbortProgram(EX_OSERR);
|
|
}
|
|
iov[*niov].iov_len = sz;
|
|
(*niov)++;
|
|
|
|
dev = device2tty(d);
|
|
|
|
#ifndef NONETGRAPH
|
|
if (dev->cs >= 0) {
|
|
*auxfd = dev->cs;
|
|
(*nauxfd)++;
|
|
}
|
|
#endif
|
|
|
|
if (dev->Timer.state != TIMER_STOPPED) {
|
|
timer_Stop(&dev->Timer);
|
|
dev->Timer.state = TIMER_RUNNING;
|
|
}
|
|
}
|
|
|
|
static struct device basettydevice = {
|
|
TTY_DEVICE,
|
|
"tty",
|
|
0,
|
|
{ CD_VARIABLE, DEF_TTYCDDELAY },
|
|
tty_AwaitCarrier,
|
|
NULL,
|
|
tty_Raw,
|
|
tty_Offline,
|
|
tty_Cooked,
|
|
tty_SetAsyncParams,
|
|
tty_StopTimer,
|
|
tty_Free,
|
|
tty_Read,
|
|
tty_Write,
|
|
tty_device2iov,
|
|
tty_Speed,
|
|
tty_OpenInfo,
|
|
tty_Slot
|
|
};
|
|
|
|
struct device *
|
|
tty_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
|
|
int maxiov __unused,
|
|
#ifndef NONETGRAPH
|
|
int *auxfd, int *nauxfd
|
|
#else
|
|
int *auxfd __unused, int *nauxfd __unused
|
|
#endif
|
|
)
|
|
{
|
|
if (type == TTY_DEVICE) {
|
|
struct ttydevice *dev = (struct ttydevice *)iov[(*niov)++].iov_base;
|
|
|
|
dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
|
|
if (dev == NULL) {
|
|
log_Printf(LogALERT, "Failed to allocate memory: %d\n",
|
|
(int)(sizeof *dev));
|
|
AbortProgram(EX_OSERR);
|
|
}
|
|
|
|
#ifndef NONETGRAPH
|
|
if (*nauxfd) {
|
|
dev->cs = *auxfd;
|
|
(*nauxfd)--;
|
|
} else
|
|
dev->cs = -1;
|
|
#endif
|
|
|
|
/* Refresh function pointers etc */
|
|
memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
|
|
|
|
physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
|
|
if (dev->Timer.state != TIMER_STOPPED) {
|
|
dev->Timer.state = TIMER_STOPPED;
|
|
p->handler = &dev->dev; /* For the benefit of StartTimer */
|
|
tty_StartTimer(p);
|
|
}
|
|
return &dev->dev;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct device *
|
|
tty_Create(struct physical *p)
|
|
{
|
|
struct ttydevice *dev;
|
|
struct termios ios;
|
|
int oldflag;
|
|
|
|
if (p->fd < 0 || !isatty(p->fd))
|
|
/* Don't want this */
|
|
return NULL;
|
|
|
|
if (*p->name.full == '\0') {
|
|
physical_SetDevice(p, ttyname(p->fd));
|
|
log_Printf(LogDEBUG, "%s: Input is a tty (%s)\n",
|
|
p->link.name, p->name.full);
|
|
} else
|
|
log_Printf(LogDEBUG, "%s: Opened %s\n", p->link.name, p->name.full);
|
|
|
|
/* We're gonna return a ttydevice (unless something goes horribly wrong) */
|
|
|
|
if ((dev = malloc(sizeof *dev)) == NULL) {
|
|
/* Complete failure - parent doesn't continue trying to ``create'' */
|
|
close(p->fd);
|
|
p->fd = -1;
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
|
|
memset(&dev->Timer, '\0', sizeof dev->Timer);
|
|
dev->mbits = -1;
|
|
#ifndef NONETGRAPH
|
|
dev->real.speed = 0;
|
|
dev->real.fd = -1;
|
|
dev->real.disc = -1;
|
|
*dev->hook = '\0';
|
|
#endif
|
|
tcgetattr(p->fd, &ios);
|
|
dev->ios = ios;
|
|
|
|
if (p->cfg.cd.necessity != CD_DEFAULT)
|
|
/* Any override is ok for the tty device */
|
|
dev->dev.cd = p->cfg.cd;
|
|
|
|
log_Printf(LogDEBUG, "%s: tty_Create: physical (get): fd = %d,"
|
|
" iflag = %lx, oflag = %lx, cflag = %lx\n", p->link.name, p->fd,
|
|
(u_long)ios.c_iflag, (u_long)ios.c_oflag, (u_long)ios.c_cflag);
|
|
|
|
cfmakeraw(&ios);
|
|
if (p->cfg.rts_cts)
|
|
ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
|
|
else {
|
|
ios.c_cflag |= CLOCAL;
|
|
ios.c_iflag |= IXOFF;
|
|
}
|
|
ios.c_iflag |= IXON;
|
|
if (p->type != PHYS_DEDICATED)
|
|
ios.c_cflag |= HUPCL;
|
|
|
|
if (p->type != PHYS_DIRECT) {
|
|
/* Change tty speed when we're not in -direct mode */
|
|
ios.c_cflag &= ~(CSIZE | PARODD | PARENB);
|
|
ios.c_cflag |= p->cfg.parity;
|
|
if (cfsetspeed(&ios, UnsignedToSpeed(p->cfg.speed)) == -1)
|
|
log_Printf(LogWARN, "%s: %s: Unable to set speed to %d\n",
|
|
p->link.name, p->name.full, p->cfg.speed);
|
|
}
|
|
|
|
if (tcsetattr(p->fd, TCSADRAIN, &ios) == -1) {
|
|
log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
|
|
p->link.name);
|
|
if (p->type != PHYS_DIRECT && p->cfg.speed > 115200)
|
|
log_Printf(LogWARN, "%.*s Perhaps the speed is unsupported\n",
|
|
(int)strlen(p->link.name), "");
|
|
}
|
|
|
|
log_Printf(LogDEBUG, "%s: physical (put): iflag = %lx, oflag = %lx, "
|
|
"cflag = %lx\n", p->link.name, (u_long)ios.c_iflag,
|
|
(u_long)ios.c_oflag, (u_long)ios.c_cflag);
|
|
|
|
oldflag = fcntl(p->fd, F_GETFL, 0);
|
|
if (oldflag < 0) {
|
|
/* Complete failure - parent doesn't continue trying to ``create'' */
|
|
|
|
log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
|
|
p->link.name, strerror(errno));
|
|
tty_Cooked(p);
|
|
close(p->fd);
|
|
p->fd = -1;
|
|
free(dev);
|
|
return NULL;
|
|
} else
|
|
fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
|
|
|
|
physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
|
|
|
|
return &dev->dev;
|
|
}
|