#!/usr/bin/haserl --shell=/bin/bash --upload-limit=32768 --upload-dir=/www/tmp <%# upload limit: 32Mb %> <% _WWW='/www' _TMP="${_WWW}/tmp" _LOG="${_WWW}/systemupdate.log" ## safe persistently _DEBUG=1 err() { _ERR="$?" [[ "$_ERR" -gt 0 ]] || return 0 log "$1" head "${2:='400'}" exit "$_ERR" } logThis() { [[ "$_DEBUG" -gt 0 ]] || return 0 local _TYPE='I:' [[ "$_ERR" -gt 0 ]] && _TYPE='E:' local _TIME; printf -v _TIME '%(%d.%m.%Y %H:%M:%S)T' -1 printf '%b\n' "$_TIME $_TYPE ${@} " >> "$_LOG" [[ "$_DEBUG" -gt 1 ]] && printf '%b\n' "[verbose] $_TYPE ${1}" } headerPrint() { case "$1" in 200|'') printf '%b' 'HTTP/1.1 200 OK\r\n';; 301) printf '%b' "HTTP/1.1 301 Moved Permanently\r\nLocation: $HTTP_REFERER\r\n";; 403) printf '%b' 'HTTP/1.1 403 Forbidden\r\n';; 405) printf '%b' 'HTTP/1.1 405 Method Not Allowed\r\n';; 406) printf '%b' 'HTTP/1.1 406 Not Acceptable\r\n';; *) printf '%b' 'HTTP/1.1 400 Bad Request\r\n';; esac printf '%b' 'Content-Type: text/html\r\n\r\n'; } setQueryVars() { env } runSuid() { local _SID=$(/usr/bin/ps -p $$ -o sid=) ## pass session id to the child local _CMD=$@ sudo ./suid.sh $_CMD $_SID 2>/dev/null } getQueryFile() { local _UPLD="${HASERL_fwupload_path##*/}" logThis "'multipart': decoding stream" mv "$_TMP/$_UPLD" "$_TMP/fwupload.bin" 2>/dev/null || _ERR=$? if [[ $_ERR -gt 0 ]]; then showMesg 'Firmware upload has failed' 'Reboot your Superglue server and try again' fi } validIp() { local _IP=$1 local _RET=1 if [[ $_IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' _IP=($_IP) IFS=$OIFS [[ ${_IP[0]} -le 255 && ${_IP[1]} -le 255 && ${_IP[2]} -le 255 && ${_IP[3]} -le 255 ]] _RET=$? fi return $_RET } pwdChange() { if [[ ! -z "${_pwd##$_pwdd}" ]]; then _ERR=1 showMesg 'Passwords did not match' fi if [[ ${#_pwd} -lt 6 ]]; then _ERR=1 showMesg 'Password must be at least 6 characters long' fi runSuid "echo -e \"$_pwd\n$_pwd\" | passwd root" runSuid "echo $(htDigest $_pwd) > $_PWDFILE" _ERR=$? if [[ $_ERR -gt 0 ]]; then showMesg 'Password change failed' else showMesg 'Password is changed' fi } lanAddr() { logThis "new LAN addr is: $_laddr" validIp $_laddr || showMesg 'Not valid network address' doUci set laddr $_laddr _ERR=$? if [[ $_ERR -gt 0 ]]; then showMesg 'Setting network address failed' else (sleep 1; doUci commit network; doUci commit wireless;)& showMesg 'New network address is set' "Your server is now accessible under <a href='http://superglue.local/admin'>http://superglue.local/admin</a>" fi } wanSet() { if [[ ! -z $_wanifname ]]; then ## eth and wlan wan cases are different! ## eth wan requires: ## config interface 'wan' ## option ifname 'eth0' ## ## config wifi-iface ## option device 'radio0' ## option network 'wan' ## option disabled '1' (or no 'config wifi-iface' section at all) ## ## wlan wan requires: ## config interface 'wan' ## option proto 'dhcp' ## (without 'option ifname' specified!) ## ## config wifi-iface ## option device 'radio0' ## option network 'wan' logThis "wan.ifname=$_wanifname" if [[ $_wanifname == 'eth0' ]]; then doUci set wanifname $_wanifname doUci set wanwifacedis '1' elif [[ $_wanifname == 'wlan1' ]]; then doUci set wanifname '' doUci set wanwifacedis '' fi if [[ $_wanproto == 'dhcp' ]]; then doUci set wanproto dhcp elif [[ $_wanproto == 'static' ]]; then logThis "wan.ipaddr=$_wanipaddr" doUci set wanproto static doUci set wanipaddr $_wanipaddr doUci set wannetmask $_wannetmask fi if [[ $_wanifname == 'wlan1' ]]; then ssidChange || showMesg 'Wireless changes failed' fi ## background the following doUci commit network && showMesg 'Internet connection is configured' 'Waiting for device to get ready' || showMesg 'Configuring Internet connection failed' fi logThis "new WAN iface is: $_wanifname" } ssidChange() { ## check for iface [[ ! $_iface =~ ^('wan'|'lan')$ ]] && showMesg 'Error changing wireless settings' 'unknown/unconfigured interface' logThis "$_iface is being set" _p=$_iface ## default enc for now local _enc='psk2' if [[ $_iface == 'wan' ]]; then local _mode='sta' local _ssid="${_wanssid}" local _key="${_wankey}" else local _mode='ap' local _ssid="${_lanssid}" local _key="${_lankey}" fi logThis "ssid: $_ssid [$_mode], key: $_key [$_enc]" logThis $_wanssid if [[ ${#_ssid} -lt 4 ]]; then _ERR=1 showMesg 'SSID must be at least 4 characters long' fi doUci set $_p'ssid' "${_ssid}" _ERR=$? [[ $_ERR -gt 0 ]] && showMesg 'New SSID is not set' if [[ -z $_key ]]; then ## if key is empty set encryption to none and remove key doUci set $_p'key' && doUci set $_p'enc' 'none' _ERR=$? else if [[ ${#_key} -lt 8 ]]; then _ERR=1 showMesg 'Passphrase must be at least 8 characters long' fi doUci set $_p'key' "${_key}" && doUci set $_p'enc' "${_enc}" _ERR=$? [[ $_ERR -gt 0 ]] && showMesg 'Passphrase is not set' fi [[ $_ERR -gt 0 ]] && return $_ERR ##showMesg 'Wireless changes failed' doUci commit wireless ##&& showMesg 'Wireless changes applied' } #showError() { # headerPrint 406 # logThis "$@" # echo "ERROR: $@" # exit 1 #} showMesg() { logThis "$@" local _MSG=$1 local _SUBMSG=$2 _MSG=${_MSG:='Not defined'} _SUBMSG=${_SUBMSG:='back to control panel in a second..'} if [[ $_ERR -gt 0 ]]; then local _TYPE='ERROR: ' headerPrint 406 else local _TYPE='OK: ' headerPrint 200 fi htmlHead "<meta http-equiv='refresh' content='3;url=${HTTP_REFERER}'>" echo "<body> <h1>Superglue system update</h1> <hr> <h2 style='display:inline'>$_TYPE $_MSG</h2> <span style='display:inline; margin-left: 50px;'>$_SUBMSG</span> <hr> </body></html>" exit 0 } updateFw() { logThis "updating fw" _FWFILE="${_TMP}/fwupload.bin" logThis "fwfile is: $(ls -lad $_FWFILE)" _OUT="$(/sbin/sysupgrade -T $_FWFILE 2>&1)" _ERR=$? [[ $_ERR -gt 0 ]] && showMesg "$_OUT" _OUT="$(runSuid /sbin/mtd -e firmware -q write $_FWFILE firmware)" _ERR=$? [[ $_ERR -gt 0 ]] && showMesg "update failed, $_OUT" _OUT="$(/sbin/mtd verify $_FWFILE firmware_2>&1)" _ERR=$? [[ $_ERR -gt 0 ]] && showMesg "update check failed, $_OUT" runSuid reboot showMesg 'Firmware update is completed, rebooting..' 'this might take up to 60 seconds' } rebootNow() { logThis "reboot: now!" runSuid reboot showMesg 'Rebooting..' 'this might take up to 60 seconds' } doUci() { local _CMD='' local _ARG='' case $1 in get|set|commit) _CMD=$1;; *) logThis 'bad UCI command'; headerPrint 405; echo 'bad UCI command'; exit 1 ;; esac case $2 in lanssid) _ARG='wireless.@wifi-iface[0].ssid';; lanenc) _ARG='wireless.@wifi-iface[0].encryption';; lankey) _ARG='wireless.@wifi-iface[0].key';; lanipaddr) _ARG='network.lan.ipaddr';; wanifname) _ARG='network.wan.ifname';; wanproto) _ARG='network.wan.proto';; wanipaddr) _ARG='network.wan.ipaddr';; wannetmask) _ARG='network.wan.netmask';; wanwifacedis) _ARG='wireless.@wifi-iface[1].disabled';; wanssid) _ARG='wireless.@wifi-iface[1].ssid';; wanenc) _ARG='wireless.@wifi-iface[1].encryption';; wankey) _ARG='wireless.@wifi-iface[1].key';; *) if [[ $_CMD == 'commit' ]]; then _ARG=$2 else logThis "bad UCI entry: $2" _ERR=1 showMesg 'bad UCI entry' fi ;; esac if [[ $_CMD == 'get' ]]; then if [ ! -z $_ARG ]; then /sbin/uci -q get $_ARG || return $? fi fi if [[ $_CMD == 'set' ]]; then local _VAL=$3 if [ -z $_VAL ]; then logThis "empty $_ARG value, removing record" runSuid /sbin/uci delete $_ARG || ( echo "uci delete $_ARG: error"; exit 1; ) fi if [ ! -z $_ARG ]; then logThis "setting $_ARG value" runSuid /sbin/uci set $_ARG=$_VAL || ( echo "uci set $_ARG: error"; exit 1; ) fi fi if [[ $_CMD == 'commit' ]]; then runSuid /sbin/uci commit $_ARG|| echo "uci commit $_ARG: error" if [[ "$_ARG" == 'wireless' ]]; then runSuid /sbin/wifi || echo 'wifi: error' fi if [[ "$_ARG" == 'network' ]]; then runSuid /etc/init.d/dnsmasq restart && runSuid /etc/init.d/network restart || echo 'network: error' fi fi } getStat() { . /usr/share/libubox/jshn.sh local _IFACE=$1 local _IFSTAT=$(runSuid ubus call network.interface.wan status 2>/dev/null) logThis "$_IFSTAT" json_get_type _IFSTAT ipv4_address if json_get_type _IFSTAT ipv4_address && [[ "$_IFSTAT" == 'array' ]]; then json_select ipv4_address json_get_type _IFSTAT 1 if [[ "$_IFSTAT" == 'object' ]]; then json_select 1 json_get_var IP4 address json_get_var Subnet4 mask [[ "$IP4" != '' ]] && [[ "$Subnet4" != '' ]] && IP4="$IP4/$Subnet4" fi fi logThis $IP4 } htmlHead() { echo "<!-- obnoxious code below, keep your ports tight --> <!doctype html> <html> <head><title>SuperGlue | System update</title> $@ <link rel='stylesheet' type='text/css' href='http://${HTTP_HOST}/resources/admin/admin.css'> </head>" } if [[ "${REQUEST_METHOD^^}" == "POST" ]]; then [[ $CONTENT_LENGTH -gt 0 ]] || err 'content length is zero, 301 back to referer' '301' case "${CONTENT_TYPE^^}" in APPLICATION/X-WWW-FORM-URLENCODED*) setQueryVars;; MULTIPART/FORM-DATA*) getQueryFile;; *) _ERR=1; _OUT='this is not a post';; esac case $REQUEST_URI in *pwdchange) pwdChange;; *ssidchange) ssidChange;; *lanaddr) lanAddr;; *updatefw) updateFw;; *rebootnow) rebootNow;; *wan) wanSet;; *) logThis 'bad action'; headerPrint 405; echo 'no such thing'; exit 1;; esac fi headerPrint 200 ## html head htmlHead sgver=$(cat /etc/superglue_version) devmod=$(cat /etc/superglue_model) openwrt=$(cat /etc/openwrt_version) wanifname=$(doUci get wanifname || echo 'wlan0') ## TODO fix this wanproto=$(doUci get wanproto) wanipaddr=$(doUci get wanipaddr) wannetmask=$(doUci get wannetmask) wanssid=$(doUci get wanssid) wankey=$(doUci get wankey) echo "<body> <img src='/resources/images/superglue-logo.png' id='sg-logo'> <h2 style='display:inline'>Superglue system update</h2> <span style='display:block;'>System version: $sgver | Device: $devmod</span> <span style='display:block;'>$(uptime)</span> <hr> <span class='title2'>Firmware update:</span> <p>The main purpose of Superglue <b>system update mode</b> is to ensure smooth and simple upgrades to the latest versions of Superglue firmware (OS) during the period of active project development. These updates help us get rid of the bugs and introduce new features (and sometimes, the other way around)</p> <p>Before you begin, please make sure to obtain the latest firmware by visiting our <a href='http://discourse.superglue.it/category/superglue/l/latest' target='_blank'>online users comminuty</a> or directly via the <a href='http://dev.superglue.it/firmware/latest' target='_blank'>Superglue firmware repository</a>.</p> <p>Once you are ready, upload the firmware update file using the form below:</p> <form method='post' action='/systemupdate/updatefw' enctype='multipart/form-data' name='fwupdate'> <div id='uploadbox'> <input id='uploadfile' placeholder='Choose file' disabled='disabled' style='width:400px'> <input id='uploadbtn' name='fwupload' type='file'> </div> <input type='submit' value='Upload'> </form> <p>Configuration options below are optional.<br>Unless you upgrade the current firmware any changes done to the setting will not be saved.</p> <hr> <br><br> Internet connection: <form method='post' action='/systemupdate/wan' name='wan' onchange='formChange();'> <div style='display:inline-flex'> <div style='display:inline-block;'> <select name='wanifname' id='wanifname' style='display:block'> <option value='eth0' id='eth' $([[ $wanifname =~ ('eth') ]] && echo 'selected')>Wired (WAN port)</option> <option value='wlan1' id='wlan' $([[ $wanifname =~ ('wlan') ]] && echo 'selected')>Wireless (Wi-Fi)</option> </select> <fieldset id='wanwifi' class='hide'> <input type='text' name='wanssid' value='$wanssid'> <input type='password' name='wankey' value='$wankey'> </fieldset> </div> <div style='display:inline-block;'> <select name='wanproto' id='wanproto' style='display:block'> <option value='dhcp' name='dhcp' id='dhcp' $([[ $wanproto == 'dhcp' ]] && echo 'selected')>Automatic (DHCP)</option> <option value='stat' name='dhcp' id='stat' $([[ $wanproto == 'static' ]] && echo 'selected')>Manual (Static IP)</option> </select> <fieldset id='wanaddr' class='hide' > <input type='text' name='wanipaddr' id='wanipaddr' value='$wanipaddr'> <input type='text' name='wangw' id='wannetmask' value='$wannetmask'> </fieldset> </div> </div> <input type='hidden' name='iface' value='wan' class='inline'> <input type='submit' value='Apply'> </form> <hr> Local wireless network: <form method='post' action='/systemupdate/ssidchange'> <div style='display:inline-flex'> <div style='display:inline-block;'> <input type='text' name='lanssid' value='$(doUci get lanssid)'> <input type='password' name='lankey' value='$(doUci get lankey)'> </div> <div style='display:inline-block;'> <input type='text' name='lanipaddr' value='$(doUci get lanipaddr)'> <input type='hidden' name='iface' value='lan' class='inline'> </div> </div> <input type='submit' value='Apply'> </form> <hr> <form action='/systemupdate/rebootnow' method='post' class='inline'> <input type='hidden' name='reboot' value='now' class='inline'> <input type='submit' value='Reboot' class='inline'> </form> <form action='http://logout@${HTTP_HOST}/systemupdate' method='get' class='inline'> <input type='submit' value='Logout' class='inline'> </form> <hr> Memory: <pre>$(free)</pre> <hr> Storage: <pre>$(df -h)</pre> <hr> Environment: <pre>$(env)</pre> <hr> </body> <script type='text/javascript' src='http://${HTTP_HOST}/resources/admin/admin.js'></script> </html>" %>