Here is a simple bash script that logs into the web interface of a Grandstream IP phone and submits some configuration changes, and then reboots the device for the changes to take effect. I wrote this to update a handful of Grandstream phones with a new phonebook XML URL, but it could be used for other configuration changes as well. This was tested with a Grandstream GXP2120 on firmware versions 1.0.1.56, 1.0.1.64, 1.0.1.105, 1.0.4.23, and 1.0.6.7, but might be adaptable for other Grandstream devices as well.
Grandstream has configuration templates for a number of devices. You can download a zip archive of them from the following URL: http://www.grandstream.com/tools/GAPSLITE/config-template.zip [archive.org]. The configuration is made up of a pair of values, a P-code and the P-codes applicable value, Grandstream calls these P values in some of their documentation.
My goal was to get my Grandstream phones to periodically download my automatically generated phonebook file discussed in a previous blog post. The applicable configuration details in my case are shown below.
# Number: 0, 1, 2, 3
# Mandatory
#P330 = 0
# Phonebook XML Server Path
# This is a string of up to 256 characters that should contain a path to the XML file. It MUST be in the host/path format.
# For example: directory.grandstream.com/engineering
# String
#P331 =
# Phonebook Download Interval (in minutes)
# Valid value range is 5-720. Default is 0 for disabled
# Number: 0, 5-720;
#P332 = 0
# Remove Manually-edited entries on Download. 0 - No, 1 - Yes. Default is 1
# Number: 0, 1
# Mandatory
#P333 = 1
# Sort Phonebook by. 0 - Last Name. 1 - First Name. Default is 0
# Number: 0, 1
# Mandatory
#P2914 = 0
My Enable Phonebook XML Download method is HTTPS, my Phonebook XML Server Path is “10.0.0.1/phonebook.php“, I want the phone to download on a 30 minute interval and I want it to replace any phone book entries the user might have created.
My applicable P-values were:
P330=3
P331=10.0.0.1/phonebook.php
P332=30
P333=1
I then concatenate these P-values into one long string without any white space using the ampersand as a separator:
Example usage
Here is how I updated a single phone with the P-values discussed earlier. First I set a shell variable containing the values. My device is located on the LAN at 10.0.0.172. I use this IP as the first argument to the script, and the P-values variable in the second argument. If you want to specify the P-values directly to the script without a shell variable, just make sure to quote them so that the ampersands don’t get misinterpreted by your shell. The final argument to the script is the web interface password of the device being updated.
[root@phone scripts]# ./gs_push.sh 10.0.0.172 $Pvalues admin
Config sent, rebooting...
Saving User Data...Done
Blocks Used ( 119 / 1024 )
Success!
Only one argument (the IP address) is required, the script will ask for the additional data if it isn’t passed in to it from the command line.
Usage: ./gs_push.sh <IP Address of Grandstream device> [P-value pairs] [password]
Here is an example of updating just the phone book URL using a more interactive mode to the script
Enter password [admin]: *****
Enter the P-code and value pair. Use '&' to separate multiple P-values.
Example: P330=3&P331=10.0.0.1/phonebook.xml&P332=30&P333=1
P-values:P331=10.0.0.4/phonebook.php
Config sent, rebooting...
Saving User Data...Done
Blocks Used ( 119 / 1024 )
Success!
Here are the commands I used to update all of the Grandstream devices communicating with my Asterisk PBX. I grep for the Grandstream OUI in the list of MAC addresses outputted from the “arp” command. These devices will usually be communicating with the Asterisk server periodically anyhow, and so probably they are already listed in the ARP table. To try and hedge that bet, all the devices are first sent a ping request to generate some new traffic. The output from the gs_push.sh script is suppressed by redirecting it to /dev/null, however in the event of a failure the script should announce via STDERR the IP address of the device it had trouble with.
asterisk -rx "sip show peers" |awk '{print $2}' |grep '\.' |xargs -n1 ping -c1 >/dev/null
arp |grep -i '00:0b:82' |awk '{print $1}' |xargs -i{} -n1 ./gs_push.sh "{}" "$Pvalues" admin >/dev/null
# +----------------------------------------------------------------------+
# | Grandstream GXP21xx IP Phone HTTP POSTing P-value config pusher |
# | May work with with other Grandstream devices, use at your own risk. |
# +----------------------------------------------------------------------+
# | For the latest P-value descriptions consult the applicable firmware |
# | release notes and configuration template descriptions: |
# | http://www.grandstream.com/tools/GAPSLITE/config-template.zip |
# +----------------------------------------------------------------------+
# | The contents of this file are subject to the General Public License |
# | (GPL) Version 2 (the "License"); you may not use this file except in |
# | compliance with the License. You may obtain a copy of the License at |
# | http://www.opensource.org/licenses/gpl-license.php |
# | |
# | Software distributed under the License is distributed on an "AS IS" |
# | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See |
# | the License for the specific language governing rights and |
# | limitations under the License. |
# +----------------------------------------------------------------------+
get_password () {
saveIFS="$IFS"
IFS=$'\n'
while read -s -n 1 char
do
case $(printf "%d\n" \'$char) in
127)
if [ "${#passwd}" -gt 0 ]; then
echo -ne "\b \b"
passwd="${passwd:0:${#passwd}-1}"
fi
;;
0)
echo
break
;;
*)
echo -n "*"
passwd+=$char
esac
done
IFS=$saveIFS
}
target_ip="$1"
if [ -z "$target_ip" ]; then
echo "Usage: $0 <IP Address of Grandstream device> [P-value pairs] [password]" >&2
exit 1
fi
Pvalues="$2"
passwd="$3"
if [ -z "$passwd" ]; then
# If the second argument isn't a valid P-code pair use it as the password
if [ "${Pvalues//'='}" == "$Pvalues" ]; then
passwd="$Pvalues"
unset Pvalues
fi
fi
if [ -z "$passwd" ]; then
echo -n "Enter password [admin]: "
get_password
if [ -z "$passwd" ]; then
passwd="admin"
fi
fi
if [ -z "$Pvalues" ]; then
# See if anything is waiting on stdin
read -t1 Pvalues
if [ -z "$PValues" ]; then
# Or just ask the user..
echo "Enter the P-code and value pair. Use '&' to separate multiple P-values."
echo "Example: P330=3&P331=10.0.0.1/phonebook.xml&P332=30&P333=1"
echo -n "P-values: "
read Pvalues
fi
fi
get_sid() {
while read -r || [[ -n "$REPLY" ]]
do
if [ -n "$sid" ]; then
break
fi
if [ "${REPLY:0:23}" == "Set-Cookie: session_id=" ]; then
sid="${REPLY:23}"
sid="${sid//;}"
fi
if [ "${REPLY//'"sid"'}" != "$REPLY" ]; then
sid="${REPLY#*'"sid"'}"
sid="${sid#*'"'}"
sid="${sid%%'"'*}"
fi
done
echo "$sid"
}
post_status() {
# Old firmware says need to reboot, new firmware says success
# new firmware doesn't end with a line break :(
while read -r || [[ -n "$REPLY" ]]
do
if [ "${REPLY//eboot}" != "$REPLY" ]; then
echo "1"
break
fi
if [ "${REPLY//success}" != "$REPLY" ]; then
echo "1"
break
fi
done
}
reboot_status() {
success=0
headers=0
while read -r || [[ -n "$REPLY" ]]
do
if [ -z "$REPLY" ]; then
# header data done
headers=1
fi
if [ "${REPLY//'{"results":['}" != "$REPLY" ]; then
if [ "${REPLY//[1]}" != "$REPLY" ]; then
success=1
fi
break
fi
if [ "${REPLY//'savereboot'}" != "$REPLY" ]; then
success=1
break
fi
if [ "${REPLY//'relogin'}" != "$REPLY" ]; then
success=1
break
fi
# fw 1.0.1.56 requires a different reboot action
if [ "${REPLY//'The requested URL was not found'}" != "$REPLY" ]; then
curl -s -i -b "session_id=$sid" "http://$target_ip/cgi-bin/rs" |reboot_status
success=2
fi
# print extra saving info mixed in the headers, but ignore other bits
if [ "${REPLY//:}" != "$REPLY" ]; then
unset REPLY
fi
if [ "${REPLY:0:4}" == "HTTP" ]; then
unset REPLY
fi
if [ "${REPLY:0:1}" == "<" ]; then
unset REPLY
fi
if [ "$headers" == "0" ] && [ -n "$REPLY" ]; then
echo "$REPLY"
fi
done
if [ "$success" == "1" ]; then
echo "Success!"
fi
if [ "$success" == "0" ]; then
echo "Fail. ($target_ip)" >&2
fi
}
if [ -z "$Pvalues" ]; then
echo "No configuration parameters specified." >&2
exit 1
fi
# Older firmware passed password as "P2", newer firmware passes as "password"
# <form> action is the same
sid="$(curl -s -i -d "P2=$passwd&password=$passwd" "http://$target_ip/cgi-bin/dologin" |get_sid)"
if [ -z "$sid" ]; then
echo "Login failed." >&2
exit 1
fi
# <form> action for config update is different across firmware versions
# Try old firmware style first
status="$(curl -s -i -b "session_id=$sid" -d "$Pvalues&update=Update" "http://$target_ip/cgi-bin/update" |post_status)"
if [ -z "$status" ]; then
# Try new firmware config update action
status="$(curl -s -i -b "session_id=$sid" -d "sid=$sid&$Pvalues" "http://$target_ip/cgi-bin/api.values.post" |post_status)"
fi
# Try new firmware way if the last attempt failed
if [ -n "$status" ]; then
echo "Config sent, rebooting..."
# Old firmware used query_string to pass REBOOT, new firmware uses POST data
curl -s -i -d "request=REBOOT&sid=$sid" -b "session_id=$sid" "http://$target_ip/cgi-bin/api-sys_operation?REBOOT" |reboot_status
else
echo "Couldn't update configuration ($target_ip)" >&2
fi
1 thought on “Grandstream Simple HTTP Configuration Pusher”
Gr ·
Hi Chris,
Thank you for the scripts.
I am trying to build interface with cURL and PHP that allows me to make certain operations on the phone without using web-ui. For the P values everything I tested is working, but is difficult for me to figure out how to upload wallpaper. Setting P2916 to 3 and P2917 to the web url of the image works, but I want to do it with upload. Actually, I made attempt with cURL, as follows:
For CURLOPT_POSTFIELDS I assigned “&sid=”.$sid.”&file=@img/test.png”
Unfortunately, it doesn’t work.
I will be glad if you give me any solution for this.
Thak you in advance!
BR,
Gr