dhcp だけど自分のホストくらいは名前で参照したくならない?
dhcp かつ Dymanic DNS を使わないことが前提で。
DNS の接続時に自分を登録するのが Dynamic DNS なんだったかな。
Windows のネットワーク接続設定にあるやつ。
でも、普通の LAN ではやってない、と思う。
となると、可能なのはこの 2 つか。
- RESOLVER を騙す
- /etc/hosts を書き換える
RESOLVER を騙すのはかなりオオゴトな気がします。
libresolv ってくらいですし。
なので却下。
普通に /etc/hosts を書き換えることにしますか。
ネットワークインターフェースが有効になったときのためのフックスクリプトは、最近では /etc/network の下に置いてあるみたい。
Mac は知らない。Automator で好きにすればいいと思う。
/etc/network/if-up.d の下を見ると、いくつかスクリプトが入ってる。
$ ls /etc/network/if-up.d mountnfs ntpdate openssh-server wpasupplicant
中身を見てみると、だいたいこれらが呼ばれるときに渡される引数が分かります。
引数は環境変数経由で渡されるのが常識みたいです。
よく分からないときは適当にファイルに落としてみればいいと思います。
こんなかんじで。
$ cat /etc/network/if-up.d/test #!/bin/bash # すべての出力を /var/tmp/hoge にリダイレクト exec 2>&1 exec 1>>/var/tmp/hoge # 環境変数をすべて標準出力に /usr/bin/env $ chmod +x /etc/network/if-up.d/test $ sudo /etc/init.d/networking restart $ cat /var/tmp/hoge MODE=start VERBOSITY=0 PHASE=post-up IF_METRIC=100 ADDRFAM=inet METHOD=dhcp PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/etc/network/if-up.d SHLVL=1 IFACE=eth1 LOGICAL=eth1 _=/usr/bin/env
見て分かるとおり、/etc/hosts に記録するために必要な IP アドレスがありません。
なので、ここのフックポイントは使えないようです。
初心に帰って dhclient-script とか使うことになるんでしょうか。
以前 dhcp クライアントはいろんなものがあった気がするのですが、
最近は Internet Systems Consortium DHCP Client V3.1.1 がデフォルトみたいです。
$ ls /etc/dhcp3/ dhclient-enter-hooks.d dhclient-exit-hooks.d dhclient.conf
名前からして、dhclient-enter-hooks.d が DHCP の問い合わせをした時に参照されそう。
ここにはすでに debug スクリプトがいたので、それを実行して引数とかを確認できたので細かいことは省略。
ところで、このフックスクリプトはどういうタイミングで呼ばれるんでしょう。
特に dhcp にしているネットワークインターフェースが複数あるときとか。
特に調べなかったのですが、インターフェースごとにばらばらに適当に呼ばれてる感じです。
そもそも /etc/init.d/networking が悪いような気もしますが。
ということで、きちんと /etc/hosts を更新するには、ファイルの排他処理をしないといけません。
しかも、フックスクリプトの呼び出しは DHCP の問い合わせをする時だけなので、
google:シェルスクリプト 排他処理とか検索結果の上位を占める多重起動回避とは違ったことをしないといけません。
java や ruby の synchronized が必要ですが、シェルスクリプトでそんなんを作るのはあまり楽しそうじゃありません。
よってスクリプト自体を ruby で書くのがよさそうです。
スクリプトは書き途中だけど、別にいい方法が見つかったらそうするつもり。
結局、こんなかんじになりました。
/etc/dhcp3/dhclient-enter-hooks.d の下に置くものは sh スクリプトじゃないと駄目みたいなので、
そちらは sh です。
#!/usr/bin/ruby require 'logger' class App LOCK_DIR = "/tmp/" + File.basename(__FILE__) + ".lock" ETC_HOSTS = "/etc/hosts" def initialize @log = Logger.new("/var/tmp/#{File.basename($0)}.log") @log.progname = File.basename($0) @log.level = Logger::DEBUG @log.debug("args dump") end def update_etc_hosts ENV.each{|k,v| @log.debug("#{k}=#{v}") } lock_dir = nil begin @log.info("enter critical area") if File.exists?(LOCK_DIR) while ! File.exists?(LOCK_DIR) log.info("another process exists, wait for 1 second") sleep(1000) end end lock_dir = Dir.mkdir(LOCK_DIR) entries = Array.new open(ETC_HOSTS, "r") {|etc_hosts| entries = etc_hosts.readlines } new_etc_hosts = begin @log.info("reason: #{ENV["reason"]}") case ENV["reason"] when "RELEASE" proc_reason_release entries when "BOUND" proc_reason_bound entries else @log.info("nothing to do") end rescue => e @log.error(e) ensure new_etc_hosts.close if new_etc_hosts end if ENV["reason"] =~ /RELEASE|BOUND/ @log.info("diff") open("| /usr/bin/diff #{ETC_HOSTS} /var/tmp/#{Process.pid}_etc_hosts", "r") {|diff| while s = diff.gets @log.info(s) end } File.rename("/var/tmp/#{Process.pid}_etc_hosts", ETC_HOSTS) end ensure @log.info("leave critical area") if lock_dir != nil and File.exists?(LOCK_DIR) Dir.delete(LOCK_DIR) end end end def proc_reason_release(entries) if ENV.key?("old_ip_address") and ENV["old_ip_address"] != "127.0.0.1" @log.info("comment out address: #{ENV["old_ip_address"]}") open("/var/tmp/#{Process.pid}_etc_hosts", "w") {|new_etc_hosts| entries.each {|line| s = line.gsub(/^#{ENV["old_ip_address"]}/, "#" + ENV["old_ip_address"]) new_etc_hosts.puts(s) } } end end def proc_reason_bound(entries) if ENV.key?("new_ip_address") and ENV["new_ip_address"] != "127.0.0.1" open("/var/tmp/#{Process.pid}_etc_hosts", "w") {|new_etc_hosts| entries.map {|line| new_etc_hosts.puts(line) } @log.info("add new address: #{ENV["new_ip_address"]}") open("/etc/hostname", "r") { |etc_hostname| hostname = etc_hostname.gets.chomp new_etc_hosts.puts("#{ENV["new_ip_address"]} #{hostname}") } } end end end if $0 == __FILE__ App.new.update_etc_hosts end