u-isdn/isdn_4/info.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;
}