2491 lines
57 KiB
C
2491 lines
57 KiB
C
/*
|
|
* This file is part of the ISDN master program.
|
|
*
|
|
* Copyright (C) 1995 Matthias Urlichs.
|
|
* See the file COPYING for license details.
|
|
*/
|
|
|
|
#include "master.h"
|
|
#include "isdn_12.h" /* for the header keys */
|
|
|
|
/* Once upon a time, do_info() was a very big function. */
|
|
|
|
#ifndef __GNUC__
|
|
#error "Sorry, but you need GCC's nested functions for this."
|
|
#endif
|
|
|
|
extern void log_printmsg (void *log, const char *, mblk_t *, const char *);
|
|
|
|
void
|
|
do_info (streamchar * data, int len)
|
|
{
|
|
mblk_t xx;
|
|
struct datab db;
|
|
ushort_t id;
|
|
ushort_t ind;
|
|
streamchar ans[MAXLINE];
|
|
char *msgbuf;
|
|
char *resp;
|
|
long fminor;
|
|
long minor;
|
|
long callref;
|
|
long foffset;
|
|
long thelength;
|
|
long seqnum;
|
|
struct conninfo *conn;
|
|
char crd[5];
|
|
char prot[40];
|
|
char site[40];
|
|
char nr[MAXNR + 2];
|
|
char lnr[MAXNR + 2];
|
|
long uid;
|
|
long connref;
|
|
char dialin;
|
|
long charge;
|
|
ushort_t cause;
|
|
int xlen;
|
|
int has_force;
|
|
long bchan;
|
|
long subcard;
|
|
long hdrval;
|
|
char no_error;
|
|
struct loader *loader;
|
|
long errnum;
|
|
char incomplete;
|
|
|
|
|
|
/* Take the incoming arguments and put them into their variables. */
|
|
int
|
|
parse_arg(void)
|
|
{
|
|
switch (id) {
|
|
case ARG_CAUSE:
|
|
(void)m_getid(&xx,&cause);
|
|
break;
|
|
case ARG_INCOMPLETE:
|
|
incomplete = 1;
|
|
break;
|
|
case ARG_CHARGE:
|
|
(void)m_geti(&xx,&charge);
|
|
break;
|
|
case ARG_FORCE:
|
|
has_force=1;
|
|
break;
|
|
case ARG_LLC:
|
|
case ARG_ULC:
|
|
case ARG_BEARER:
|
|
break;
|
|
case ARG_NUMBER:
|
|
{
|
|
int err2;
|
|
|
|
if ((err2 = m_getstr (&xx, nr, MAXNR)) != 0) {
|
|
if (err2 != -ENOENT && err2 != -ESRCH) {
|
|
printf (" XErr 0\n");
|
|
return 2;
|
|
}
|
|
}
|
|
} break;
|
|
case ARG_LNUMBER:
|
|
{
|
|
int err2;
|
|
|
|
if ((err2 = m_getstr (&xx, lnr, MAXNR)) != 0) {
|
|
if (err2 != -ENOENT && err2 != -ESRCH) {
|
|
printf (" XErr 02\n");
|
|
return 2;
|
|
}
|
|
}
|
|
} break;
|
|
case ARG_CARD:
|
|
if (m_getstr (&xx, crd, 4) != 0) {
|
|
printf (" XErr 1\n");
|
|
return 2;
|
|
}
|
|
for(loader = isdn4_loader; loader != NULL; loader = loader->next) {
|
|
if(!strcmp(crd,loader->name))
|
|
break;
|
|
}
|
|
break;
|
|
case PROTO_INCOMING:
|
|
dialin = 1;
|
|
break;
|
|
case PROTO_OUTGOING:
|
|
dialin = 0;
|
|
break;
|
|
case ARG_MINOR:
|
|
if (m_geti (&xx, &minor) != 0) {
|
|
printf (" XErr 3\n");
|
|
return 2;
|
|
}
|
|
if (minor < 0 || minor >= NMINOR) {
|
|
printf (" XErr 4a\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_LENGTH:
|
|
if (m_geti (&xx, &thelength) != 0) {
|
|
printf (" XErr len\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_OFFSET:
|
|
if (m_geti (&xx, &foffset) != 0) {
|
|
printf (" XErr off\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_SEQNUM:
|
|
if (m_geti (&xx, &seqnum) != 0) {
|
|
printf (" XErr seq\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_UID:
|
|
if (m_geti (&xx, &uid) != 0) {
|
|
printf (" XErr u\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_ERRNO:
|
|
if (m_geti (&xx, &errnum) != 0) {
|
|
printf (" XErr en\n");
|
|
if(0)return 2;
|
|
}
|
|
break;
|
|
case ARG_ERRHDR:
|
|
if (m_geti (&xx, &hdrval) != 0) {
|
|
printf (" XErr 34\n");
|
|
if(0)return 2;
|
|
}
|
|
break;
|
|
case ARG_FMINOR:
|
|
{
|
|
long fmi;
|
|
if (m_geti (&xx, &fmi) != 0) {
|
|
printf (" XErr 3\n");
|
|
return 2;
|
|
}
|
|
if (fminor != 0 && fminor != fmi) {
|
|
resp = "ILLEGAL FMINOR";
|
|
return 1;
|
|
}
|
|
fminor = fmi;
|
|
if (fminor < 0 || fminor >= NMINOR) {
|
|
printf (" XErr 4b\n");
|
|
return 2;
|
|
}
|
|
} break;
|
|
case ARG_CALLREF:
|
|
if (m_geti (&xx, &callref) != 0) {
|
|
printf (" XErr 5x\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_CONNREF:
|
|
if (m_geti (&xx, &connref) != 0) {
|
|
printf (" XErr 5\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_SUBCARD:
|
|
if (m_geti (&xx, &subcard) != 0) {
|
|
printf (" XErr 9s\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_CHANNEL:
|
|
if (m_geti (&xx, &bchan) != 0) {
|
|
printf (" XErr 9a\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_SITE:
|
|
if (m_getstr (&xx, site, sizeof(site)-1) != 0) {
|
|
printf (" XErr 9s\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case ARG_STACK:
|
|
if (m_getstr (&xx, prot, sizeof(prot)-1) != 0) {
|
|
printf (" XErr 9\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Get the best-matching connection for the arguments we found. */
|
|
void
|
|
find_conn(void)
|
|
{
|
|
struct conninfo *xconn = NULL;
|
|
if(0)printf ("Check Conn %ld/%ld/%ld: ", minor, fminor, connref);
|
|
if(fminor == 0) fminor = minor;
|
|
for (conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->ignore)
|
|
continue;
|
|
if(0)printf ("%d/%d/%ld ", conn->minor, conn->fminor, conn->connref);
|
|
if ((connref != 0) && (conn->connref != 0)) {
|
|
if (conn->connref == connref)
|
|
break;
|
|
else
|
|
continue; /* the connection was taken over... */
|
|
}
|
|
if (((minor ? minor : fminor) != 0) && (conn->minor != 0)) {
|
|
if (conn->minor == (minor ? minor : fminor))
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
if ((fminor != 0) && (conn->fminor != 0)) {
|
|
if (conn->fminor == fminor)
|
|
xconn = conn;
|
|
}
|
|
#if 0
|
|
if (*(ulong_t *) crd != 0 && conn->cg->card != 0 && conn->cg->card != *(ulong_t *) crd)
|
|
continue;
|
|
#endif
|
|
}
|
|
if(0)printf ("\n");
|
|
if(conn == NULL)
|
|
conn = xconn;
|
|
if(conn != NULL) {
|
|
if(0)printf("Found Conn %d/%d, cref %ld\n",conn->minor,conn->fminor,conn->connref);
|
|
}
|
|
if (conn != NULL && ind != IND_OPEN) {
|
|
if (conn->minor == 0 && minor != 0) {
|
|
conn->minor = minor;
|
|
}
|
|
if (conn->fminor == 0 && fminor != 0) {
|
|
conn->fminor = fminor;
|
|
}
|
|
if (conn->connref == 0 && connref != 0) {
|
|
setconnref(conn,connref);
|
|
}
|
|
if (conn->cg != NULL && conn->cg->card == NULL)
|
|
conn->cg->card = str_enter(crd);
|
|
if(conn->flags & F_INCOMING)
|
|
dialin = 1;
|
|
if(charge > 0) {
|
|
if(conn->did_bounce && (conn->state < c_going_up)) {
|
|
if(conn->cg != NULL)
|
|
syslog(LOG_ERR,"OFF %s:%s %s",conn->cg->site,conn->cg->protocol,CauseInfo(conn->cause, conn->causeInfo));
|
|
else
|
|
syslog(LOG_ERR,"OFF ??? %s",CauseInfo(conn->cause, conn->causeInfo));
|
|
setconnstate(conn,c_off);
|
|
} else if((conn->retries > 0) && (conn->charge > 0)) {
|
|
if(conn->cg != NULL)
|
|
syslog(LOG_ALERT,"Cost Runaway, connection not closed for %s:%s",conn->cg->site,conn->cg->protocol);
|
|
else
|
|
syslog(LOG_ALERT,"Cost Runaway, connection not closed for ???");
|
|
}
|
|
conn->charge = charge;
|
|
ReportConn(conn);
|
|
run_rp(conn,'k');
|
|
}
|
|
if(cause != 0)
|
|
conn->cause = cause;
|
|
}
|
|
}
|
|
|
|
/* Self-explanatory... */
|
|
int
|
|
init_vars (void)
|
|
{
|
|
msgbuf = NULL;
|
|
resp = NULL;
|
|
fminor = 0;
|
|
minor = 0;
|
|
callref = 0;
|
|
conn = NULL;
|
|
site[0] = '*';
|
|
site[1] = '\0';
|
|
prot[0] = '*';
|
|
prot[1] = '\0';
|
|
nr[0] = '\0';
|
|
lnr[0] = '\0';
|
|
uid = -1;
|
|
connref = 0;
|
|
incomplete = 0;
|
|
dialin = -1;
|
|
charge = 0;
|
|
cause = 0;
|
|
has_force = 0;
|
|
bchan = -1;
|
|
hdrval = -1;
|
|
no_error = 0;
|
|
seqnum = 0;
|
|
foffset = -1;
|
|
thelength = -1;
|
|
errnum = 0;
|
|
subcard = 0;
|
|
|
|
*(ulong_t *) crd = 0;
|
|
crd[4] = '\0';
|
|
if(log_34 & 1) {
|
|
printf ("HL R ");
|
|
dumpascii (data, len);
|
|
printf ("\n");
|
|
}
|
|
data[len] = '\0';
|
|
xx.b_rptr = data;
|
|
xx.b_wptr = data + len;
|
|
xx.b_cont = NULL;
|
|
db.db_base = data;
|
|
db.db_lim = data + len;
|
|
db.db_type = M_DATA;
|
|
xx.b_datap = &db;
|
|
if (m_getid (&xx, &ind) != 0)
|
|
return 1;
|
|
(char *) data = (char *) xx.b_rptr;
|
|
return 0;
|
|
}
|
|
|
|
void do_updown(CState state)
|
|
{
|
|
int lim;
|
|
|
|
if(conn->cg && conn->cg->retries)
|
|
lim = conn->cg->retries;
|
|
else if(conn->flags & F_FORCEIN)
|
|
lim = 2;
|
|
else if(conn->retiming) /* we had a problem earlier */
|
|
lim = 5;
|
|
else if(conn->flags & F_FASTREDIAL)
|
|
lim = 20;
|
|
else
|
|
lim = 10;
|
|
if((conn->charge > 0) || (conn->retries >= lim)) {
|
|
if(conn->cg != NULL)
|
|
syslog(LOG_ERR,"OFF %s:%s %s",conn->cg->site,conn->cg->protocol,CauseInfo(conn->cause, conn->causeInfo));
|
|
else
|
|
syslog(LOG_ERR,"OFF ??? %s",CauseInfo(conn->cause, conn->causeInfo));
|
|
if(conn->state > c_off)
|
|
conn->retries++;
|
|
setconnstate(conn,c_off);
|
|
} else {
|
|
conn->retries++;
|
|
setconnstate(conn,has_force ? c_off : state);
|
|
}
|
|
}
|
|
|
|
void do_down(CState state)
|
|
{
|
|
if(conn->cg != NULL && conn->cg->mintime > 0) {
|
|
if(time(NULL) - conn->upwhen < conn->cg->mintime) {
|
|
syslog(LOG_ERR,"OFF %s:%s %s",conn->cg->site,conn->cg->protocol,CauseInfo(conn->cause, conn->causeInfo));
|
|
setconnstate(conn,c_off);
|
|
return;
|
|
}
|
|
}
|
|
setconnstate(conn,state);
|
|
}
|
|
|
|
|
|
/* Incoming IND_CARD -- a new ISDN card is recognized, or somebody put the
|
|
ISDN cable back in. */
|
|
int
|
|
do_card(void)
|
|
{
|
|
cf dl;
|
|
long nbchan, ndchan;
|
|
long cardcap;
|
|
int ret;
|
|
struct isdncard *card;
|
|
struct loader *ld;
|
|
|
|
if ((ret = m_getstr (&xx, crd, 4)) != 0)
|
|
return ret;
|
|
if ((ret = m_geti (&xx, &ndchan)) != 0)
|
|
return ret;
|
|
if ((ret = m_geti (&xx, &nbchan)) != 0)
|
|
return ret;
|
|
if ((ret = m_getx (&xx, &cardcap)) != 0)
|
|
return ret;
|
|
cards_known++;
|
|
for(ld = isdn4_loader; ld != NULL; ld = ld->next) {
|
|
if (!strcmp(ld->name, crd))
|
|
return -EEXIST;
|
|
}
|
|
for(card = isdn4_card; card != NULL; card = card->next) {
|
|
if (!strcmp(card->name, crd))
|
|
return -EEXIST;
|
|
}
|
|
card = gcxmalloc(sizeof(*card));
|
|
if(card == NULL)
|
|
return -ENOMEM;
|
|
bzero(card,sizeof(*card));
|
|
card->name = str_enter(crd);
|
|
card->nrbchan = nbchan;
|
|
card->nrdchan = ndchan;
|
|
card->mask = (1<<ndchan)-1; /* mask of all subcards */
|
|
card->cap = cardcap;
|
|
card->next = isdn4_card; isdn4_card = card;
|
|
|
|
if(cardcap & CHM_INTELLIGENT) {
|
|
ld = gcxmalloc(sizeof(struct loader));
|
|
if(ld == NULL)
|
|
return -errno;
|
|
bzero(ld,sizeof(*ld));
|
|
ld->card = card;
|
|
ld->name = str_enter(crd);
|
|
|
|
ld->next = isdn4_loader;
|
|
isdn4_loader = ld;
|
|
|
|
card->name = str_enter("NULL");
|
|
ld->card = card;
|
|
cards_loading++;
|
|
} else {
|
|
struct iovec io[3];
|
|
int len;
|
|
for(dl = cf_DL; dl != NULL; dl = dl->next) {
|
|
if(wildmatch(crd,dl->card))
|
|
break;
|
|
}
|
|
if(dl == NULL)
|
|
return -ENXIO;
|
|
card->name = str_enter(crd);
|
|
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_DOCARD);
|
|
m_putsx(&xx,ARG_CARD);
|
|
m_putsz(&xx,crd);
|
|
|
|
*xx.b_wptr++ = ' ';
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
io[0].iov_base = ans;
|
|
io[0].iov_len = xlen;
|
|
len = 1;
|
|
if(dl->args != NULL) {
|
|
if(log_34 & 1)printf ("+ ");
|
|
io[len].iov_base = ":: ";
|
|
io[len].iov_len = 3;
|
|
len++;
|
|
io[len].iov_base = dl->args;
|
|
io[len].iov_len = strlen(dl->args);
|
|
DUMPW (dl->args,io[len].iov_len);
|
|
len++;
|
|
}
|
|
(void) strwritev (xs_mon, io,len, 1);
|
|
}
|
|
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->causeInfo = ld ? "loading" : "passive interface";
|
|
conn->cause = ID_priv_Print;
|
|
conn->cardname = ld ? ld->name : card->name;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
if(ld != NULL)
|
|
ld->connseq = conn->seqnum;
|
|
dropconn(conn);
|
|
}
|
|
if(ld != NULL)
|
|
card_load(ld);
|
|
do_run_now++;
|
|
timeout(run_now,NULL,2*HZ);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* IND_NOCARD; a card got deconfigured. */
|
|
int
|
|
do_nocard(void)
|
|
{
|
|
int ret;
|
|
struct isdncard **pcard;
|
|
|
|
if ((ret = m_getstr(&xx, crd, 4)) != 0)
|
|
return ret;
|
|
for (pcard = &isdn4_card; *pcard != NULL; pcard = &(*pcard)->next) {
|
|
if (!strcmp((*pcard)->name, crd)) {
|
|
struct loader *ld;
|
|
struct isdncard *card = *pcard;
|
|
*pcard = card->next;
|
|
|
|
for(ld = isdn4_loader; ld != NULL; ld = ld->next) {
|
|
if (ld->card == card) {
|
|
card_load_fail(ld,-EIO);
|
|
break;
|
|
}
|
|
}
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
if(cause == 0) {
|
|
conn->causeInfo = "interface died";
|
|
conn->cause = ID_priv_Print;
|
|
} else
|
|
conn->cause = cause;
|
|
conn->cardname = card->name;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
resp = NULL;
|
|
break;
|
|
}
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
|
|
int
|
|
do_offcard(void)
|
|
{
|
|
int ret;
|
|
struct isdncard *card;
|
|
|
|
if ((ret = m_getstr(&xx, crd, 4)) != 0)
|
|
return ret;
|
|
|
|
for(card = isdn4_card; card != NULL; card = card->next) {
|
|
if (!strcmp(card->name, crd))
|
|
card->is_down = 1;
|
|
}
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->causeInfo = "card is offline";
|
|
conn->cause = ID_priv_Print;
|
|
conn->cardname = str_enter(crd);
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* IND_RECARD; an ISDN cable got reattached. */
|
|
int
|
|
do_recard(void)
|
|
{
|
|
int ret;
|
|
struct isdncard *card;
|
|
|
|
if ((ret = m_getstr(&xx, crd, 4)) != 0)
|
|
return ret;
|
|
for(card = isdn4_card; card != NULL; card = card->next) {
|
|
if (!strcmp(card->name, crd))
|
|
card->is_down = 0;
|
|
}
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->causeInfo = "card is back online";
|
|
conn->cause = ID_priv_Print;
|
|
conn->cardname = str_enter(crd);
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
|
|
for(conn=isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->ignore >= 3)
|
|
continue;
|
|
if(!(conn->flags & F_LEASED))
|
|
continue;
|
|
if(conn->cg == NULL)
|
|
continue;
|
|
if(wildmatch(crd,conn->cg->card) == NULL)
|
|
continue;
|
|
if(conn->state != c_off)
|
|
continue;
|
|
setconnstate(conn,c_down);
|
|
try_reconn(conn);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* IND_CARDPROTO: request to set the mode of a channel. */
|
|
/* Maps to a call of pushcardprot(). */
|
|
int
|
|
do_cardproto(void)
|
|
{
|
|
if (crd[0] == '\0' || connref == 0 || minor == 0) {
|
|
printf ("\n*** NoProto: Card %p, callref %ld, minor %ld\n", crd, callref, minor);
|
|
return -EINVAL;
|
|
}
|
|
if (conn == NULL) {
|
|
printf ("\n*** Warn NoConnProto: Card %p, callref %ld, minor %ld\n", crd, callref, minor);
|
|
}
|
|
{
|
|
conngrab cg = newgrab(conn ? conn->cg : NULL);
|
|
if(cg == NULL) {
|
|
resp = "OutOfMem";
|
|
return 1;
|
|
}
|
|
cg->card = str_enter(crd);
|
|
cg->site = str_enter(site);
|
|
cg->protocol = str_enter(prot);
|
|
if ((resp = findsite (&cg,1)) != NULL) {
|
|
dropgrab(cg);
|
|
if(conn != NULL) {
|
|
conn->want_reconn = 0;
|
|
setconnstate(conn, c_off);
|
|
if(conn->pid == 0) {
|
|
dropconn(conn);
|
|
conn = NULL;
|
|
}
|
|
}
|
|
|
|
syslog (LOG_ERR, "ISDN NoProtocol1 %s %ld %s", resp, minor, data);
|
|
if(conn != NULL) {
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = resp;
|
|
ReportConn(conn);
|
|
}
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis10 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
if(minor > 0) {
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
}
|
|
if(connref != 0) {
|
|
m_putsx (&xx, ARG_CONNREF);
|
|
m_puti (&xx, connref);
|
|
}
|
|
if(crd[0] != '\0') {
|
|
m_putsx(&xx,ARG_CARD);
|
|
m_putsz(&xx,crd);
|
|
}
|
|
if(callref != 0) {
|
|
m_putsx (&xx, ARG_CALLREF);
|
|
m_puti (&xx, callref);
|
|
}
|
|
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
|
|
return 2;
|
|
}
|
|
if (pushprot (cg, minor, connref, PUSH_BEFORE) == 0) {
|
|
dropgrab(cg);
|
|
/* Success */
|
|
return 0;
|
|
} else {
|
|
dropgrab(cg);
|
|
syslog (LOG_ERR, "ISDN NoProtocol2 %ld %s", minor, data);
|
|
if(conn != NULL) {
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = "Error in PushCardProt";
|
|
ReportConn(conn);
|
|
}
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis11 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
if(minor > 0) {
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
}
|
|
if(connref != 0) {
|
|
m_putsx (&xx, ARG_CONNREF);
|
|
m_puti (&xx, connref);
|
|
}
|
|
if(crd[0] != '\0') {
|
|
m_putsx(&xx,ARG_CARD);
|
|
m_putsz(&xx,crd);
|
|
}
|
|
if(callref != 0) {
|
|
m_putsx (&xx, ARG_CALLREF);
|
|
m_puti (&xx, callref);
|
|
}
|
|
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
|
|
|
|
resp = "ERROR";
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* IND_INCOMING: Incoming call processing. */
|
|
int
|
|
do_incoming(void)
|
|
{
|
|
cf cfr;
|
|
mblk_t *cinf;
|
|
conngrab cg = newgrab(NULL);
|
|
if(cg == NULL) {
|
|
resp = "OutOfMemFoo";
|
|
incomplete = 0;
|
|
goto inc_err;
|
|
}
|
|
cg->flags = F_INCOMING|F_DIALFLAGS|F_NRCOMPLETE|F_LNRCOMPLETE;
|
|
cinf = allocb(len,BPRI_LO);
|
|
if(cinf == NULL) {
|
|
resp = "OutOfMemFoo";
|
|
incomplete = 0;
|
|
goto inc_err;
|
|
}
|
|
|
|
syslog (LOG_INFO, "ISDN In %ld %s", minor, data);
|
|
bcopy (data, cinf->b_wptr, len);
|
|
cinf->b_wptr += len;
|
|
cg->par_in = cinf;
|
|
cg->card = str_enter(crd);
|
|
if ((resp = findit (&cg,0)) != NULL) {
|
|
if(incomplete && !strncmp(resp+1,"LNrIncomp",8))
|
|
resp = "waiting for number";
|
|
goto inc_err;
|
|
}
|
|
incomplete = 0;
|
|
if (quitnow) {
|
|
resp = "SHUTTING DOWN";
|
|
goto inc_err;
|
|
}
|
|
{
|
|
char *sit = NULL,*pro = NULL,*car = NULL,*cla = NULL; /* GCC */
|
|
long flg;
|
|
ulong_t sub = 0;
|
|
if(0)printf("Hunt for %s/%s/%s/%s/%o\n",cg->site,cg->protocol,cg->card,cg->cclass,cg->flags);
|
|
/* Figure out which program to run. */
|
|
for (cfr = cf_R; cfr != NULL; cfr = cfr->next) {
|
|
if(cfr->got_err) continue;
|
|
if ((flg = matchflag (cg->flags,cfr->type)) == 0) continue;
|
|
if ((sit = wildmatch (cg->site, cfr->site)) == NULL) continue;
|
|
if ((pro = wildmatch (cg->protocol, cfr->protocol)) == NULL) continue;
|
|
if ((car = wildmatch (cg->card, cfr->card)) == NULL) continue;
|
|
if ((sub = maskmatch (cg->mask, cfr->mask)) == 0) continue;
|
|
if ((cla =classmatch (cg->cclass, cfr->cclass)) == NULL) continue;
|
|
break;
|
|
}
|
|
if (cfr == NULL) { /* None. Sorry, won't do; ATA isn't implemented yet... */
|
|
resp = "NO PROGRAM";
|
|
goto inc_err;
|
|
}
|
|
cg->site = sit; cg->protocol = pro; cg->cclass = cla; cg->card = car;
|
|
cg->mask = sub;
|
|
}
|
|
if((bchan < 0) && (cg->flags & F_CHANBUSY)) {
|
|
resp = "0BUSY other";
|
|
goto inc_err;
|
|
}
|
|
if(((conn = startconn(cg,fminor,connref,&resp, NULL)) != NULL) && (resp != NULL)) {
|
|
/* An existing connection feels responsible for this. */
|
|
mblk_t *mz;
|
|
if(conn->state <= c_forceoff) {
|
|
goto cont;
|
|
} else if ((conn->connref == connref || conn->connref == 0)) {
|
|
if(*resp != '=')
|
|
goto cont;
|
|
}
|
|
|
|
mz = allocb(80,BPRI_HI); if(mz == NULL) goto cont;
|
|
|
|
if(*resp != '+') {
|
|
/* Throw away this call, there's either an old one active
|
|
or we want to call back */
|
|
if(log_34 & 2)printf("Dis1 ");
|
|
*mz->b_wptr++ = PREF_NOERR;
|
|
m_putid (mz, CMD_OFF);
|
|
m_putsx (mz, ARG_NODISC);
|
|
m_putsx (mz, ARG_FORCE);
|
|
m_putsx (mz, ARG_CAUSE);
|
|
m_putsx2(mz, ID_N1_CallRejected);
|
|
if(crd[0] != '\0') {
|
|
m_putsx(mz,ARG_CARD);
|
|
m_putsz(mz,crd);
|
|
}
|
|
if(connref != 0) {
|
|
m_putsx (mz, ARG_CONNREF);
|
|
m_puti (mz, connref);
|
|
}
|
|
if(callref != 0) {
|
|
m_putsx (mz, ARG_CALLREF);
|
|
m_puti (mz, callref);
|
|
}
|
|
if(cg != NULL)
|
|
syslog (LOG_WARNING, "DropIn '%s' for %s/%s/%s", cg->site, cg->protocol, cg->cclass, nr);
|
|
else
|
|
syslog (LOG_WARNING, "DropIn '??' for ???/%s", nr);
|
|
xlen = mz->b_wptr - mz->b_rptr;
|
|
DUMPW (mz->b_rptr, xlen);
|
|
(void) strwrite (xs_mon, mz->b_rptr, xlen, 1);
|
|
freeb(mz);
|
|
|
|
if(*resp == '=') {
|
|
setconnref(conn,connref);
|
|
conn->want_reconn = MAX_RECONN; /* Wait for confirmation before doing anything */
|
|
conn->want_fast_reconn = 1;
|
|
}
|
|
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = resp+1;
|
|
cg->refs++;
|
|
/* dropgrab(conn->cg; ** is new anyway */
|
|
conn->cg = cg;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
resp = NULL;
|
|
} else { /* Throw away the old call */
|
|
if(log_34 & 2)printf("Dis2 ");
|
|
*mz->b_wptr++ = PREF_NOERR;
|
|
m_putid (mz, CMD_OFF);
|
|
m_putsx (mz, ARG_NODISC);
|
|
m_putsx (mz, ARG_FORCE);
|
|
m_putsx (mz, ID_N0_cause);
|
|
m_putsx2(mz, ID_N1_CallRejected);
|
|
if(conn->cg != NULL && conn->cg->card != NULL && conn->cg->card[0] != '\0') {
|
|
m_putsx(mz,ARG_CARD);
|
|
m_putsz(mz,conn->cg->card);
|
|
}
|
|
if(conn->connref != 0) {
|
|
m_putsx (mz, ARG_CONNREF);
|
|
m_puti (mz, conn->connref);
|
|
}
|
|
setconnstate(conn,c_down);
|
|
setconnref(conn,connref);
|
|
if(cg != NULL)
|
|
syslog (LOG_WARNING, "DropOut '%s' for %s/%s/%s", cg->site, cg->protocol, cg->cclass, nr);
|
|
else
|
|
syslog (LOG_WARNING, "DropOut '??' for ??/??/%s", nr);
|
|
xlen = mz->b_wptr - mz->b_rptr;
|
|
DUMPW (mz->b_rptr, xlen);
|
|
(void) strwrite (xs_mon, mz->b_rptr, xlen, 1);
|
|
freeb(mz);
|
|
|
|
cg->refs++; dropgrab(conn->cg); conn->cg = cg;
|
|
ReportConn(conn);
|
|
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = resp+1;
|
|
cg->refs++;
|
|
/* dropgrab(conn->cg; ** is new anyway */
|
|
conn->cg = cg;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
resp = NULL;
|
|
#if 1
|
|
/* cg->flags &=~ F_INCOMING; */
|
|
/* cg->flags |= F_OUTGOING; */
|
|
startconn(cg,fminor,connref,&resp, NULL);
|
|
if(resp != NULL) {
|
|
printf("OhNo %s\n",resp);
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = resp+1;
|
|
cg->refs++;
|
|
/* dropgrab(conn->cg; ** is new anyway */
|
|
conn->cg = cg;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
goto cont;
|
|
resp = NULL;
|
|
} else if(conn != NULL) /* existing connection record */
|
|
goto cont;
|
|
|
|
/* At this point we don't have a connection. The call is valid, so
|
|
record the thing and start the program for it. */
|
|
conn = (struct conninfo *)gcxmalloc (sizeof (struct conninfo));
|
|
|
|
if (conn == NULL) {
|
|
resp = "NO MEMORY.5";
|
|
goto inc_err;
|
|
}
|
|
bzero (conn, sizeof (struct conninfo));
|
|
conn->seqnum = ++connseq;
|
|
|
|
cg->refs++;
|
|
/* dropgrab(conn->cg; ** is new anyway */
|
|
conn->cg = cg;
|
|
syncflags(conn,0);
|
|
setconnref(conn,connref);
|
|
conn->cause = 999999;
|
|
setconnstate(conn, c_down);
|
|
ReportConn(conn);
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
resp = runprog (cfr, &conn, &cg,'I');
|
|
if(resp != NULL)
|
|
dropconn(conn);
|
|
else
|
|
chkone(conn);
|
|
chkone(cg);
|
|
cont:
|
|
if (resp != NULL) {
|
|
inc_err:
|
|
if(!incomplete) {
|
|
xx.b_wptr = xx.b_rptr = ans;
|
|
xx.b_datap = &db;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
|
|
if(log_34 & 2)printf("Dis3 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
if(connref != 0) {
|
|
m_putsx (&xx, ARG_CONNREF);
|
|
m_puti (&xx, connref);
|
|
}
|
|
|
|
/* BUSY-if-no-channel is very ugly but unavoidable when
|
|
sharing the bus with brain-damaged devices (there are
|
|
many out there) which don't answer at all when they're busy.
|
|
Grr. The PBX should catch this case. */
|
|
/* We send the BUSY fast if _we_re busy, else we have to send it slow
|
|
because somebody else might in fact answer... */
|
|
m_putsx (&xx, ARG_CAUSE);
|
|
if((bchan < 0) || !strncmp(resp+1,"BUSY",4)) {
|
|
m_putsx2 (&xx, ID_N1_UserBusy);
|
|
if(!strcmp(resp+1,"BUSY") || (cg->flags & F_FASTDROP))
|
|
m_putsx(&xx,ARG_FASTDROP);
|
|
|
|
if(conn != NULL && (conn->flags & F_BACKCALL)) {
|
|
if(conn->want_reconn == 0)
|
|
conn->want_reconn = MAX_RECONN - (MAX_RECONN >> 1);
|
|
setconnstate(conn,conn->state);
|
|
}
|
|
} else {
|
|
if(cg->flags & F_NOREJECT)
|
|
m_putsx2 (&xx, ID_N1_NoChans);
|
|
else
|
|
m_putsx2 (&xx, ID_N1_CallRejected);
|
|
if(cg->flags & F_FASTDROP)
|
|
m_putsx(&xx,ARG_FASTDROP);
|
|
}
|
|
if(crd[0] != '\0') {
|
|
m_putsx(&xx,ARG_CARD);
|
|
m_putsz(&xx,crd);
|
|
}
|
|
if(callref != 0) {
|
|
m_putsx (&xx, ARG_CALLREF);
|
|
m_puti (&xx, callref);
|
|
}
|
|
|
|
if(cg != NULL) {
|
|
syslog (LOG_WARNING, "Got '%s' for %s/%s/%s/%s,%s", resp, cg->site, cg->protocol, cg->card, cg->cclass, nr);
|
|
} else
|
|
syslog (LOG_WARNING, "Got '%s' for ???,%s", resp, nr);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
}
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn != NULL) {
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = resp;
|
|
cg->refs++;
|
|
/* dropgrab(conn->cg); ** is new anyway */
|
|
conn->cg = cg;
|
|
run_rp(conn,'r');
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
dropconn(conn);
|
|
}
|
|
}
|
|
dropgrab(cg);
|
|
return 0;
|
|
}
|
|
|
|
/* IND_CONN: The B channel is connected, but the protocols may not be
|
|
synced yet. */
|
|
int
|
|
do_conn(void)
|
|
{
|
|
if (conn != NULL)
|
|
setconnstate(conn, c_going_up);
|
|
if(conn == NULL || (conn->flags & F_OUTGOING)) {
|
|
syslog (LOG_INFO, "ISDN Out %ld %s", minor, data);
|
|
|
|
if(minor > 0 || fminor > 0 /* conn == NULL ** || !(conn->dialin & 2) */ ) {
|
|
resp = "CARRIER";
|
|
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_FMINOR);
|
|
m_puti (&xx, minor ? minor : fminor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_AT);
|
|
m_putsz (&xx, (uchar_t *) resp);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
}
|
|
}
|
|
#if 0 /* not yet */
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_FMINOR);
|
|
m_puti (&xx, minor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_MODULE);
|
|
m_putsx (&xx, PROTO_MODULE);
|
|
m_putsz (&xx, (uchar_t *) "proto");
|
|
printf("On1\n");
|
|
m_putsx (&xx, PROTO_ONLINE);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* PROTO_ENABLE was successful -> mark a connection as DOWN. */
|
|
int
|
|
do_hasenable(void)
|
|
{
|
|
if(conn != NULL) {
|
|
if(conn->state == c_off)
|
|
setconnstate(conn, c_down);
|
|
else if(conn->state == c_offdown)
|
|
setconnstate(conn, c_going_down);
|
|
}
|
|
#if 0
|
|
if(do_run_now) {
|
|
untimeout(run_now,NULL);
|
|
run_now(NULL);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* PROTO_DISABLE was successful -> mark a connection as OFF. */
|
|
int
|
|
do_hasdisable(void)
|
|
{
|
|
if(conn != NULL) {
|
|
if(conn->state == c_down)
|
|
setconnstate(conn, c_off);
|
|
else if(conn->state == c_going_down)
|
|
setconnstate(conn, c_offdown);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
do_discrun(CState state)
|
|
{
|
|
if(conn != NULL) {
|
|
switch(conn->state) {
|
|
case c_going_up:
|
|
do_updown(state);
|
|
if((conn->flags & F_INCOMING) && !(conn->flags & F_PERMANENT)) {
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis4d ");
|
|
m_putid (&xx, CMD_CLOSE);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
m_putsx (&xx, ARG_NOCONN);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
if(conn->minor == minor) {
|
|
conn->minor = 0;
|
|
if(conn->pid == 0)
|
|
dropconn(conn);
|
|
else
|
|
kill(conn->pid,SIGHUP);
|
|
}
|
|
}
|
|
break;
|
|
case c_up:
|
|
do_down(state);
|
|
break;
|
|
case c_down:
|
|
setconnstate(conn,state);
|
|
break;
|
|
default:;
|
|
}
|
|
if (conn->pid == 0)
|
|
dropconn (conn);
|
|
else {
|
|
if(fminor != 0 && minor != fminor && minor != 0) {
|
|
syslog(LOG_ERR,"fMinor does not match -- closing (%ld/%ld)",minor,fminor);
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_CLOSE);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
if(conn->flags & F_PERMANENT)
|
|
m_putsx (&xx, ARG_NOCONN);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
if(conn->minor == minor) {
|
|
conn->minor = 0;
|
|
if(conn->pid == 0)
|
|
dropconn(conn);
|
|
else
|
|
kill(conn->pid,SIGHUP);
|
|
}
|
|
|
|
}
|
|
}
|
|
} else {
|
|
if(charge > 0)
|
|
syslog(LOG_WARNING,"COST ??:?? %ld",charge);
|
|
}
|
|
#if 0
|
|
if(minor > 0) {
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis4 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* IND_DISC: Connection broken. */
|
|
int
|
|
do_disc(void)
|
|
{
|
|
if (conn != NULL) {
|
|
if(conn->cg->nr != NULL)
|
|
conn->cg->oldnr = conn->cg->nr;
|
|
if(conn->cg->lnr != NULL)
|
|
conn->cg->oldlnr = conn->cg->lnr;
|
|
if(!conn->want_fast_reconn) {
|
|
conn->cg->nr = NULL;
|
|
conn->cg->flags &=~ F_NRCOMPLETE;
|
|
}
|
|
conn->cg->lnr = NULL;
|
|
conn->cg->flags &=~ F_LNRCOMPLETE;
|
|
conn->fminor = 0;
|
|
|
|
if(conn->cg != NULL)
|
|
syslog(LOG_INFO,"DOWN %s:%s",conn->cg->site,conn->cg->protocol);
|
|
else
|
|
syslog(LOG_INFO,"DOWN ???");
|
|
|
|
switch(conn->state) {
|
|
case c_offdown:
|
|
setconnstate(conn,c_down);
|
|
break;
|
|
case c_off:
|
|
break;
|
|
case c_going_up:
|
|
/* If the connect cost us anything, or if the limit was
|
|
reached, punt. */
|
|
(void)do_discrun(c_down);
|
|
break;
|
|
case c_up:
|
|
(void)do_discrun(c_down);
|
|
break;
|
|
case c_going_down:
|
|
setconnstate(conn,has_force ? c_off : c_down);
|
|
break;
|
|
case c_down:
|
|
setconnstate(conn,c_down);
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
#if 1
|
|
if(conn != NULL && conn->sentsetup && conn->state <= c_going_down) {
|
|
resp = "NO CARRIER";
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* IND_CLOSE: a /dev/isdn/isdnXX or /dev/ttyiXX port got closed. */
|
|
int
|
|
do_close(void)
|
|
{
|
|
if (minor == 0)
|
|
return 2;
|
|
{
|
|
if (conn != NULL) {
|
|
conn->minor = 0;
|
|
if(conn->fminor == minor)
|
|
conn->fminor = 0;
|
|
if (conn->pid == 0)
|
|
dropconn (conn);
|
|
else {
|
|
if(log_34 & 2)printf("PID still %d\n",conn->pid);
|
|
kill(conn->pid,SIGHUP);
|
|
}
|
|
}
|
|
{
|
|
struct conninfo *nconn;
|
|
for(conn = isdn4_conn; conn != NULL; conn = nconn) {
|
|
nconn = conn->next;
|
|
if(conn->minor == minor && conn->ignore >= 3) {
|
|
dropconn(conn);
|
|
continue;
|
|
}
|
|
if(conn->minor == minor && conn->ignore == 0) {
|
|
conn->minor = 0;
|
|
if(conn->fminor == minor)
|
|
conn->fminor = 0;
|
|
if (conn->pid == 0)
|
|
dropconn (conn);
|
|
else {
|
|
if(log_34 & 2)printf("PID still %d\n",conn->pid);
|
|
kill(conn->pid,SIGHUP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unlockdev(minor);
|
|
|
|
{ /* Now update our UTMP record, if we have one. */
|
|
struct utmp ut;
|
|
|
|
bzero (&ut, sizeof (ut));
|
|
strncpy (ut.ut_id, sdevname (minor), sizeof (ut.ut_id));
|
|
strncpy (ut.ut_line, mdevname (minor), sizeof (ut.ut_line));
|
|
if (getutline (&ut) != 0) {
|
|
int wf;
|
|
ut.ut_pid = getpid ();
|
|
ut.ut_type = DEAD_PROCESS;
|
|
ut.ut_time = time(NULL);
|
|
wf = open ("/etc/wtmp", O_WRONLY | O_APPEND);
|
|
|
|
if (wf >= 0) {
|
|
(void) write (wf, &ut, sizeof (ut));
|
|
close (wf);
|
|
}
|
|
pututline (&ut);
|
|
}
|
|
endutent ();
|
|
}
|
|
unlink (devname (minor));
|
|
chmod (idevname (minor), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
chown (idevname (minor), 0,0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* IND_TRACE: something happened... */
|
|
int
|
|
do_trace(void)
|
|
{
|
|
mblk_t xy;
|
|
struct datab db;
|
|
char ans[20];
|
|
char bufstr[4000], *bufi = bufstr;
|
|
int len = xx.b_wptr-xx.b_rptr;
|
|
|
|
xy.b_rptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
xy.b_datap = &db;
|
|
|
|
if(subcard == 0)
|
|
return 0;
|
|
|
|
bufi += sprintf(bufi,"%s/%ld %s",crd[0] ? crd : "????",subcard, dialin ? " in." : "out.");
|
|
|
|
*bufi++ = '<';
|
|
while (len--) {
|
|
bufi += sprintf (bufi,"%02x", *xx.b_rptr++);
|
|
if (len)
|
|
*bufi++ = (' ');
|
|
}
|
|
*bufi++ = '>';
|
|
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
struct iovec io[2];
|
|
|
|
chkone(conn);
|
|
if(conn->ignore < 4 || conn->minor == 0)
|
|
continue;
|
|
|
|
xy.b_wptr = ans;
|
|
m_putid (&xy, CMD_PROT);
|
|
m_putsx (&xy, ARG_FMINOR);
|
|
m_puti (&xy, conn->minor);
|
|
m_putdelim (&xy);
|
|
m_putid (&xy, PROTO_AT);
|
|
io[0].iov_base = xy.b_rptr;
|
|
io[0].iov_len = xy.b_wptr - xy.b_rptr;
|
|
io[1].iov_base = bufstr;
|
|
io[1].iov_len = bufi-bufstr;
|
|
DUMPW (xy.b_rptr, io[0].iov_len);
|
|
(void) strwritev (xs_mon, io, 2, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* PROTO_TELLME -- log what's happening... */
|
|
int
|
|
do_tellme(void)
|
|
{
|
|
mblk_t xy;
|
|
struct datab db;
|
|
char ans[20];
|
|
int len = xx.b_wptr-xx.b_rptr;
|
|
|
|
xy.b_rptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
xy.b_datap = &db;
|
|
|
|
if(subcard == 0)
|
|
return 0;
|
|
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
struct iovec io[3];
|
|
|
|
chkone(conn);
|
|
if(conn->ignore < 4 || conn->minor == 0)
|
|
continue;
|
|
|
|
xy.b_wptr = ans;
|
|
m_putid (&xy, CMD_PROT);
|
|
m_putsx (&xy, ARG_FMINOR);
|
|
m_puti (&xy, conn->minor);
|
|
m_putdelim (&xy);
|
|
m_putid (&xy, PROTO_AT);
|
|
io[0].iov_base = xy.b_rptr;
|
|
io[0].iov_len = xy.b_wptr - xy.b_rptr;
|
|
io[1].iov_base = xx.b_rptr;
|
|
io[1].iov_len = len;
|
|
DUMPW (xy.b_rptr, io[0].iov_len);
|
|
(void) strwritev (xs_mon, io, 2, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* IND_OPEN: a /dev/isdn/isdnXX device was openend. */
|
|
int
|
|
do_open(void)
|
|
{
|
|
if (minor == 0)
|
|
return 2;
|
|
|
|
user[fminor] = uid;
|
|
chmod (idevname (minor), S_IRUSR | S_IWUSR);
|
|
chown (idevname (minor), uid,-1);
|
|
|
|
if(conn != NULL)
|
|
return 0;
|
|
|
|
/* Configure the thing to be an AT command interpreter. */
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_FMINOR);
|
|
m_puti (&xx, minor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_MODULE);
|
|
m_putsx (&xx, PROTO_MODULE);
|
|
m_putsz (&xx, (uchar_t *) "proto");
|
|
m_putsx (&xx, PROTO_CR);
|
|
m_puti (&xx, 13);
|
|
m_putsx (&xx, PROTO_LF);
|
|
m_puti (&xx, 10);
|
|
m_putsx (&xx, PROTO_BACKSPACE);
|
|
m_puti (&xx, 8);
|
|
m_putsx (&xx, PROTO_ABORT);
|
|
m_puti (&xx, 3);
|
|
m_putsx (&xx, PROTO_CARRIER);
|
|
m_puti (&xx, 1);
|
|
m_putsx (&xx, PROTO_BREAK);
|
|
m_puti (&xx, 1);
|
|
m_putsx (&xx, ((conn != NULL) || (dialin > 0)) ? PROTO_ONLINE : PROTO_OFFLINE);
|
|
|
|
len = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, len);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, len, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Disconnecting.. */
|
|
int
|
|
do_disconnect(void)
|
|
{
|
|
#if 1
|
|
if (conn != NULL) {
|
|
switch(conn->state) {
|
|
case c_going_up:
|
|
do_updown(c_going_down);
|
|
break;
|
|
case c_up:
|
|
do_down(c_going_down);
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis5 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
#endif
|
|
resp = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/* A connection has been fully established. */
|
|
int
|
|
do_hasconnected(void)
|
|
{
|
|
if (conn != NULL) {
|
|
syncflags(conn,1);
|
|
setconnstate(conn,c_up);
|
|
if(conn->flags & F_FORCEIN) {
|
|
setconnstate(conn,c_forceoff);
|
|
do_disconnect();
|
|
resp = "NO CARRIER";
|
|
return 1;
|
|
}
|
|
}
|
|
resp = "CONNECT";
|
|
|
|
if(conn != NULL && conn->cg != NULL)
|
|
syslog(LOG_INFO,"UP %s:%s",conn->cg->site,conn->cg->protocol);
|
|
else
|
|
syslog(LOG_INFO,"UP ??:??");
|
|
|
|
if(resp != NULL) {
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_FMINOR);
|
|
m_puti (&xx, fminor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_AT);
|
|
*xx.b_wptr++ = ' ';
|
|
m_putsz (&xx, (uchar_t *) resp);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
}
|
|
|
|
/* Tell the command interpreter to be transparent. */
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_MODULE);
|
|
m_putsx (&xx, PROTO_MODULE);
|
|
m_putsz (&xx, (uchar_t *) "proto");
|
|
m_putsx (&xx, PROTO_ONLINE);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* ... disconnect complete. */
|
|
int
|
|
do_hasdisconnect(void)
|
|
{
|
|
if (minor == fminor) { /* Tell the interpreter to revert to command line. */
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_MODULE);
|
|
m_putsx (&xx, PROTO_MODULE);
|
|
m_putsz (&xx, (uchar_t *) "proto");
|
|
if(log_34 & 2)printf("On4 %p %d\n",conn,dialin);
|
|
m_putsx (&xx, ((conn != NULL) || (dialin > 0)) ? PROTO_ONLINE : PROTO_OFFLINE);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
}
|
|
#if 1
|
|
if(minor > 0) {
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis6 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
#if 0
|
|
if((conn != NULL) && (conn->flags & F_PERMANENT))
|
|
m_putsx (&xx, ARG_NOCONN);
|
|
#endif
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
}
|
|
#endif
|
|
if(conn != NULL) {
|
|
switch(conn->state) {
|
|
case c_going_up:
|
|
do_updown(c_going_down);
|
|
break;
|
|
case c_up:
|
|
do_down(c_going_down);
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
#if 0
|
|
resp = "NO CARRIER";
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* Some interface has data to transmit -> open a connection. */
|
|
int
|
|
do_wantconnect(void)
|
|
{
|
|
if (!quitnow && conn != NULL && (conn->state > c_off || (conn->state == c_off && has_force))) {
|
|
if(conn->state < c_going_up) {
|
|
if(conn->state == c_off)
|
|
setconnstate(conn,c_down);
|
|
setconnref(conn,0);
|
|
try_reconn(conn);
|
|
}
|
|
} else { /* No way right now. Disable. */
|
|
if(minor > 0) {
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis7 ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
m_putsx (&xx, ARG_NOCONN);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Somebody typed a command line. */
|
|
int
|
|
do_atcmd(void)
|
|
{
|
|
for (;;) { /* Find the AT prefix. */
|
|
while (xx.b_rptr < xx.b_wptr && *xx.b_rptr != 'a' && *xx.b_rptr != 'A')
|
|
xx.b_rptr++;
|
|
if (xx.b_rptr == xx.b_wptr)
|
|
return 0;
|
|
while (xx.b_rptr < xx.b_wptr && (*xx.b_rptr == 'a' || *xx.b_rptr == 'A'))
|
|
xx.b_rptr++;
|
|
if (xx.b_rptr < xx.b_wptr && (*xx.b_rptr == 't' || *xx.b_rptr == 'T'))
|
|
break;
|
|
}
|
|
/* AT recognized */
|
|
/* If we're AT/Listening, stop it. */
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->minor == minor && conn->ignore >= 3) {
|
|
dropconn(conn);
|
|
break;
|
|
}
|
|
}
|
|
xx.b_rptr++;
|
|
resp = "OK";
|
|
while (1) {
|
|
int dodrop;
|
|
at_again:
|
|
dodrop = 0;
|
|
m_getskip (&xx);
|
|
if (xx.b_rptr >= xx.b_wptr)
|
|
return 1;
|
|
switch (*xx.b_rptr++) {
|
|
case '/': /* AT/ */
|
|
m_getskip (&xx);
|
|
switch (*xx.b_rptr++) {
|
|
default:
|
|
return 3;
|
|
case 'k': /* AT/K#, kill one program; AT/K, kill all programs */
|
|
case 'K': /* Restart in half a minute. */
|
|
if(user[fminor] != 0 && user[fminor] != rootuser) {
|
|
resp = "NO PERMISSION";
|
|
return 1;
|
|
}
|
|
/* Kick a connection. */
|
|
if(m_geti(&xx,&minor) != 0) {
|
|
kill_progs(NULL);
|
|
} else {
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->ignore)
|
|
continue;
|
|
if(conn->minor == minor)
|
|
break;
|
|
}
|
|
if(conn != NULL) {
|
|
kill_progs(conn);
|
|
break;
|
|
}
|
|
resp = "NOT FOUND";
|
|
return 1;
|
|
}
|
|
break;
|
|
case 's': case 'S':
|
|
if(m_geti(&xx,&minor) >= 0) {
|
|
mblk_t xy;
|
|
struct datab db;
|
|
char ans[20];
|
|
|
|
xy.b_rptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
xy.b_datap = &db;
|
|
xy.b_wptr = ans;
|
|
|
|
m_putid (&xy, CMD_PROT);
|
|
m_putsx (&xy, ARG_FMINOR);
|
|
m_puti (&xy, minor);
|
|
m_putdelim (&xy);
|
|
m_putid (&xy, PROTO_TELLME);
|
|
DUMPW (xy.b_rptr, xy.b_wptr-xy.b_rptr);
|
|
(void) strwrite (xs_mon, (uchar_t *) xy.b_rptr, xy.b_wptr-xy.b_rptr, 1);
|
|
} else
|
|
minor = -1; /* flag */
|
|
goto enable_watch;
|
|
case 'r': /* AT/R */
|
|
case 'R': /* Reload database. */
|
|
if(user[fminor] != 0 && user[fminor] != rootuser) {
|
|
resp = "NO PERMISSION";
|
|
return 1;
|
|
}
|
|
read_args(NULL);
|
|
do_run_now++;
|
|
run_now(NULL);
|
|
break;
|
|
case 'q': /* AT/Q */
|
|
case 'Q': /* Shutdown the ISDN system. */
|
|
if(user[fminor] != 0 && user[fminor] != rootuser) {
|
|
resp = "NO PERMISSION";
|
|
return 1;
|
|
}
|
|
do_quitnow(NULL);
|
|
break;
|
|
case 'b': /* AT/B# */
|
|
case 'B': /* Reenable a connection. */
|
|
{
|
|
if(m_geti(&xx,&minor) != 0) {
|
|
return 3;
|
|
}
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->ignore)
|
|
continue;
|
|
if(conn->minor == minor)
|
|
break;
|
|
}
|
|
if(conn != NULL) {
|
|
if(conn->state == c_off) {
|
|
conn->retries = conn->retiming = 0;
|
|
setconnstate(conn,c_down);
|
|
goto at_again;
|
|
} else {
|
|
resp = "BAD STATE";
|
|
return 1;
|
|
}
|
|
}
|
|
resp = "NOT FOUND";
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'f': /* AT/F# */
|
|
case 'F': /* freeze the connection, i.e. state = OFF */
|
|
dodrop = 1;
|
|
/* FALL THRU */
|
|
case 'x': /* AT#X# */
|
|
case 'X': /* Drop a connection, i.e. state = DOWN */
|
|
{
|
|
if(user[fminor] != 0 && user[fminor] != rootuser) {
|
|
resp = "NO PERMISSION";
|
|
return 1;
|
|
}
|
|
if(m_geti(&xx,&minor) != 0) {
|
|
resp = "ERROR";
|
|
return 1;
|
|
}
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->ignore)
|
|
continue;
|
|
if(conn->minor == minor)
|
|
break;
|
|
}
|
|
if(conn != NULL) {
|
|
if(conn->state >= c_going_up || (dodrop && conn->state == c_down)) {
|
|
mblk_t *mb = allocb(80,BPRI_MED);
|
|
|
|
if(dodrop)
|
|
setconnstate(conn, c_forceoff);
|
|
else
|
|
setconnstate(conn, c_down);
|
|
if(log_34 & 2)printf("Dis8 ");
|
|
if(mb != NULL) {
|
|
|
|
{
|
|
*mb->b_wptr++ = PREF_NOERR;
|
|
m_putid (mb, CMD_OFF);
|
|
m_putsx (mb, ARG_MINOR);
|
|
m_puti (mb, minor);
|
|
xlen = mb->b_wptr - mb->b_rptr;
|
|
DUMPW (mb->b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) mb->b_rptr, xlen, 1);
|
|
freeb(mb);
|
|
}
|
|
goto at_again;
|
|
} else {
|
|
resp = "BAD STATE";
|
|
return 1;
|
|
}
|
|
}
|
|
resp = "NOT FOUND";
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
case 'l': /* AT/L */
|
|
case 'L': /* List connections and state changes. */
|
|
minor = 0;
|
|
enable_watch:
|
|
{
|
|
struct conninfo *fconn;
|
|
char buf[30];
|
|
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn == NULL) {
|
|
gfree(msgbuf);
|
|
resp = "NoMem";
|
|
return 1;
|
|
}
|
|
bzero(conn,sizeof(*conn));
|
|
if(!minor) {
|
|
streamchar *m1, *m2, m3;
|
|
m_getskip (&xx);
|
|
if (xx.b_rptr != xx.b_wptr) {
|
|
m1 = m2 = xx.b_rptr;
|
|
while (m2 < xx.b_wptr && *m2 != '/' && *m2 != ';') {
|
|
if (isspace (*m2))
|
|
break;
|
|
m2++;
|
|
}
|
|
if(m1 == m2)
|
|
conn->cardname = "*";
|
|
else {
|
|
m3 = *m2;
|
|
*m2 = '\0';
|
|
conn->cardname = str_enter(m1);
|
|
*m2 = m3;
|
|
xx.b_rptr = m2;
|
|
m_getskip (&xx);
|
|
}
|
|
} else
|
|
conn->cardname = "*";
|
|
} else
|
|
conn->cardname = "*";
|
|
conn->seqnum = ++connseq;
|
|
conn->ignore = (minor < 0) ? 4 : 3;
|
|
conn->minor = fminor;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
|
|
if(!minor) {
|
|
connreport("#:ref id site protocol class pid state/card cost total flags,remNr;locNr cause","*",fminor);
|
|
for(fconn = isdn4_conn; fconn != NULL; fconn = fconn->next) {
|
|
if(fconn->ignore >= 3)
|
|
continue;
|
|
if((fconn->cg != NULL) && (fconn->cg->card != NULL))
|
|
if(!wildmatch(fconn->cg->card,conn->cardname))
|
|
continue;
|
|
ReportOneConn(fconn,fminor);
|
|
}
|
|
}
|
|
if(minor > 0)
|
|
sprintf(buf,"# Dump&Wait %ld...", minor);
|
|
else
|
|
sprintf(buf,"# Waiting %s...", conn->cardname);
|
|
resp = str_enter(buf);
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case 'w': /* AT/W */
|
|
case 'W': /* Monitor D channels */
|
|
{
|
|
char buf[30];
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn == NULL) {
|
|
resp = "NoMemConn";
|
|
return 1;
|
|
}
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->ignore = 4;
|
|
conn->minor = minor;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
{
|
|
streamchar *m1, *m2, m3;
|
|
m_getskip (&xx);
|
|
if (xx.b_rptr != xx.b_wptr) {
|
|
m1 = m2 = xx.b_rptr;
|
|
while (m2 < xx.b_wptr && *m2 != '/' && *m2 != ';') {
|
|
if (isspace (*m2))
|
|
break;
|
|
m2++;
|
|
}
|
|
if(m1 == m2)
|
|
conn->cardname = "*";
|
|
else {
|
|
m3 = *m2;
|
|
*m2 = '\0';
|
|
conn->cardname = str_enter(m1);
|
|
*m2 = m3;
|
|
xx.b_rptr = m2;
|
|
m_getskip (&xx);
|
|
}
|
|
} else
|
|
conn->cardname = "*";
|
|
}
|
|
sprintf(buf,"# Monitoring %s...",conn->cardname);
|
|
resp = str_enter(buf);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'i': /* AT/I */
|
|
case 'I': /* List of active cards, and Level 3 state. */
|
|
{
|
|
char *sp;
|
|
#if LEVEL < 4
|
|
extern int l3print(char *);
|
|
#endif
|
|
msgbuf = gbxmalloc(10240);
|
|
if(msgbuf == NULL) {
|
|
resp = "NO MEMORY.6";
|
|
return 1;
|
|
}
|
|
sp = resp = msgbuf;
|
|
{
|
|
struct isdncard *card;
|
|
for(card = isdn4_card;card != NULL; card = card->next)
|
|
sp += sprintf(sp,"%s(%d) ",card->name,card->nrbchan);
|
|
*sp++ = '\r'; *sp++ = '\n';
|
|
}
|
|
#if LEVEL < 4
|
|
sp += l3print(sp);
|
|
*sp++ = '\r'; *sp++ = '\n';
|
|
#endif
|
|
sprintf(sp,"OK");
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case 'm': /* AT/M foo */
|
|
case 'M': /* send "foo" to L3. */
|
|
{
|
|
m_getskip (&xx);
|
|
if (xx.b_rptr == xx.b_wptr)
|
|
return 3;
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, xx.b_wptr, xlen, 1);
|
|
return 0;
|
|
}
|
|
break;
|
|
#if 0
|
|
case 'f':
|
|
case 'F':
|
|
{ /* Forward call. Untested; only works with 1TR6, if at all. */
|
|
mblk_t yy;
|
|
|
|
m_getskip (&xx);
|
|
yy.b_rptr = yy.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&yy, CMD_PROT);
|
|
m_putsx (&yy, ARG_FMINOR);
|
|
m_puti (&yy, minor);
|
|
if (xx.b_rptr < xx.b_wptr && isdigit (*xx.b_rptr)) {
|
|
m_putsx (&yy, ARG_EAZ);
|
|
m_puti (&yy, *xx.b_rptr++);
|
|
if (xx.b_rptr < xx.b_wptr && isdigit (*xx.b_rptr)) {
|
|
m_putsx (&yy, ARG_EAZ2);
|
|
m_puti (&yy, *xx.b_rptr++);
|
|
}
|
|
}
|
|
if (xx.b_rptr < xx.b_wptr && *xx.b_rptr == '.') {
|
|
}
|
|
xlen = yy.b_wptr - yy.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
resp = NULL;
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
case 'o': /* ATO */
|
|
case 'O': /* go online again */
|
|
{
|
|
mblk_t yy;
|
|
|
|
m_getskip (&xx);
|
|
if (xx.b_rptr < xx.b_wptr) {
|
|
resp = "ERROR";
|
|
return 1;
|
|
}
|
|
resp = "CONNECT";
|
|
|
|
yy.b_rptr = yy.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&yy, CMD_PROT);
|
|
m_putsx (&yy, ARG_FMINOR);
|
|
m_puti (&yy, minor);
|
|
m_putdelim (&yy);
|
|
m_putid (&yy, PROTO_AT);
|
|
*yy.b_wptr++ = ' ';
|
|
m_putsz (&yy, (uchar_t *) resp);
|
|
xlen = yy.b_wptr - yy.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
|
|
yy.b_rptr = yy.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
m_putid (&yy, CMD_PROT);
|
|
m_putsx (&yy, ARG_MINOR);
|
|
m_puti (&yy, minor);
|
|
m_putdelim (&yy);
|
|
m_putid (&yy, PROTO_MODULE);
|
|
m_putsx (&yy, PROTO_MODULE);
|
|
m_putsz (&yy, (uchar_t *) "proto");
|
|
if(log_34 & 2)printf("On5\n");
|
|
m_putsx (&yy, PROTO_ONLINE);
|
|
xlen = yy.b_wptr - yy.b_rptr;
|
|
DUMPW (yy.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) yy.b_rptr, xlen, 1);
|
|
resp = NULL;
|
|
}
|
|
return 0;
|
|
case 'H': /* ATH */
|
|
case 'h': /* hang up. */
|
|
{
|
|
mblk_t yy;
|
|
struct datab dbb;
|
|
|
|
yy.b_datap = &dbb;
|
|
yy.b_rptr = yy.b_wptr = ans;
|
|
dbb.db_base = ans;
|
|
dbb.db_lim = ans + sizeof (ans);
|
|
if(log_34 & 2)printf("Dis9 ");
|
|
*yy.b_wptr++ = PREF_NOERR;
|
|
m_putid (&yy, CMD_OFF);
|
|
m_putsx (&yy, ARG_MINOR);
|
|
m_puti (&yy, minor);
|
|
xlen = yy.b_wptr - yy.b_rptr;
|
|
DUMPW (yy.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) yy.b_rptr, xlen, 1);
|
|
}
|
|
break;
|
|
case 'D': /* ATD###, ATD/site, .../protocol, .../card */
|
|
case 'd': /* Dial out. */
|
|
{
|
|
/* cf cfr; */
|
|
streamchar *m1, *m2, *m3;
|
|
/* char *cclass = NULL; */
|
|
mblk_t *md = allocb (256, BPRI_MED);
|
|
conngrab cg = newgrab(NULL);
|
|
|
|
if (md == NULL || cg == NULL) {
|
|
if(md != NULL)
|
|
freeb(md);
|
|
if(cg != NULL)
|
|
dropgrab(cg);
|
|
resp = "NO MEMORY.7";
|
|
return 1;
|
|
}
|
|
if(quitnow) {
|
|
freeb(md);
|
|
dropgrab(cg);
|
|
resp = "SHUTTING DOWN";
|
|
return 1;
|
|
}
|
|
if(fminor != 0) {
|
|
m_putid (md, CMD_NOPROT);
|
|
m_putsx (md, ARG_FMINOR);
|
|
m_puti (md, fminor);
|
|
xlen=md->b_wptr-md->b_rptr;
|
|
DUMPW (md->b_rptr, xlen);
|
|
(void) strwrite (xs_mon, md->b_rptr, xlen, 1);
|
|
md->b_wptr=md->b_rptr;
|
|
}
|
|
|
|
m_getskip (&xx);
|
|
if (xx.b_rptr == xx.b_wptr) {
|
|
freemsg (md);
|
|
return 3;
|
|
}
|
|
m1 = m3 = m2 = xx.b_rptr;
|
|
if (*m1 == '/') {
|
|
m2++;
|
|
m3++;
|
|
}
|
|
while (m2 < xx.b_wptr && *m2 != '/' && *m2 != ';') {
|
|
if (*m1 == '/') { /* site name */
|
|
if (isspace (*m2))
|
|
break;
|
|
*m3++ = *m2++;
|
|
} else { /* site number */
|
|
if (isdigit (*m2))
|
|
*m3++ = *m2;
|
|
m2++;
|
|
}
|
|
}
|
|
m_getskip (&xx);
|
|
if (m2 < xx.b_wptr && *m2 == '/') {
|
|
*m3 = '\0';
|
|
m_getskip (&xx);
|
|
m3 = ++m2;
|
|
while (m3 < xx.b_wptr && !isspace (*m3) && *m3 != '/' && *m3 != ';')
|
|
m3++;
|
|
/* if (m3 < xx.b_rptr) */
|
|
if (m3 == m2)
|
|
m2 = NULL;
|
|
if (*m3 == '/') {
|
|
*m3++ = '\0';
|
|
m_getskip(&xx);
|
|
xx.b_rptr = m3+4;
|
|
} else {
|
|
*m3++ = '\0'; /* Probably unsafe */
|
|
xx.b_rptr = m3;
|
|
m3 = NULL;
|
|
}
|
|
} else {
|
|
*m2++ = '\0';
|
|
xx.b_rptr = m2;
|
|
m2 = NULL;
|
|
m3 = NULL;
|
|
}
|
|
conn = gcxmalloc(sizeof(*conn));
|
|
if(conn == NULL) {
|
|
dropgrab(cg);
|
|
resp = "NoMemConn";
|
|
return 1;
|
|
}
|
|
bzero(conn,sizeof(*conn));
|
|
conn->seqnum = ++connseq;
|
|
conn->fminor = fminor;
|
|
conn->minor = minor;
|
|
conn->next = isdn4_conn; isdn4_conn = conn;
|
|
|
|
setconnref(conn,isdn4_connref);
|
|
isdn4_connref += 2;
|
|
cg->refs++;
|
|
dropgrab(conn->cg);
|
|
conn->cg = cg;
|
|
|
|
cg->flags = F_OUTGOING|F_MULTIDIALUP|F_DIALUP;
|
|
if(m3 != NULL)
|
|
cg->card = str_enter(m3);
|
|
else
|
|
cg->card = "*";
|
|
if(m2 != NULL)
|
|
cg->protocol = str_enter(m2);
|
|
else
|
|
cg->protocol = "login";
|
|
|
|
if (*m1 == '/') {
|
|
cg->site = str_enter(m1+1);
|
|
} else {
|
|
cg->nr = str_enter(m1);
|
|
cg->flags |= F_NRCOMPLETE;
|
|
}
|
|
resp = findit (&cg,0);
|
|
if (resp != NULL) {
|
|
freeb (md);
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = isdigit(*resp) ? resp+1 : resp;
|
|
dropconn(conn);
|
|
return 1;
|
|
}
|
|
if(!(cg->flags & F_NRCOMPLETE)) {
|
|
freeb(md);
|
|
resp = "RemoteNr incomplete";
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = resp;
|
|
dropconn(conn);
|
|
return 1;
|
|
}
|
|
cg->refs++;
|
|
dropgrab(conn->cg);
|
|
conn->cg = cg;
|
|
setconnstate(conn,c_down);
|
|
conn->sentsetup = 1; /* Not really, but... */
|
|
if(startconn(cg,fminor,0,&resp, NULL) != NULL) {
|
|
freeb(md);
|
|
break;
|
|
}
|
|
freeb (md);
|
|
if(resp == NULL)
|
|
resp = "ERROR (internal)";
|
|
conn->cause = ID_priv_Print;
|
|
conn->causeInfo = isdigit(*resp) ? resp+1 : resp;
|
|
dropconn(conn);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'A': /* ATA */
|
|
case 'a': /* Sorry, no answer yet. */
|
|
return 3;
|
|
default:
|
|
return 3;
|
|
}
|
|
} /* end while */
|
|
return 0;
|
|
}
|
|
|
|
/* IND_INFO -- informational message received */
|
|
int
|
|
do_getinfo(void)
|
|
{
|
|
int DoReport = 0;
|
|
if(conn == NULL) {
|
|
/* What to do? */
|
|
return -ENXIO;
|
|
}
|
|
if(conn->charge != 0 && (conn->charge % 10) == 0)
|
|
syslog(LOG_INFO,"Cost %s:%s %ld",conn->cg->site,conn->cg->protocol,conn->charge);
|
|
if(cause != 0) {
|
|
conn->cause = cause;
|
|
DoReport = 1;
|
|
}
|
|
if((charge > 0) && (conn->state >= c_going_up)) {
|
|
/* Send a TICK messge to the protocol stack to sync timers. */
|
|
mblk_t *mb = allocb(80,BPRI_MED);
|
|
|
|
if(mb != NULL) {
|
|
m_putid (mb, CMD_PROT);
|
|
m_putsx (mb, ARG_MINOR);
|
|
m_puti (mb, minor ? minor : fminor);
|
|
m_putdelim (mb);
|
|
m_putid (mb, PROTO_TICK);
|
|
xlen = mb->b_wptr - mb->b_rptr;
|
|
DUMPW (mb->b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) mb->b_rptr, xlen, 1);
|
|
freemsg(mb);
|
|
}
|
|
setconnstate(conn,conn->state);
|
|
DoReport = 0;
|
|
}
|
|
if(DoReport)
|
|
ReportConn(conn);
|
|
return 0;
|
|
}
|
|
|
|
/* IND_ERROR. An error has occurred. Oh no... */
|
|
int
|
|
do_error(void)
|
|
{
|
|
printf("GotAnError: Minor %ld, connref %ld, hdr %s\n",minor,connref,HdrName(hdrval));
|
|
if(hdrval == HDR_CLOSE) /* Ignore if uncloseable */
|
|
return 0;
|
|
if(hdrval == HDR_LOAD) {
|
|
if(loader != NULL) {
|
|
if ((foffset >= 0) && (errnum == -EAGAIN) && (seqnum == loader->nrfile)) {
|
|
loader->foffset = foffset;
|
|
if(loader->timer)
|
|
untimeout(card_load,loader);
|
|
loader->timer = 1;
|
|
timeout(card_load,loader,HZ*5);
|
|
return 0;
|
|
}
|
|
card_load_fail(loader,(errnum < 0) ? errnum : -errnum);
|
|
}
|
|
return 0;
|
|
}
|
|
if(conn == NULL && connref != 0) {
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if(conn->connref == connref)
|
|
break;
|
|
}
|
|
}
|
|
if(conn != NULL) {
|
|
if(conn->cg != NULL) {
|
|
cf cfr;
|
|
|
|
for (cfr = cf_R; cfr != NULL; cfr = cfr->next) {
|
|
if (matchflag (conn->cg->flags,cfr->type) == 0) continue;
|
|
if (wildmatch (conn->cg->site, cfr->site) == NULL) continue;
|
|
if (wildmatch (conn->cg->protocol, cfr->protocol) == NULL) continue;
|
|
if (wildmatch (conn->cg->card, cfr->card) == NULL) continue;
|
|
if (maskmatch (conn->cg->mask, cfr->mask) == 0) continue;
|
|
if (classmatch (conn->cg->cclass, cfr->cclass) == NULL) continue;
|
|
break;
|
|
}
|
|
if(cfr != NULL) {
|
|
struct conninfo *xconn;
|
|
if(cfr->got_err)
|
|
goto conti;
|
|
if(strchr(cfr->type,'E'))
|
|
cfr->got_err = 1;
|
|
|
|
xconn = gcxmalloc(sizeof(*xconn));
|
|
if(xconn != NULL) {
|
|
bzero(xconn,sizeof(*xconn));
|
|
xconn->seqnum = ++connseq;
|
|
xconn->cause = ID_priv_Print;
|
|
xconn->causeInfo = "Program Error";
|
|
conn->cg->refs++;
|
|
/* dropgrab(conn->cg; ** is new anyway */
|
|
xconn->cg = conn->cg;
|
|
xconn->next = isdn4_conn; isdn4_conn = xconn;
|
|
dropconn(xconn);
|
|
}
|
|
}
|
|
}
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
|
|
if(log_34 & 2)printf("DisA ");
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_OFF);
|
|
m_putsx(&xx,ARG_FORCE);
|
|
if(minor > 0) {
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, minor);
|
|
}
|
|
if(connref != 0) {
|
|
m_putsx (&xx, ARG_CONNREF);
|
|
m_puti (&xx, connref);
|
|
}
|
|
if(crd[0] != '\0') {
|
|
m_putsx(&xx,ARG_CARD);
|
|
m_putsz(&xx,crd);
|
|
}
|
|
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
|
|
}
|
|
if((minor ? minor : fminor) != 0) {
|
|
char dats[30];
|
|
xx.b_rptr = xx.b_wptr = dats;
|
|
db.db_base = dats;
|
|
db.db_lim = dats + sizeof(dats);
|
|
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_CLOSE);
|
|
m_putsx (&xx, ARG_MINOR);
|
|
m_puti (&xx, (minor ? minor : fminor));
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (xx.b_rptr, xlen);
|
|
(void) strwrite (xs_mon, (uchar_t *) xx.b_rptr, xlen, 1);
|
|
if(conn != NULL && conn->minor == (minor ? minor : fminor)) {
|
|
conn->minor = 0;
|
|
if(conn->pid == 0)
|
|
dropconn(conn);
|
|
else
|
|
kill(conn->pid,SIGHUP);
|
|
}
|
|
|
|
for(conn = isdn4_conn; conn != NULL; conn = conn->next) {
|
|
if((conn->ignore >= 3) && (conn->minor == (minor ? minor : fminor))) {
|
|
dropconn(conn);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
conti:
|
|
no_error=1;
|
|
return 3;
|
|
}
|
|
|
|
|
|
/* No error. Wow... */
|
|
int
|
|
do_noerror(void)
|
|
{
|
|
if(hdrval == HDR_CLOSE) /* Ignore if uncloseable */
|
|
return 0;
|
|
if((hdrval == HDR_LOAD) && (loader != NULL)) {
|
|
if(thelength == 0) { /* End of file. Do now if there's no timeout set. */
|
|
if(!loader->timer)
|
|
card_load(loader);
|
|
} else { /* Continue file. Kick now. */
|
|
if(loader->timer)
|
|
untimeout(card_load,loader);
|
|
card_load(loader);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Main code of do_info() follows. Finally... */
|
|
int ret;
|
|
|
|
if(init_vars())
|
|
return;
|
|
while (m_getsx (&xx, &id) == 0) {
|
|
switch(parse_arg()) {
|
|
case 0: break;
|
|
case 1: goto print;
|
|
case 2: goto err;
|
|
default:goto error;
|
|
}
|
|
}
|
|
find_conn();
|
|
if(ind != IND_TRACE)
|
|
(char *) xx.b_rptr = (char *) data;
|
|
redo:
|
|
if (conn != NULL && conn->cg != NULL) {
|
|
if (conn->cg->nr != NULL && nr[0] == '\0')
|
|
strcpy(nr, conn->cg->nr);
|
|
}
|
|
chkall();
|
|
switch (ind) {
|
|
case IND_CARD: ret = do_card(); break;
|
|
case IND_RECARD: ret = do_recard(); break;
|
|
case IND_OFFCARD: ret = do_offcard(); break;
|
|
case IND_NOCARD: ret = do_nocard(); break;
|
|
case IND_CARDPROTO: ret = do_cardproto(); break;
|
|
case IND_INCOMING: ret = do_incoming(); break;
|
|
case IND_CONN: ret = do_conn(); break;
|
|
case PROTO_HAS_ENABLE: ret = do_hasenable(); break;
|
|
case PROTO_HAS_DISABLE: ret = do_hasdisable(); break;
|
|
case ID_N1_INFO: ret = do_getinfo(); break;
|
|
case ID_ET_FAC: ret = do_getinfo(); break;
|
|
case IND_DISCONNECT: ret = do_disc(); break;
|
|
case IND_DISCONNECTING: ret = do_discrun(c_going_down); break;
|
|
case IND_CLOSE: ret = do_close(); break;
|
|
case IND_OPEN: ret = do_open(); break;
|
|
case IND_TRACE: ret = do_trace(); break;
|
|
case PROTO_TELLME: ret = do_tellme(); break;
|
|
case PROTO_HAS_CONNECTED: ret = do_hasconnected(); break;
|
|
case PROTO_DISCONNECT: ret = do_disconnect(); break;
|
|
case PROTO_HAS_DISCONNECT: ret = do_hasdisconnect(); break;
|
|
case PROTO_WANT_CONNECTED: ret = do_wantconnect(); break;
|
|
case PROTO_AT: ret = do_atcmd(); break;
|
|
case IND_ERR: ret = do_error(); break;
|
|
case IND_NOERR: ret = do_noerror(); break;
|
|
|
|
case IND_INFO: /* Skip the INFO thing and parse what's beneach. */
|
|
if (m_getid (&xx, &ind) != 0)
|
|
goto err;
|
|
goto redo;
|
|
case ID_N1_FAC_ACK: /* Something or other... */
|
|
resp = "OK";
|
|
goto print;
|
|
case ID_N1_FAC_REJ:
|
|
resp = "ERROR";
|
|
goto print;
|
|
case ID_N1_ALERT: /* Hey, there is somebody at the other end! */
|
|
resp = "RRING";
|
|
goto print;
|
|
#if 0
|
|
case IND_NEEDSETUP:
|
|
{
|
|
} break;
|
|
case IND_EXPAND:
|
|
{
|
|
} break;
|
|
#endif
|
|
default:
|
|
err:
|
|
chkall();
|
|
return;
|
|
/* ok: */
|
|
resp = "OK";
|
|
goto print;
|
|
error:
|
|
resp = "ERROR";
|
|
goto print;
|
|
print:
|
|
chkall();
|
|
if (fminor == 0)
|
|
fminor = minor;
|
|
if (((fminor != minor) || !no_error) && resp != NULL && fminor > 0) {
|
|
xx.b_rptr = xx.b_wptr = ans;
|
|
db.db_base = ans;
|
|
db.db_lim = ans + sizeof (ans);
|
|
|
|
if(no_error)
|
|
*xx.b_wptr++ = PREF_NOERR;
|
|
m_putid (&xx, CMD_PROT);
|
|
m_putsx (&xx, ARG_FMINOR);
|
|
m_puti (&xx, fminor);
|
|
m_putdelim (&xx);
|
|
m_putid (&xx, PROTO_AT);
|
|
*xx.b_wptr++ = ' ';
|
|
m_putsz (&xx, (uchar_t *) resp);
|
|
xlen = xx.b_wptr - xx.b_rptr;
|
|
DUMPW (ans, xlen);
|
|
(void) strwrite (xs_mon, ans, xlen, 1);
|
|
}
|
|
if(msgbuf != NULL && resp == msgbuf)
|
|
gfree(msgbuf);
|
|
return;
|
|
}
|
|
if(ret < 0 || ret == 2)
|
|
goto err;
|
|
else if(ret == 1)
|
|
goto print;
|
|
else if(ret == 3)
|
|
goto error;
|
|
}
|