Mac OS Xの標準機能のみで無線LANアクセスポイント毎にプロキシ設定を自動切り替えする方法

#背景
私の環境では、Wifiルーター経由でNW接続する場合はProxyが必要となるので、Networkプロファイルを作成し、それを手動で切り替えてました。（先生1にも載ってます）
ただ、それが面倒なので自動化したいんです。かつ、**全てOS標準機能＋Shellスクリプトだけ**で実実装したいんです。

#参考にした記事（先生）
先生1：[Mac OS X で ネットワークプロファイルをアクセスポイント名で自動切り替え](https://qiita.com/kobakou/items/93f18aac4334e87f6d04)
先生2：[無線APによって自動でhostsを変える方法](http://unknownplace.org/memo/2009/12/03/1/)


#要するに

1. アクセスポイント情報が変わった事をトリガーとして
2. アクセスポイント名に合わせてネットワーク環境を切り替える

という事をやります。

具体的な方針としては、

- Networkプロファイルをプロキシ版とそうでない版を作っておく
- OSのLaunchAgentを使って、アクセスポイント設定ファイルの更新を検知したら、
- プロファイル切り替えBashスクリプトを実行

という事をやりますよ。

#前提
MacOSの様なUnixのことがさっぱりわからない人はやらない方が良いです。
（ターミナル使ってファイル操作、とか意味わからない人って事です。）

#1. Networkプロファイルでプロキシ版とそうでない版の作成

まずはサマリーです。

1-1. 「システム環境設定」から「ネットワーク」を開く
1-2. 「ネットワーク環境を編集」でデフォルトの設定の名前を「NOProxy」に変更する
1-3. 同じく「ネットワーク環境を編集」で、「Proxy」を作成する。
1-4. 作成した「Proxy」プロファイルで、それぞれProxyの内容を設定する

です。では実際にやっていきましょう。

**1-1.「システム環境設定」から「ネットワーク」を開く**
システム環境は開けますよね。そこのネットワークを開いて、上部にある「ネットワーク環境を編集」をポチってください。（ちなみに、下の画像ではすでにNOProxyとProxyがありますが、普通の人はデフォルト、とかいう設定があるのみだと思います。）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/116534/837b5650-10f7-a948-984b-c7285fba973f.png)

**1-2.「ネットワーク環境を編集」でデフォルトの設定の名前を「NOProxy」に変更する**
以下図の様に歯車マークをポチって、デフォルトの設定を名称変更します。名前は「NOProxy」にしておけば、後続のShellスクリプトそのまま使っても動いてくれるはずです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/116534/ce75efb5-6005-4d37-fe8d-f9a73a7b9f09.png)

**1-3.同じく「ネットワーク環境を編集」で、「Proxy」を作成する。**
今度は、＋をポチって、新しい「Proxy」というプロファイルを作成し、「完了」をポチってください。
以下の様に2つのプロファイルが出来ていればOKです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/116534/579c97fc-7459-2700-d668-92a008d4295d.png)


**1-4. 作成した「Proxy」プロファイルで、それぞれProxyの内容を設定する**
そしたら、「Proxy」プロファイルを選択して、右下の「詳細」をポチりましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/116534/7006e9a5-f79c-ae17-89df-a2a68deeedfb.png)

出てきた設定の「プロキシ」タブで、プロキシ設定を適宜入れ込みます。ここは各々の環境によって違うと思いますので、ネットワーク管理者などに確認してください。
設定したら「OK」で終わりです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/116534/76f02c33-4c56-c88a-ff09-c764bddb3d3c.png)




#2.アクセスポイント情報が変わった事をトリガーにする方法

ここでのステップは
2-1. LaunchAgent用のplistファイルを作る
2-2. LaunchAgentに反映させる
となります。（LaunchAgentってのは、ファイルの監視でのプログラム実行や時間毎での実行などができる機能です。Cronよりも便利な奴ですね。Cronの方が手軽だけど。）

では参りましょう。

##2-1.LaunchAgent用のplistファイルを作る

以下の様なplistファイルを~/Library/LaunchAgents/以下に作成しましょう。（~/ってのはユーザーのホームディレクトリです。）

```xml:custom.airport_pref_monitor.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
          "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>org.unknownplace.wifi_switch</string>
    <key>ProgramArguments</key>
    <array>
      <string>/Users/ここに自分のユーザー名/sh/NW_Prof_switch.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/dev/null</string>
    <key>StandardOutPath</key>
    <string>/dev/null</string>
    <key>WatchPaths</key>
    <array>
      <string>/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist</string>
    </array>
  </dict>
</plist>
```
上記でいじる必要があるのは「ここに自分のユーザー名」の箇所です。まさかわからない人はターミナルで`whoami`とかで確認できます。
なお、このplistは先生2を参考にしていますが、私の場合はProgramArgumentsではShellスクリプトを直接実行する仕様になってます。単純にShellスクリプト書くのが慣れている為なので、それ以外の言語を使いたい人は好きにしてください。
`今回は実行するShellスクリプトの内容的にrootとして動かす必要も無いし、かつ、自分以外のユーザーがこのMacに登録されていない為、ホームのLibrary/LaunchAgentsに入れています。ですが、そうで無い場合はシステムのLaunchDaemonsに登録するという方針でもアリだと思います。（ただ、この説明が何言ってるか意味わからない場合は、ここで紹介している方法をやってもらえれば良いと思います。）`

ちなみに、スクリプトのパスに空白を含んでいた場合、””でくくっても、’’でくくっても、バックスラッシュを入れてもうまく行きませんでした。誰か知ってたら教えてください。:frowning2:

##2-2.LaunchAgentに反映させる
方法としては、

 - OS再起動する
 - コマンドで認識させる

のどちらかで良いです。


コマンドの場合は

```
sudo launchctl load /Users/XXXXXXX/Library/LaunchAgents/custom.airport_pref_monitor.plist
```

で反映できます。実行すべきスクリプトがすでにあれば、即時で実行されるはずです。
もしplistの設定などを書き換えて、それを再度即座に反映したい場合は、

```
sudo launchctl unload /Users/XXXXXXX/Library/LaunchAgents/custom.airport_pref_monitor.plist
```

を実行してから再度loadの方を叩けば反映できます。


#3.アクセスポイント名に合わせてNetworkプロファイルを切り替える方法
先ほどのplistの中で実行されるShellスクリプトを準備します。

##3-1.まずは、Proxyを使わないといけないAP名を把握する
コマンド`networksetup -getairportnetwork  en0`を実行して出てきたAP名をメモっておきましょう。

##3-2.スクリプトを準備します。
まずはホームにshディレクトリを準備します。他の場所が良い人は適宜置き換えてください。わかんなければ、そのままで。あと、viわからない人は、emacsでも何のエディタでも良いです。

```
mkdir ~/sh
vi ~/sh/NW_Prof_switch.sh
```
サンプルとして以下を用意しました。先生1を参考にしてます。

```bash:NW_Prof_switch.sh 
#!/bin/bash

# set AccessPoint uniqe name for switch profile.
apname=ここにAP名を記載しましょう

lastapfile=/tmp/last_airport.flg
[ -e "$lastapfile" ] || touch "$lastapfile"
lastap=`cat $lastapfile`
curap=`networksetup -getairportnetwork  en0  | awk '{print $4}'`

if [ "$curap" = "$lastap" ]; then
    #exit if ap not changed
    exit 0
else
    echo $curap > $lastapfile
fi

if [ "$curap" = "$apname" ]; then
    scselect Proxy  > /dev/null
    #osascript -e 'display notification "NW Changed to Proxy" with title "NW Config"'
else
    scselect NOProxy > /dev/null
    #osascript -e 'display notification "NW Changed to NOProxy" with title "NW Config"'
fi
```

ネットワーク環境で「Proxy」と「NOProxy」が準備してあれば、スクリプトでいじる箇所はapnameの値のみです。スクリプトの「ここにAP名を記載しましょう」を3-1でメモった内容にしてください。念のためですが`Current Wi-Fi Network:`ってのは要らないですからね。
（ちなみ中身についてですが、先生のスクリプトのままではAP設定ファイルが**APの変更ではない理由で更新された場合**でも都度ネットワークプロファイルを切り替えようとしてしまうので、一時ファイルを/tmpに作成して、そこに最後のAP名を記載してチェックかけています。そして、こっそり通知センターでPopupだそうと思って書いた行がありますが、手で叩くと動くのにLaunchAgentでは動かなかったので一旦コメントアウトしてます。PDしてない。）

ファイルができたら実行権限もつけておきます。700でも744でも、そこは主義主張に合わせてご自由にどうぞ。

```
chmod 700 ~/sh/NW_Prof_switch.sh
ls -l ~/sh/NW_Prof_switch.sh
```

#準備完了！
ということで、あとはAPを変えて動くかどうか試してみてください。切り替わっているかは、以下で確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/116534/744fd41f-88a9-b782-fbbe-9b547c5c85eb.png)


##ちゃんと動かないーという人は
- ネットワークプロファイルの名前があってますか？
- plistの中のユーザー名は正しいですか？
- スクリプトのAP名は正しいですか？
- スクリプトの実行権限は正しいですか？

というあたりを見直しつつ、plistの標準出力と標準エラー出力を今の/dev/nullから適当な/tmp/hogehoge.logとかにしてデバッグしてみてください。


#以上！

ありがとうございました。


.

.