travelmate.sh (8834B)
1 #!/bin/sh 2 # travelmate, a wlan connection manager for travel router 3 # written by Dirk Brenken (dev@brenken.org) 4 5 # This is free software, licensed under the GNU General Public License v3. 6 # You should have received a copy of the GNU General Public License 7 # along with this program. If not, see <http://www.gnu.org/licenses/>. 8 9 # set initial defaults 10 # 11 LC_ALL=C 12 PATH="/usr/sbin:/usr/bin:/sbin:/bin" 13 trm_ver="0.6.0" 14 trm_sysver="$(ubus -S call system board | jsonfilter -e '@.release.description')" 15 trm_enabled=0 16 trm_debug=0 17 trm_automatic=0 18 trm_maxwait=30 19 trm_maxretry=3 20 trm_timeout=60 21 trm_iw=1 22 23 # source required system library 24 # 25 if [ -r "/lib/functions.sh" ] 26 then 27 . "/lib/functions.sh" 28 else 29 f_log "error" "required system library not found" 30 fi 31 32 # f_envload: load travelmate environment 33 # 34 f_envload() 35 { 36 # initialize lists 37 # 38 trm_aplist="" 39 trm_stalist="" 40 trm_radiolist="" 41 42 # load uci config and check 'enabled' option 43 # 44 option_cb() 45 { 46 local option="${1}" 47 local value="${2}" 48 eval "${option}=\"${value}\"" 49 } 50 config_load travelmate 51 52 if [ ${trm_enabled} -ne 1 ] 53 then 54 f_log "info " "travelmate is currently disabled, please set 'trm_enabled' to '1' to use this service" 55 exit 0 56 fi 57 58 # check for preferred wireless tool 59 # 60 if [ ${trm_iw} -eq 1 ] 61 then 62 trm_scanner="$(which iw)" 63 else 64 trm_scanner="$(which iwinfo)" 65 fi 66 if [ -z "${trm_scanner}" ] 67 then 68 f_log "error" "no wireless tool for wlan scanning found, please install 'iw' or 'iwinfo'" 69 fi 70 } 71 72 # f_prepare: gather radio information & bring down all STA interfaces 73 # 74 f_prepare() 75 { 76 local config="${1}" 77 local mode="$(uci -q get wireless."${config}".mode)" 78 local radio="$(uci -q get wireless."${config}".device)" 79 local disabled="$(uci -q get wireless."${config}".disabled)" 80 81 if [ "${mode}" = "ap" ] && ([ -z "${disabled}" ] || [ "${disabled}" = "0" ]) && \ 82 ([ -z "${trm_radio}" ] || [ "${trm_radio}" = "${radio}" ]) 83 then 84 trm_radiolist="${trm_radiolist} ${radio}" 85 elif [ "${mode}" = "sta" ] 86 then 87 trm_stalist="${trm_stalist} ${config}_${radio}" 88 if [ -z "${disabled}" ] || [ "${disabled}" = "0" ] 89 then 90 uci -q set wireless."${config}".disabled=1 91 fi 92 fi 93 f_log "debug" "mode: ${mode}, radio: ${radio}, config: ${config}, disabled: ${disabled}" 94 } 95 96 # f_check: check interface status 97 # 98 f_check() 99 { 100 local ifname radio cnt=1 mode="${1}" 101 trm_ifstatus="false" 102 103 while [ ${cnt} -le ${trm_maxwait} ] 104 do 105 if [ "${mode}" = "ap" ] 106 then 107 for radio in ${trm_radiolist} 108 do 109 trm_ifstatus="$(ubus -S call network.wireless status | jsonfilter -e "@.${radio}.up")" 110 if [ "${trm_ifstatus}" = "true" ] 111 then 112 trm_aplist="${trm_aplist} $(ubus -S call network.wireless status | jsonfilter -e "@.${radio}.interfaces[@.config.mode=\"ap\"].ifname")_${radio}" 113 ifname="${trm_aplist}" 114 else 115 trm_aplist="" 116 trm_ifstatus="false" 117 break 118 fi 119 done 120 else 121 ifname="$(ubus -S call network.wireless status | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].ifname')" 122 if [ -n "${ifname}" ] 123 then 124 trm_ifstatus="$(ubus -S call network.interface dump | jsonfilter -e "@.interface[@.device=\"${ifname}\"].up")" 125 fi 126 fi 127 if [ "${mode}" = "initial" ] || [ "${trm_ifstatus}" = "true" ] 128 then 129 break 130 fi 131 cnt=$((cnt+1)) 132 sleep 1 133 done 134 f_log "debug" "mode: ${mode}, name: ${ifname}, status: ${trm_ifstatus}, count: ${cnt}, max-wait: ${trm_maxwait}, automatic: ${trm_automatic}" 135 } 136 137 # f_ubus: update ubus service 138 # 139 f_ubus() 140 { 141 local active_triggers iface="${1}" radio="${2}" ssid="${3}" 142 143 for name in ${trm_iface} 144 do 145 active_triggers="${active_triggers}[\"interface.*.down\",[\"if\",[\"eq\",\"interface\",\"${name}\"],[\"run_script\",\"/etc/init.d/travelmate\",\"start\"],1000]]," 146 done 147 active_triggers="${active_triggers}[\"config.change\",[\"if\",[\"eq\",\"package\",\"travelmate\"],[\"run_script\",\"/etc/init.d/travelmate\",\"start\"],1000]]" 148 149 ubus call service add "{\"name\":\"travelmate\", 150 \"instances\":{\"travelmate\":{\"command\":[\"/usr/bin/travelmate.sh\"], 151 \"data\":{\"travelmate_version\":\"${trm_ver}\", 152 \"station_interface\":\"${iface}\", 153 \"station_radio\":\"${radio}\", 154 \"station_ssid\":\"${ssid}\", 155 \"last_rundate\":\"$(/bin/date "+%d.%m.%Y %H:%M:%S")\", 156 \"online_status\":\"${trm_ifstatus}\", 157 \"system\":\"${trm_sysver}\"}}}, 158 \"triggers\":[${active_triggers}]}" 159 } 160 161 # f_log: write to syslog, exit on error 162 # 163 f_log() 164 { 165 local class="${1}" 166 local log_msg="${2}" 167 168 if [ -n "${log_msg}" ] && ([ "${class}" != "debug" ] || [ ${trm_debug} -eq 1 ]) 169 then 170 logger -t "travelmate-[${trm_ver}] ${class}" "${log_msg}" 171 if [ "${class}" = "error" ] 172 then 173 logger -t "travelmate-[${trm_ver}] ${class}" "Please check 'https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md' (${trm_sysver})" 174 exit 255 175 fi 176 fi 177 } 178 179 # f_main: main function for connection handling 180 # 181 f_main() 182 { 183 local config ssid_list ap ap_radio sta_ssid sta_radio sta_iface cnt=1 184 185 f_check "initial" 186 if [ "${trm_ifstatus}" != "true" ] 187 then 188 config_load wireless 189 config_foreach f_prepare wifi-iface 190 if [ -n "$(uci -q changes wireless)" ] 191 then 192 uci -q commit wireless 193 ubus call network reload 194 sleep 5 195 fi 196 f_check "ap" 197 f_log "debug" "ap-list: ${trm_aplist}, sta-list: ${trm_stalist}" 198 if [ -z "${trm_aplist}" ] || [ -z "${trm_stalist}" ] 199 then 200 f_log "error" "no usable AP/STA configuration found" 201 fi 202 for ap in ${trm_aplist} 203 do 204 cnt=1 205 ap_radio="${ap##*_}" 206 ap="${ap%%_*}" 207 if [ -z "$(printf "${trm_stalist}" | grep -Fo "_${ap_radio}")" ] 208 then 209 continue 210 fi 211 while [ ${cnt} -le ${trm_maxretry} ] 212 do 213 if [ ${trm_iw} -eq 1 ] 214 then 215 ssid_list="$(${trm_scanner} dev "${ap}" scan 2>/dev/null | \ 216 awk '/SSID: /{if(!seen[$0]++){printf "\"";for(i=2; i<=NF; i++)if(i==2)printf $i;else printf " "$i;printf "\" "}}')" 217 else 218 ssid_list="$(${trm_scanner} "${ap}" scan | \ 219 awk '/ESSID: ".*"/{ORS=" ";if (!seen[$0]++) for(i=2; i<=NF; i++) print $i}')" 220 fi 221 f_log "debug" "scanner: ${trm_scanner}, ap: ${ap}, ssids: ${ssid_list}" 222 if [ -n "${ssid_list}" ] 223 then 224 for sta in ${trm_stalist} 225 do 226 config="${sta%%_*}" 227 sta_radio="${sta##*_}" 228 sta_ssid="$(uci -q get wireless."${config}".ssid)" 229 sta_iface="$(uci -q get wireless."${config}".network)" 230 if [ -n "$(printf "${ssid_list}" | grep -Fo "\"${sta_ssid}\"")" ] && [ "${ap_radio}" = "${sta_radio}" ] 231 then 232 uci -q set wireless."${config}".disabled=0 233 ubus call network reload 234 f_check "sta" 235 if [ "${trm_ifstatus}" = "true" ] 236 then 237 uci -q commit wireless 238 f_log "info " "interface '${sta_iface}' on '${sta_radio}' connected to uplink '${sta_ssid}' (${trm_sysver})" 239 sleep 5 240 f_ubus "${sta_iface}" "${sta_radio}" "${sta_ssid}" 241 return 0 242 else 243 uci -q revert wireless 244 ubus call network reload 245 f_log "info " "interface '${sta_iface}' on '${sta_radio}' can't connect to uplink '${sta_ssid}' (${trm_sysver})" 246 f_ubus "${sta_iface}" "${sta_radio}" "${sta_ssid}" 247 fi 248 fi 249 done 250 fi 251 cnt=$((cnt+1)) 252 sleep 5 253 done 254 done 255 fi 256 } 257 258 f_envload 259 f_main 260 while [ ${trm_automatic} -eq 1 ] 261 do 262 sleep ${trm_timeout} 263 f_envload 264 f_main 265 done 266 exit 0