Add service maintenance message support
authorJeff Peeler <jpeeler@digium.com>
Tue, 14 Apr 2009 15:54:16 +0000 (15:54 +0000)
committerJeff Peeler <jpeeler@digium.com>
Tue, 14 Apr 2009 15:54:16 +0000 (15:54 +0000)
This is the companion commit to libpri r732. Service messages are now supported
for switch types 4ess/5ess. A new option service_message_support has been added
to chan_dahdi.conf and is noted in the sample config file. The service message
support is turned off by default. The current implementation relies on AstDB
to keep track of channel state, which allows the statuses to be preserved
across Asterisk restarts. Below is a description of the storage format.

The state and reason for the service state are in the form <state>:<reason>,
where:
<state> ::= { 'O' }  // 'O' – Out Of Service
<reason> ::= { '0' | '1' | '2' | '3' }, where:
'0' – No reason (backwards compatibility)
'1' – NEAR END
'2' – FAR END
'3' – both NEAR and FAR END

The new CLI commands to handle channel service state are:
pri service disable channel <chan>
pri service enable channel <chan>

Many people contributed to the development of this functionality. Because I
entered at the very end I do not know the exact history. Special thanks to
all who moved the bug forward one way or another:
cmaj, PCadach, markster, mattf, drmac, MikeJ, serge-v, murf, kanelbullar, Seb7,
tilghman, lmadsen, and especially dhubbard (he answered lots of my questions
and did a large portion of the work)

(closes issue #3450)
Reported by: cmaj

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@188342 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/chan_dahdi.c
configs/chan_dahdi.conf.sample
configure
configure.ac
include/asterisk/autoconfig.h.in

diff --git a/CHANGES b/CHANGES
index 1c7e5c9..71e325b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -11,6 +11,8 @@
 --- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3  -------------
 ------------------------------------------------------------------------------
 
+
+
 SIP Changes
 -----------
  * Added preferred_codec_only option in sip.conf. This feature limits the joint
@@ -142,6 +144,7 @@ DAHDI Changes
    and a 'full' buffer policy for a fax transmission, add:
      faxbuffers=>6,full
    The faxbuffers configuration will be in affect until the call is torn down.
+ * Added service message support for 4ess/5ess switches.
 
 Dialplan Functions
 ------------------
index f6d3fb9..adc7e51 100644 (file)
@@ -292,6 +292,28 @@ static const char config[] = "chan_dahdi.conf";
 #define CALLPROGRESS_FAX_INCOMING      4
 #define CALLPROGRESS_FAX               (CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING)
 
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+/*! \brief Persistent Service State */
+#define SRVST_DBKEY "service-state"
+/*! \brief The out-of-service SERVICE state */
+#define SRVST_TYPE_OOS "O"
+/*! \brief SRVST_INITIALIZED is used to indicate a channel being out-of-service 
+ *  The SRVST_INITIALIZED is mostly used maintain backwards compatibility but also may
+ *  mean that the channel has not yet received a RESTART message.  If a channel is
+ *  out-of-service with this reason a RESTART message will result in the channel
+ *  being put into service. */
+#define SRVST_INITIALIZED 0
+/*! \brief SRVST_NEAREND is used to indicate that the near end was put out-of-service */
+#define SRVST_NEAREND  (1 << 0)
+/*! \brief SRVST_FAREND is used to indicate that the far end was taken out-of-service */
+#define SRVST_FAREND   (1 << 1)
+/*! \brief SRVST_BOTH is used to indicate that both sides of the channel are out-of-service */
+#define SRVST_BOTH (SRVST_NEAREND | SRVST_FAREND)
+
+/*! \brief The AstDB family */
+static const char dahdi_db[] = "dahdi/registry";
+#endif
+
 static char defaultcic[64] = "";
 static char defaultozz[64] = "";
 
@@ -543,6 +565,9 @@ struct dahdi_pri {
        int resetting;
        /*! \brief Current position during a reset (-1 if not started) */
        int resetpos;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+       unsigned int enable_service_message_support:1;  /*!< enable SERVICE message support */
+#endif
 #ifdef HAVE_PRI_INBANDDISCONNECT
        unsigned int inbanddisconnect:1;                                /*!< Should we support inband audio after receiving DISCONNECT? */
 #endif
@@ -3934,6 +3959,21 @@ static void destroy_all_channels(void)
                pl = p;
                p = p->next;
                x = pl->channel;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+               {
+                       char db_chan_name[20], db_answer[5], state;
+                       int why = -1;
+
+                       snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pl->span, x);
+                       if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                               sscanf(db_answer, "%c:%d", &state, &why);
+                       }
+                       if (!why) {
+                               /* SRVST persistence is not required */
+                               ast_db_del(db_chan_name, SRVST_DBKEY);
+                       }
+               }
+#endif
                /* Free associated memory */
                if (pl)
                        destroy_dahdi_pvt(&pl);
@@ -10134,7 +10174,6 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                                        }
                                                }
                                        }
-                                       offset = p.chanpos;
                                        if (!matchesdchan) {
                                                if (pris[span].nodetype && (pris[span].nodetype != conf->pri.nodetype)) {
                                                        ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype));
@@ -10190,6 +10229,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                                pris[span].overlapdial = conf->pri.overlapdial;
                                                pris[span].qsigchannelmapping = conf->pri.qsigchannelmapping;
                                                pris[span].discardremoteholdretrieval = conf->pri.discardremoteholdretrieval;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                                               pris[span].enable_service_message_support = conf->pri.enable_service_message_support;
+#endif
 #ifdef HAVE_PRI_INBANDDISCONNECT
                                                pris[span].inbanddisconnect = conf->pri.inbanddisconnect;
 #endif
@@ -10204,7 +10246,11 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                                pris[span].resetinterval = conf->pri.resetinterval;
 
                                                tmp->pri = &pris[span];
-                                               tmp->prioffset = offset;
+                                               if (si.spanno != span + 1) { /* in another trunkgroup */
+                                                       tmp->prioffset = pris[span].numchans;
+                                               } else {
+                                                       tmp->prioffset = p.chanpos;
+                                               }
                                                tmp->call = NULL;
                                        } else {
                                                ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", offset);
@@ -10483,10 +10529,23 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                tmp->sendcalleridafter = conf->chan.sendcalleridafter;
                if (!here) {
                        tmp->locallyblocked = tmp->remotelyblocked = 0;
-                       if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_SS7))
+                       if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_SS7)) {
                                tmp->inservice = 0;
-                       else /* We default to in service on protocols that don't have a reset */
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                               if (chan_sig == SIG_PRI) {
+                                       char db_chan_name[20], db_answer[5];
+
+                                       snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, tmp->span, tmp->channel);
+                                       if (ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                                               snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, SRVST_INITIALIZED);
+                                               ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+                                       }
+                               }
+#endif
+                       } else { 
+                                /* We default to in service on protocols that don't have a reset */
                                tmp->inservice = 1;
+                       }
                }
        }
        if (tmp && !here) {
@@ -10536,7 +10595,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
        return tmp;
 }
 
-static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched)
+static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *reason, int *channelmatched, int *groupmatched)
 {
        int res;
        struct dahdi_params par;
@@ -10554,9 +10613,9 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
                *channelmatched = 1;
        }
        /* We're at least busy at this point */
-       if (busy) {
+       if (reason) {
                if ((p->sig == SIG_FXOKS) || (p->sig == SIG_FXOLS) || (p->sig == SIG_FXOGS))
-                       *busy = 1;
+                       *reason = AST_CAUSE_BUSY;
        }
        /* If do not disturb, definitely not */
        if (p->dnd)
@@ -10573,10 +10632,25 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
 #ifdef HAVE_PRI
                /* Trust PRI */
                if (p->pri) {
-                       if (p->resetting || p->call)
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                       char db_chan_name[20], db_answer[5], state;
+                       int why = 0;
+                                               
+                       snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, p->span, p->channel);
+                       if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                               sscanf(db_answer, "%c:%d", &state, &why);
+                       }
+                       if ((p->resetting || p->call) || (why)) {
+                               if (why) {
+                                       *reason = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+                               }
+#else
+                       if (p->resetting || p->call) {
+#endif
                                return 0;
-                       else
+                       } else {
                                return 1;
+                       }
                }
 #endif
 #ifdef HAVE_SS7
@@ -10736,7 +10810,7 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat
        int channelmatch = -1;
        int roundrobin = 0;
        int callwait = 0;
-       int busy = 0;
+       int unavailreason = 0;
        struct dahdi_pvt *p;
        struct ast_channel *tmp = NULL;
        char *dest=NULL;
@@ -10865,7 +10939,7 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat
                ast_verbose("name = %s, %d, %d, %d\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch);
 #endif
 
-               if (p && available(p, channelmatch, groupmatch, &busy, &channelmatched, &groupmatched)) {
+               if (p && available(p, channelmatch, groupmatch, &unavailreason, &channelmatched, &groupmatched)) {
                        ast_debug(1, "Using channel %d\n", p->channel);
                        if (p->inalarm)
                                goto next;
@@ -10973,10 +11047,10 @@ next:
                *cause = AST_CAUSE_BUSY;
        else if (!tmp) {
                if (channelmatched) {
-                       if (busy)
+                       if (unavailreason)
                                *cause = AST_CAUSE_BUSY;
                } else if (groupmatched) {
-                       *cause = AST_CAUSE_CONGESTION;
+                       *cause = (unavailreason) ? unavailreason : AST_CAUSE_CONGESTION;
                }
        }
 
@@ -12181,6 +12255,7 @@ static void dahdi_pri_error(struct pri *pri, char *s)
 #if defined(HAVE_PRI)
 static int pri_check_restart(struct dahdi_pri *pri)
 {
+tryanotherpos:
        do {
                pri->resetpos++;
        } while ((pri->resetpos < pri->numchans) &&
@@ -12188,6 +12263,24 @@ static int pri_check_restart(struct dahdi_pri *pri)
                pri->pvts[pri->resetpos]->call ||
                pri->pvts[pri->resetpos]->resetting));
        if (pri->resetpos < pri->numchans) {
+               char db_chan_name[20], db_answer[5], state;
+               int why;
+
+               /* check if the channel is out of service */
+               ast_mutex_lock(&pri->pvts[pri->resetpos]->lock);
+               snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[pri->resetpos]->span, pri->pvts[pri->resetpos]->channel);
+               ast_mutex_unlock(&pri->pvts[pri->resetpos]->lock);
+
+               /* if so, try next channel */
+               if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                       sscanf(db_answer, "%c:%d", &state, &why);
+                       if (why) {
+                               ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), not sending RESTART\n", pri->span,
+                               pri->pvts[pri->resetpos]->channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end");
+                               goto tryanotherpos;
+                       }
+               }
+
                /* Mark the channel as resetting and restart it */
                pri->pvts[pri->resetpos]->resetting = 1;
                pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos]));
@@ -12566,13 +12659,36 @@ static void *pri_dchannel(void *vpri)
                                                ast_log(LOG_WARNING, "Restart requested on odd/unavailable channel number %d/%d on span %d\n",
                                                        PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
                                        else {
-                                               ast_verb(3, "B-channel %d/%d restarted on span %d\n",
-                                                               PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                                               char db_chan_name[20], db_answer[5], state;
+                                               int why, skipit = 0;
+                                               
                                                ast_mutex_lock(&pri->pvts[chanpos]->lock);
-                                               if (pri->pvts[chanpos]->call) {
-                                                       pri_destroycall(pri->pri, pri->pvts[chanpos]->call);
-                                                       pri->pvts[chanpos]->call = NULL;
+                                               snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[chanpos]->span, pri->pvts[chanpos]->channel);
+                                               ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+
+                                               if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                                                       sscanf(db_answer, "%c:%d", &state, &why);
+                                                       if (why) {
+                                                               ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), ignoring RESTART\n", pri->span,
+                                                                       e->restart.channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end");
+                                                               skipit = 1;
+                                                       } else {
+                                                               ast_db_del(db_chan_name, SRVST_DBKEY);
+                                                       }
+                                               }
+                                               if (!skipit) {
+#endif
+                                                       ast_verb(3, "B-channel %d/%d restarted on span %d\n",
+                                                                       PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
+                                                       ast_mutex_lock(&pri->pvts[chanpos]->lock);
+                                                       if (pri->pvts[chanpos]->call) {
+                                                               pri_destroycall(pri->pri, pri->pvts[chanpos]->call);
+                                                               pri->pvts[chanpos]->call = NULL;
+                                                       }
+#ifdef HAVE_PRI_SERVICE_MESSAGES
                                                }
+#endif
                                                /* Force soft hangup if appropriate */
                                                if (pri->pvts[chanpos]->realcall)
                                                        pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
@@ -12652,6 +12768,62 @@ static void *pri_dchannel(void *vpri)
                                        }
                                }
                                break;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                       case PRI_EVENT_SERVICE:
+                               chanpos = pri_find_principle(pri, e->service.channel);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "Received service change status %d on unconfigured channel %d/%d span %d\n",
+                                               e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span);
+                               } else {
+                                       char db_chan_name[20], db_answer[5], state;
+                                       int ch, why = -1;
+
+                                       ast_mutex_lock(&pri->pvts[chanpos]->lock);
+                                       ch = pri->pvts[chanpos]->channel;
+                                       ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+                                       
+                                       snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[chanpos]->span, ch);
+                                       if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                                               sscanf(db_answer, "%c:%d", &state, &why);
+                                               ast_db_del(db_chan_name, SRVST_DBKEY);
+                                       }
+                                       switch (e->service.changestatus) {
+                                       case 0: /* in-service */
+                                               if (why > -1) {
+                                                       if (why & SRVST_NEAREND) {
+                                                               snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, SRVST_NEAREND);
+                                                               ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+                                                               ast_debug(2, "channel '%d' service state { near: out-of-service,  far: in-service }\n", ch);
+                                                       }
+                                               }
+                                               break;
+                                       case 2: /* out-of-service */
+                                               if (why == -1) {
+                                                       why = SRVST_FAREND;
+                                               } else {
+                                                       why |= SRVST_FAREND;
+                                               }
+                                               snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why);
+                                               ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+                                               break;
+                                       default:
+                                               ast_log(LOG_ERROR, "Huh?  changestatus is: %d\n", e->service.changestatus);
+                                       }
+                                       ast_log(LOG_NOTICE, "Channel %d/%d span %d (logical: %d) received a change of service message, status '%d'\n",
+                                               PRI_SPAN(e->service.channel), PRI_CHANNEL(e->service.channel), pri->span, ch, e->service.changestatus);
+                               }
+                               break;
+                       case PRI_EVENT_SERVICE_ACK:
+                               chanpos = pri_find_principle(pri, e->service_ack.channel);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "Received service acknowledge change status '%d' on unconfigured channel %d/%d span %d\n",
+                                               e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span);
+                               } else {
+                                       ast_debug(2, "Channel %d/%d span %d received a change os service acknowledgement message, status '%d'\n",
+                                               PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span, e->service_ack.changestatus);
+                               }
+                               break;
+#endif
                        case PRI_EVENT_RING:
                                crv = NULL;
                                if (e->ring.channel == -1)
@@ -13448,6 +13620,11 @@ static int start_pri(struct dahdi_pri *pri)
                        break;
                default:
                        pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                               if (pri->enable_service_message_support) {
+                                       pri_set_service_message_support(pri->dchans[i], 1);
+                               }
+#endif
                        break;
                }
                /* Force overlap dial if we're doing GR-303! */
@@ -13622,6 +13799,149 @@ static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 }
 #endif /* defined(HAVE_PRI) */
 
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a, int changestatus)
+{
+       int why;
+       int channel;
+       int trunkgroup;
+       int x, y, fd = a->fd;
+       int interfaceid = 0;
+       char *c;
+       char state;
+       char db_chan_name[20], db_answer[5];
+       struct dahdi_pvt *start, *tmp = NULL;
+       struct dahdi_pri *pri = NULL;
+       ast_mutex_t *lock;
+       
+       lock = &iflock;
+       start = iflist;
+
+       if (a->argc < 5 || a->argc > 6)
+               return CLI_SHOWUSAGE;
+       if ((c = strchr(a->argv[4], ':'))) {
+               if (sscanf(a->argv[4], "%d:%d", &trunkgroup, &channel) != 2)
+                       return CLI_SHOWUSAGE;
+               if ((trunkgroup < 1) || (channel < 1))
+                       return CLI_SHOWUSAGE;
+               for (x=0;x<NUM_SPANS;x++) {
+                       if (pris[x].trunkgroup == trunkgroup) {
+                               pri = pris + x;
+                               break;
+                       }
+               }
+               if (pri) {
+                       start = pri->crvs;
+                       lock = &pri->lock;
+               } else {
+                       ast_cli(fd, "No such trunk group %d\n", trunkgroup);
+                       return CLI_FAILURE;
+               }
+       } else
+               channel = atoi(a->argv[4]);
+       
+       if (a->argc == 6)
+               interfaceid = atoi(a->argv[5]);
+
+       /* either servicing a D-Channel */
+       for (x = 0; x < NUM_SPANS; x++) {
+               for (y = 0; y < NUM_DCHANS; y++) {
+                       if (pris[x].dchannels[y] == channel) {
+                               pri = pris + x;
+                               pri_maintenance_service(pri->pri, interfaceid, -1, changestatus);
+                               return CLI_SUCCESS;
+                       }
+               }
+       }
+
+       /* or servicing a B-Channel */
+       ast_mutex_lock(lock);
+       tmp = start;
+       while (tmp) {
+               if (tmp->pri && tmp->channel == channel) {
+                       if (!tmp->pri->enable_service_message_support) {
+                               ast_cli(fd, "\n\tThis operation has not been enabled in chan_dahdi.conf, set 'service_message_support=yes' to use this operation.\n\tNote only 4ess and 5ess switch types are supported.\n\n");
+                               return CLI_SUCCESS;
+                       }
+                       why = -1;
+                       snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, tmp->span, channel);
+                       if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+                               sscanf(db_answer, "%c:%d", &state, &why);
+                               ast_db_del(db_chan_name, SRVST_DBKEY);
+                       }
+                       switch(changestatus) {
+                       case 0: /* enable */
+                               if (why > -1) {
+                                       if (why & SRVST_FAREND) {
+                                               why = SRVST_FAREND;
+                                               snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why);
+                                               ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+                                               ast_debug(2, "channel '%d' service state { near: in-service,  far: out-of-service }\n", channel);
+                                       }
+                               }
+                               break;
+                       /* case 1:  -- loop */
+                       case 2: /* disable */
+                               if (why == -1) {
+                                       why = SRVST_NEAREND;
+                               } else {
+                                       why |= SRVST_NEAREND;
+                               }
+                               snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why);
+                               ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+                               break;
+                       /* case 3:  -- continuity */
+                       /* case 4:  -- shutdown */
+                       default:
+                               ast_log(LOG_WARNING, "Unsupported changestatus: '%d'\n", changestatus);
+                       }
+                       pri_maintenance_service(tmp->pri->pri, PRI_SPAN(PVT_TO_CHANNEL(tmp)), PVT_TO_CHANNEL(tmp), changestatus);
+                       ast_mutex_unlock(lock);
+                       return CLI_SUCCESS;
+               }
+               tmp = tmp->next;
+       }
+       ast_mutex_unlock(lock);
+
+       ast_cli(fd, "Unable to find given channel %d, possibly not a PRI\n", channel);
+       return CLI_FAILURE;
+}
+
+static char *handle_pri_service_enable_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:  
+               e->command = "pri service enable channel";
+               e->usage = 
+                       "Usage: pri service enable channel <channel> [<interface id>]\n"
+                       "       Send an AT&T / NFAS / CCS ANSI T1.607 maintenance message\n"
+                       "       to restore a channel to service, with optional interface id\n"
+                       "       as agreed upon with remote switch operator\n";
+               return NULL;
+       case CLI_GENERATE:      
+               return NULL;
+       }
+       return handle_pri_service_generic(e, cmd, a, 0);
+}
+
+static char *handle_pri_service_disable_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:  
+               e->command = "pri service disable channel";
+               e->usage = 
+                       "Usage: pri service disable channel <chan num> [<interface id>]\n"
+                       "       Send an AT&T / NFAS / CCS ANSI T1.607 maintenance message\n"
+                       "       to remove a channel from service, with optional interface id\n"
+                       "       as agreed upon with remote switch operator\n";
+               return NULL;
+       case CLI_GENERATE:      
+               return NULL;
+       }
+       return handle_pri_service_generic(e, cmd, a, 2);
+}
+#endif
+
 #if defined(HAVE_PRI)
 static void build_status(char *s, size_t len, int status, int active)
 {
@@ -13798,6 +14118,10 @@ static char *handle_pri_version(struct ast_cli_entry *e, int cmd, struct ast_cli
 #if defined(HAVE_PRI)
 static struct ast_cli_entry dahdi_pri_cli[] = {
        AST_CLI_DEFINE(handle_pri_debug, "Enables PRI debugging on a span"),
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+       AST_CLI_DEFINE(handle_pri_service_enable_channel, "Return a channel to service"),
+       AST_CLI_DEFINE(handle_pri_service_disable_channel, "Remove a channel from service"),
+#endif
        AST_CLI_DEFINE(handle_pri_show_spans, "Displays PRI Information"),
        AST_CLI_DEFINE(handle_pri_show_span, "Displays PRI Information"),
        AST_CLI_DEFINE(handle_pri_show_debug, "Displays current PRI debug settings"),
@@ -16581,6 +16905,14 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 #endif
                        } else if (!strcasecmp(v->name, "discardremoteholdretrieval")) {
                                confp->pri.discardremoteholdretrieval = ast_true(v->value);
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+                       } else if (!strcasecmp(v->name, "service_message_support")) {
+                               /* assuming switchtype for this channel group has been configured already */
+                               if ((confp->pri.switchtype == PRI_SWITCH_ATT4ESS || confp->pri.switchtype == PRI_SWITCH_LUCENT5E) && ast_true(v->value))
+                                       confp->pri.enable_service_message_support = 1;
+                               else
+                                       confp->pri.enable_service_message_support = 0;
+#endif
 #ifdef HAVE_PRI_INBANDDISCONNECT
                        } else if (!strcasecmp(v->name, "inbanddisconnect")) {
                                confp->pri.inbanddisconnect = ast_true(v->value);
index c3219f3..c0c9cfe 100644 (file)
@@ -74,6 +74,9 @@
 ;
 ;nsf=none
 ;
+;service_message_support=yes
+; Enable service message support for channel. Must be set after switchtype.
+;
 ; PRI Dialplan: The ISDN-level Type Of Number (TON) or numbering plan, used for
 ; the dialed number.  For most installations, leaving this as 'unknown' (the
 ; default) works in the most cases.  In some very unusual circumstances, you
index 0725b29..771a115 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 182847 .
+# From configure.ac Revision: 183242 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.61 for asterisk 1.6.
 #
 
 
 
+if test "x${PBX_PRI_SERVICE_MESSAGES}" != "x1" -a "${USE_PRI_SERVICE_MESSAGES}" != "no"; then
+   pbxlibdir=""
+   # if --with-PRI_SERVICE_MESSAGES=DIR has been specified, use it.
+   if test "x${PRI_SERVICE_MESSAGES_DIR}" != "x"; then
+      if test -d ${PRI_SERVICE_MESSAGES_DIR}/lib; then
+        pbxlibdir="-L${PRI_SERVICE_MESSAGES_DIR}/lib"
+      else
+        pbxlibdir="-L${PRI_SERVICE_MESSAGES_DIR}"
+      fi
+   fi
+   pbxfuncname="pri_maintenance_service"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_PRI_SERVICE_MESSAGES_FOUND=yes
+   else
+      as_ac_Lib=`echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lpri" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lpri... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpri ${pbxlibdir}  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_Lib=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+              { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+  AST_PRI_SERVICE_MESSAGES_FOUND=yes
+else
+  AST_PRI_SERVICE_MESSAGES_FOUND=no
+fi
+
+   fi
+
+   # now check for the header.
+   if test "${AST_PRI_SERVICE_MESSAGES_FOUND}" = "yes"; then
+      PRI_SERVICE_MESSAGES_LIB="${pbxlibdir} -lpri "
+      # if --with-PRI_SERVICE_MESSAGES=DIR has been specified, use it.
+      if test "x${PRI_SERVICE_MESSAGES_DIR}" != "x"; then
+        PRI_SERVICE_MESSAGES_INCLUDE="-I${PRI_SERVICE_MESSAGES_DIR}/include"
+      fi
+      PRI_SERVICE_MESSAGES_INCLUDE="${PRI_SERVICE_MESSAGES_INCLUDE} "
+      if test "xlibpri.h" = "x" ; then # no header, assume found
+         PRI_SERVICE_MESSAGES_HEADER_FOUND="1"
+      else                             # check for the header
+         saved_cppflags="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${PRI_SERVICE_MESSAGES_INCLUDE}"
+        if test "${ac_cv_header_libpri_h+set}" = set; then
+  { echo "$as_me:$LINENO: checking for libpri.h" >&5
+echo $ECHO_N "checking for libpri.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+echo "${ECHO_T}$ac_cv_header_libpri_h" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking libpri.h usability" >&5
+echo $ECHO_N "checking libpri.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <libpri.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking libpri.h presence" >&5
+echo $ECHO_N "checking libpri.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <libpri.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: libpri.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: libpri.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: libpri.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: libpri.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: libpri.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: libpri.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: libpri.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: libpri.h: in the future, the compiler will take precedence" >&2;}
+    ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for libpri.h" >&5
+echo $ECHO_N "checking for libpri.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_libpri_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+echo "${ECHO_T}$ac_cv_header_libpri_h" >&6; }
+
+fi
+if test $ac_cv_header_libpri_h = yes; then
+  PRI_SERVICE_MESSAGES_HEADER_FOUND=1
+else
+  PRI_SERVICE_MESSAGES_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${saved_cppflags}"
+      fi
+      if test "x${PRI_SERVICE_MESSAGES_HEADER_FOUND}" = "x0" ; then
+         PRI_SERVICE_MESSAGES_LIB=""
+         PRI_SERVICE_MESSAGES_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then                # only checking headers -> no library
+           PRI_SERVICE_MESSAGES_LIB=""
+        fi
+         PBX_PRI_SERVICE_MESSAGES=1
+         # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI_SERVICE_MESSAGES 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI_SERVICE_MESSAGES_VERSION
+_ACEOF
+
+      fi
+   fi
+fi
+
+
+
 if test "x${PBX_RESAMPLE}" != "x1" -a "${USE_RESAMPLE}" != "no"; then
    pbxlibdir=""
    # if --with-RESAMPLE=DIR has been specified, use it.
index ac129ac..00220d2 100644 (file)
@@ -1404,6 +1404,8 @@ AST_EXT_LIB_CHECK([PRI_PROG_W_CAUSE], [pri], [pri_progress_with_cause], [libpri.
 
 AST_EXT_LIB_CHECK([PRI_INBANDDISCONNECT], [pri], [pri_set_inbanddisconnect], [libpri.h])
 
+AST_EXT_LIB_CHECK([PRI_SERVICE_MESSAGES], [pri], [pri_maintenance_service], [libpri.h])
+
 AST_EXT_LIB_CHECK([RESAMPLE], [resample], [resample_open], [libresample.h], [-lm])
 
 AST_C_COMPILE_CHECK([SPANDSP], [
index dc95c83..dba007d 100644 (file)
 /* Define to indicate the ${PRI_PROG_W_CAUSE_DESCRIP} library version */
 #undef HAVE_PRI_PROG_W_CAUSE_VERSION
 
+/* Define this to indicate the ${PRI_SERVICE_MESSAGES_DESCRIP} library */
+#undef HAVE_PRI_SERVICE_MESSAGES
+
+/* Define to indicate the ${PRI_SERVICE_MESSAGES_DESCRIP} library version */
+#undef HAVE_PRI_SERVICE_MESSAGES_VERSION
+
 /* Define to indicate the ${PRI_DESCRIP} library version */
 #undef HAVE_PRI_VERSION