Disconnect all resources on a given channel via CLI

I’m looking a for way to disconnect all the resources of a given channel (or otherwise device). I see that in the dial plan, you can do SoftHangup(SIP/250,a) but I see know way of doing this from the CLI or via the AMI. Both offer a hang up command, but the CLI seems to force you to type the entire channel name (with the -a459ujhd appended). AMI seems to be similar, or maybe I’m not sure how to add that ‘a’ option on. I’m on Asterisk 1.2

Thanks.

Well, I ended up successfully accomplishing this, albeit using a very custom (and therefore more error prone) method.

I’m scripting using PHP with the AMI and a class I found called AstMan. AstMan is a small, to the point class that lets you easily query the AMI.

The purpose of all this is to be used as an emergency intercom, so that is a message absolutely must be delivered, it will be, even if someone is on their phone. It seemed there was no good way to force a call through to a phone.

Basically, I wrote two functions (along with a couple helpers). getChannelData queries the CLI through the Command AMI action and retrieves the channel information from “show channels”. It then parses that text into an easily usable object that contains total active channels, total active calls and all currently open channels.

Next I created the getDeviceChannels function, which takes the device name as the first argument (SIP/250 for instance). It then runs through the active channel data adding any channel names that belong to the passed device to an array. It then returns this, which means that we now have the full channel name of every open channel on a specified device. Yay!

Now that we have information, it’s very easy to disconnect every call on the device, simply by running through the array of returned channels and doing something like this:

$hangup = “Action: Command\r\nCommand: soft hangup $channel\r\n\r\n”;

Merging all of this into my original intercoming script effectively solved my problem. Now before originating to each extension that will receive the intercom message, the device is first checked for open channels, all of which are disconnected. Then script continues on as normal and originates the call, and then end user gets the important message.

To cut down of performance issues, I only retrieve channel data once per intercom message, at the beginning.

The below code may not really be usable for anyone else, and I can’t guarantee it to be reliable or anything, I -just- wrote, and have a lot more testing to do, but I hope I helped out someone.

function getDeviceChannels($device, $channel_data = NULL){
  global $api;
  if(is_null($channel_data)){
    $channel_data = getChannelData($api);
  }
  $open_channels = $channel_data['channel_names'];
  for($count = 0; $count < count($open_channels); $count++){
    if(substr($open_channels[$count], 0, strlen($device)) == $device){
      $device_channels[] = $open_channels[$count];
    }
  }
  
  return $device_channels;
} 

function getChannelData($api){
  $columns[] = new column('Channel');
  $columns[] = new column('Location');
  $columns[] = new column('State');
  $columns[] = new column('Application(Data)');
  
  $parser = new columnParser($columns);  

  $line[] = "Action: Command";
  $line[] = "Command: show channels";
  
  $command = buildCommand($line);
  
  $response = $api->Query($command);  
  $textArr = $parser->make_array($response, "\r\n");
  $infoArr = $parser->make_array($textArr[2], "\n");
 
  $header = $infoArr[0];
  $info_startIndex = 1;
  $info_endIndex = array_ereg('[[:digit:]]{0,5} active channels', $infoArr);
  
  for($count = $info_startIndex; $count < $info_endIndex; $count++){
    $channelInfo[] = $infoArr[$count];
  }  
 
  $active_channels = split(" ", $infoArr[array_ereg('[[:digit:]]{0,5} active channels', $infoArr)]);
  $active_channels = $active_channels[0];
    
  $active_calls = split(" ", $infoArr[array_ereg('[[:digit:]]{0,5} active call', $infoArr)]);
  $active_calls = $active_calls[0];  
  
  $results = $parser->get_columns($header, $channelInfo);

  $channel_names = $results[0]->data;
  $channel_states = $results[2]->data;
    
  $channel_data['active_channels'] = $active_channels;
  $channel_data['active_calls'] = $active_calls;
  $channel_data['channel_names'] = $channel_names;
  $channel_data['channel_states'] = $channel_states;   
  
  return $channel_data;
}

function array_ereg($pattern, $haystack) {
  for($i = 0; $i < count($haystack); $i++) {
    if(ereg($pattern, $haystack[$i])) {
      return $i;
    }
  } 
  return false;
}

function buildCommand($arr){
  foreach($arr as $l){
    $command .= $l . "\r\n";
  }
  $command .= "\r\n";
  
  return $command;
}

class columnParser {
  var $columns = '';

  function columnParser($columns){
    $this->columns = $columns;
  }
  
  function make_array($text, $split){
    return split($split, $text);
  }  
  
  // returns zero indexed array
  function get_columns($header, $textArr){
    for($count = 0; $count < count($this->columns) + 1; $count++){
      $loc = strpos($header, $this->columns[$count]->name);
     
      if($count < count($this->columns)){
        $this->columns[$count]->loc = $loc;
      }
    
      if($count > 0){
        $this->columns[$count - 1]->end_loc = ($loc - 1);
      }
    }  
   
    // grab data from results
    
    foreach($textArr as $line){
      if(strlen(trim($line)) > 0){
        foreach($this->columns as $key => $col){
          $col->data[] = trim(substr($line, $col->loc, $col->end_loc - $col->loc));
          $this->columns[$key] = $col;
        }
      }
    }
    
    return $this->columns;
  } 
}

class column {
  var $name = '';
  var $loc;
  var $end_loc;
  
  var $data = array();
  
  function column($name=''){
    $this->name = $name;
  }
}