MacOSはネットワーク周りの設定をまとめて管理できる、 「ネットワーク環境」 と言う仕組みを持っています。これは繋ぐネットワーク先によって手動で設定する必要がある場合に、簡単に切り替えることができて 一応便利 です。
ところが何故か接続先に応じて自動でネットワーク環境が切り替わる機能がありません。そこで以下のページにあるスクリプトを元に手直しした物を使わせて頂いています。
時が経ちオリジナルのままでは動作が怪しいところも出てきたので、何かの参考になればと思い私のバージョンを公開します。
動作環境
- MacBook Pro 14inch(2021)/macOS Monterey(12.4)
- MacBook 12inch(2016)/macOS Mojave
ソース
wifienv_selector.sh
(オリジナル:wifi_environment_auto_selector.sh
)
#!/bin/sh
# Set PATH strictly to avoid security hole
PATH=/usr/bin:/bin:/usr/sbin:/sbin:
#
# Configuration
logfile=${HOME}/logs/wifienv.log
ssidfile=${HOME}/.wifienv_ssid
wifiIF="en0"
log_enabled=1
# 0: do not log messages, 1: log messages
use_alert=1
# for sending messages to user,
# 0: use notification, 1: use alert dialog
#
# functions
plog(){
if [ $log_enabled -ne 0 ]; then
/bin/echo -n `date +'%Y/%m/%d %H:%M:%S'`" " >> $logfile
/bin/echo $1 >> $logfile
fi
}
last_ssid(){
if [ -r $ssidfile ]; then
read ssid < $ssidfile
echo $ssid
else
echo ""
fi
}
save_ssid(){
echo $1 > $ssidfile
}
dialog(){
if [ $use_alert -ne 0 ]; then
osascript -e 'display alert "'"${1}"'" message "'"${2}"'" as critical giving up after 5' >/dev/null 2>&1
else
osascript -e 'display notification "'"${2}"'" with title "'"${1}"'" subtitle "'"${3}"'"' >/dev/null 2>&1
fi
}
switch2loc() {
newloc=$1
newssid=$2
curloc=`networksetup -getcurrentlocation`
lastssid=`last_ssid`
save_ssid ${2}
if [ x${lastssid} = x${newssid} ]; then
plog "SSID is not changed: ${newssid}"
return
fi
plog "SSID is changed: ${lastssid} -> ${newssid}"
if [ x${curloc} = x${newloc} ]; then
plog "location is identical: (${curloc})"
dialog "SSIDが“${2}”になりました" \
"ネットワーク環境(${curloc})は変更ありません"
return
fi
if networksetup -switchtolocation ${newloc}; then
plog "location is changed: ${curloc} -> ${newloc}"
dialog "SSIDが“${newssid}”になりました" \
"ネットワーク環境を“${newloc}”に変更しました"
else
plog "failed to switch location from {$curloc} to ${newloc}"
fi
}
IsWiFiPwrOff() {
networksetup -getairportpower $1 | awk 'tolower($4) ~ /on/ { exit 1 }'
}
getSSID() {
count=5
while [ $count -gt 0 ]; do
let count=count-1
if ssid=`networksetup -getairportnetwork ${wifiIF} |\
awk '/^Current/ { print $NF; exit 0 }
{ exit 1 }'`; then
break
fi
sleep 1
done
if [ $count -le 0 ]; then
plog "failed to get SSID"
dialog "エラー" "SSIDが取得できません"
echo ""
else
echo $ssid
fi
}
#
# main
# check WiFi Power
if IsWiFiPwrOff ${wifiIF} ; then
plog "WiFi is off"
exit 0
fi
ssid=`getSSID`
case ${ssid} in
"MyHome_a" | "MyHome_bg")
switch2loc "自宅" ${ssid}
;;
"Office_8F" | "Office_9F")
switch2loc "Office" ${ssid}
*)
switch2loc "自動" ${ssid}
;;
esac
${HOME}/Library/LaunchAgents/wifienv_selector.plist
(旧:wifi_environment_auto_selector.plist
)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>wifienv_selector</string>
<key>ProgramArguments</key>
<array>
<string>/Users/yourname/bin/wifienv_selector.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>WatchPaths</key>
<array>
<!-- string>/var/run/resolv.conf</string -->
<string>/Library/Preferences/com.apple.wifi.known-networks.plist</string>
</array>
</dict>
</plist>
<key>ProgramArguments</key>
内の<string>/Users/yourname/bin/wifienv_selector.sh</string>
はwifienv_selector.shのpathです。適宜変更して下さい。
/etc/newsyslog.d/wifienv.conf
# logfilename: $HOME/logs/wifienv.log
# [owner:group]: yourname:staff
# mode: 640
# count: 12 (1 year)
# size: *
# when: $M1D0 (0:00 on 1st day of month)
# flags: J (compressed by bzip2)
/Users/yourname/logs/wifienv.log yourname:staff 640 12 * $M1D0 J
logfilename(/Users/yourname/logs/wifienv.log
)や[owner:group]
(yourname:staff
)は適宜変更して下さい。
使い方
上記ファイルを用意した後、シェルで
$ launchctl load ~/Library/LaunchAgents/wifienv_selector.plist
を実行します。
なお二度目以降は、以下の通り一旦unloadしてからloadします。
$ launchctl unload ~/Library/LaunchAgents/wifienv_selector.plist
$ launchctl load ~/Library/LaunchAgents/wifienv_selector.plist
主な変更点
wifienv_selector.sh
(オリジナル:wifi_environment_auto_selector.sh
)
通知はalert dialogで行う
私は良くnotificationを見落としてしまいます。また集中モードによっては、表示されない事もあります。その為alert dialogで通知するようにしました。なおOKボタンを押さずとも、5秒で閉じます。
notificationの方がお好みの方は、use_alert=1
を0
に変更して下さい。
SSIDのみの変更でも通知する
切り替わったのに気が付かないのは、やはり気持ち悪いので。
ログを取る
正常に動作しているのか不安になった時に、確認したい事もあります。不要な場合はlog_enabled=1
を0
にして下さい。ファイル名はlogfile
で設定されています。
その他
-
scselect
でネットワーク環境を変更していたのを、networksetup -switchtolocation
で変更するようにしました。 -
networksetup -getairportnetwork
でSSIDを得ていますが、タイミングによっては失敗する事があったので、修正しました。これはコマンドの出力が以下のように変化する為です。
(変更前) Current Wi-Fi Network: <last ssid>
↓
(一旦切断) You are not associated with an AirPort network.
↓
(再接続) Current Wi-Fi Network: <new ssid>
wifienv_selector.plist
(オリジナル:wifi_environment_auto_selector.plist
)
WatchPaths
を変更... しない方が良い
以下は接続先のすべてでDHCPが用意されている場合です。
従ってDHCPが用意されていないネットワークにつなぐ場合には機能しません。この場合はWatchPaths
を/Library/Preferences/com.apple.wifi.known-networks.plist
にすべきです。
OSのバージョンによって、WiFi I/Fが変わった時に変更されるファイルは異なるようです。しかしUNIX系OS標準の
/etc/resolv.conf
なら、DHCPでnameserverが変わる度に必ず更新される筈です。
調べてみたところ、$ LC_ALL=C \ls -l /etc/resolv.conf lrwxr-xr-x 1 root wheel 22 May 10 06:30 /etc/resolv.conf -> ../var/run/resolv.conf
と
/var/run/resolv.conf
へのシンボリックリンクになっていました。この/var/run/resolv.conf
をWatchPaths
に設定した所、問題なく動いています。
課題など
Ethernetを扱えるか?
未調査ですが、Ethernetを接続した場合でもこれで扱えるようにしたい物です。
firewallの切り替え
wifienv_selector.sh
の最後のcase
文の所を改変すれば、firewallの制御もできます。MacOS純正のfirewallなら、コマンドラインから切り替える事ができそうです。
こちらなどによれば、firewallをon/offするには:
# firewall OFF $ sudo sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 0 # firewall ON $ sudo sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 1
なのだそうです。そこで
/Library/Preferences/com.apple.alf
と言うファイルが無いか調べてみると、$ ls /Library/Preferences/com.apple.alf* /Library/Preferences/com.apple.alf.plist $ file /Library/Preferences/com.apple.alf.plist /Library/Preferences/com.apple.alf.plist: Apple binary property list
と
/Library/Preferences/com.apple.alf.plist
と言うproperty list ファイルが見つかりました。これをplutil -p
で覗いてみると、firewallの設定らしきものが書かれています。
よってこのファイルを複数用意し、差し替える事ができるのでは無いかと予想しています。
ただ私が使っているAnti Virus(ESET Cyber Security Pro)には接続先により動作を変えられるfirewallがあるので、実際に作ってはいません。予想される問題としてはfirewallを設定変更するにはroot権限が必要になるので、security holeにならないように注意する必要がある所でしょうか。
参考
- Wifi接続先に応じてネットワーク環境設定を自動で切り替える - Qiita
- Mac Automation Scripting Guide
- MacOSのマニュアル
man networksetup
man scselect
man osascript