1. create the launchd item in /Library/LaunchDaemons
/usr/bin/sudo /bin/bash -c ' yourname=$(/usr/bin/logname) LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist" EX_MARK='!' /bin/cat > "${LaunchdPlistFile}" <<-EOF <?xml version="1.0" encoding="UTF-8"?> <${EX_MARK}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>Disabled</key> <true/> <key>GroupName</key> <string>${yourname}</string> <key>Label</key> <string>net.${yourname}.wanip.update</string> <key>ProgramArguments</key> <array> <string>/Users/${yourname}/Library/wanip.sh</string> </array> <key>RunAtLoad</key> <true/> <key>StartInterval</key> <integer>20</integer> <key>UserName</key> <string>${yourname}</string> </dict> </plist> EOF ' #------------------------- yourname=$(/usr/bin/logname) LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist" open -e "${LaunchdPlistFile}" ls -l "${LaunchdPlistFile}" /usr/bin/groups /usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}" #/usr/bin/sudo /usr/sbin/chown root:admin "${LaunchdPlistFile}" /usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}" # after creating ~/Library/wanip.sh below /usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null #/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null /usr/bin/sudo /bin/launchctl list /usr/bin/sudo /usr/bin/fs_usage | /usr/bin/egrep -i wanip
2. create a shell script that will be run by the launchd item at the specified intervals in seconds (here: every 20 seconds)
Version 1:
#!/bin/bash # Version 1 with two columns (date, IP address) # cat ~/Library/wanip.sh (/Users/yourname/Library/wanip.sh) # /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh # /bin/chmod 0744 ~/Library/wanip.sh declare last_line_closed last_line_offline last_line_unreachable newfile old_wanip time wanip declare IF='en0' declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt" # try to find your router_wanip_site by surfing to the IP addresses returned by the following commands: # route -n get default | egrep interface | awk '{print $NF}' # ipconfig getoption en0 router # ipconfig getoption en0 domain_name_server declare router_wanip_site='http://xxxx.xx/xxx.htm' #declare router_wanip_site='http://checkip.dyndns.org' # alternative /bin/sleep 3 /usr/sbin/ipconfig waitall if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null if [[ $? -ne 0 ]]; then time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" /usr/bin/logger -i "${time} router_wanip_site is unreachable for wanip.sh" last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")" if [[ -n "$last_line_unreachable" ]]; then exit 0; fi echo "${time} router_wanip_site is unreachable" >> "${wanip_record_file}" exit 0 fi # match first IP address with egrep wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \ /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')" wanip="${wanip// /}" # alternative with sed for matching a line with a characteristic string plus IP address #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \ #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')" time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" if [[ -n "${wanip}" ]]; then old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")" if [[ "${wanip}" == "${old_wanip}" ]]; then exit 0; fi echo "${time} ${wanip}" >> "${wanip_record_file}" else last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")" if [[ -n "${last_line_closed}" ]]; then exit 0; fi echo "${time} connection closed" >> "${wanip_record_file}" fi else last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")" if [[ -n "$last_line_offline" ]]; then exit 0; fi time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" echo "${time} offline" >> "${wanip_record_file}" fi if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then newfile="${wanip_record_file}-$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" /bin/mv "${wanip_record_file}" "${newfile}" fi exit 0
Version 2:
#!/bin/bash # Version 2 has an additional $name column # cat ~/Library/wanip.sh (/Users/yourname/Library/wanip.sh) # /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh # /bin/chmod 0744 ~/Library/wanip.sh declare format last_line_closed last_line_offline last_line_unreachable name newfile old_name old_wanip time wanip declare IF='en0' declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt" # try to find your router_wanip_site by surfing to the IP addresses returned by the following commands: # route -n get default | egrep interface | awk '{print $NF}' # ipconfig getoption en0 router # ipconfig getoption en0 domain_name_server declare router_wanip_site='http://xxxx.xx/xxx.htm' #declare router_wanip_site='http://checkip.dyndns.org' # alternative name="$(/usr/bin/who | /usr/bin/awk '/console/ {print $1}')" name="${name//[[:cntrl:]]/,}" if [[ -z "${name}" ]]; then name='[logout]'; fi old_name="$(/usr/bin/sed -E -n -e '$,$s/^[^ ]+ +([^ ]+) +[^ ].*$/\1/p' "${wanip_record_file}")" format='%-35s%-20s%-20s\n' # for printf #/bin/sleep 3 /usr/sbin/ipconfig waitall if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null if [[ $? -ne 0 ]]; then time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" /usr/bin/logger -i "${time} router_wanip_site is unreachable for wanip.sh" last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")" if [[ -n "$last_line_unreachable" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}" fi exit 0 fi printf "${format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}" exit 0 fi # match first IP address with egrep wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \ /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')" wanip="${wanip// /}" # alternative with sed for matching a line with a characteristic string plus IP address #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \ #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')" time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" if [[ -n "${wanip}" ]]; then old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")" if [[ "${wanip}" == "${old_wanip}" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}" fi exit 0 fi printf "${format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}" else last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")" if [[ -n "${last_line_closed}" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}" fi exit 0 fi printf "${format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}" fi else last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")" time="$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" if [[ -n "$last_line_offline" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${format}" "${time}" "${name}" "offline" >> "${wanip_record_file}" fi exit 0 fi printf "${format}" "${time}" "${name}" "offline" >> "${wanip_record_file}" fi if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then newfile="${wanip_record_file}-$(/bin/date +%Y-%m-%d-%H.%M.%S-%Z)" /bin/mv "${wanip_record_file}" "${newfile}" fi exit 0
Version 3:
/usr/bin/sudo /bin/bash -c ' yourname=$(/usr/bin/logname) LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist" EX_MARK='!' /bin/cat > "${LaunchdPlistFile}" <<-EOF <?xml version="1.0" encoding="UTF-8"?> <${EX_MARK}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>Disabled</key> <true/> <key>GroupName</key> <string>${yourname}</string> <key>Label</key> <string>net.${yourname}.wanip.update</string> <key>ProgramArguments</key> <array> <string>/Users/${yourname}/Library/wanip.sh</string> </array> <key>RunAtLoad</key> <true/> <key>UserName</key> <string>${yourname}</string> </dict> </plist> EOF ' #---------------------------------------------- yourname=$(/usr/bin/logname) LaunchdPlistFile="/Library/LaunchDaemons/net.${yourname}.wanip.update.plist" open -e "${LaunchdPlistFile}" ls -l "${LaunchdPlistFile}" /usr/bin/groups /usr/bin/sudo /usr/sbin/chown root:wheel "${LaunchdPlistFile}" #/usr/bin/sudo /usr/sbin/chown root:admin "${LaunchdPlistFile}" /usr/bin/sudo /bin/chmod 0644 "${LaunchdPlistFile}" # after creating ~/Library/wanip.sh below /usr/bin/sudo /bin/launchctl load -w "${LaunchdPlistFile}" 2>/dev/null #/usr/bin/sudo /bin/launchctl unload -w "${LaunchdPlistFile}" 2>/dev/null /usr/bin/sudo /bin/launchctl list /usr/bin/sudo /usr/bin/fs_usage | /usr/bin/egrep -i wanip #----------------------------------------------------------------------------------------------------- #!/bin/bash # Version 3 is started only once by the launchd item and records shutdown processes # Inspired by: "Re: how to run scripts at shutdown - how does launchd shutdown a system?", # http://lists.apple.com/archives/macos-x-server/2007/Oct/msg00021.html # cat ~/Library/wanip.sh (/Users/yourname/Library/wanip.sh) # /usr/sbin/chown $(/usr/bin/logname):$(/usr/bin/logname) ~/Library/wanip.sh # /bin/chmod 0744 ~/Library/wanip.sh declare last_line_closed last_line_offline last_line_unreachable name declare newfile old_name old_wanip printf_format time time_format wanip declare IF='en0' declare wanip_record_file="/Users/YOURLOGNAME/Library/wanip_record.txt" # try to find your router_wanip_site by surfing to the IP addresses returned by the following commands: # route -n get default | egrep interface | awk '{print $NF}' # ipconfig getoption en0 router # ipconfig getoption en0 domain_name_server declare router_wanip_site='http://xxxx.xx/xxx.htm' #declare router_wanip_site='http://checkip.dyndns.org' # alternative WHILEVAR=1 TRAPSIGNAL= function exit_function() { TRAPSIGNAL='yes' # $! holds the PID of last process that has been started in the background (cmd &) [[ $! -gt $$ ]] && kill -TERM $! } trap exit_function SIGHUP SIGINT SIGTERM time_format='+%Y-%m-%d--%H.%M.%S--%Z' printf_format='%-40s%-20s%-20s\n' while [[ ${WHILEVAR} ]]; do name="$(/usr/bin/who | /usr/bin/awk '/console/ {print $1}')" name="${name//[[:cntrl:]]/,}" if [[ -z "${name}" ]]; then name='[logout]'; fi old_name="$(/usr/bin/sed -E -n -e '$,$s/^[^ ]+ +([^ ]+) +[^ ].*$/\1/p' "${wanip_record_file}")" /usr/sbin/ipconfig waitall /bin/sleep 20 & # time interval wait $! if [[ ${TRAPSIGNAL} ]]; then time="$(/bin/date ${time_format})" printf "${printf_format}" "${time}" "${name}" "shutdown" >> "${wanip_record_file}" exit 0 fi if [[ "$(/sbin/route -n get default | /usr/bin/egrep interface | /usr/bin/awk '{print $NF}')" == "${IF}" ]]; then /usr/bin/curl -I -L -s --max-time 10 "${router_wanip_site}" 1>/dev/null if [[ $? -ne 0 ]]; then time="$(/bin/date ${time_format})" /usr/bin/logger -i "${time} router_wanip_site is unreachable for wanip.sh" last_line_unreachable="$(/usr/bin/sed -E -n -e '$,$s/^.+ (unreachable).*$/\1/p' "${wanip_record_file}")" if [[ -n "$last_line_unreachable" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${printf_format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}" fi continue fi printf "${printf_format}" "${time}" "${name}" "router_wanip_site is unreachable" >> "${wanip_record_file}" continue fi # match first IP address with egrep wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \ /usr/bin/egrep -o -m 1 ' ([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}')" wanip="${wanip// /}" # alternative with sed for matching a line with a characteristic string plus IP address #wanip="$(/usr/bin/curl -L -s --max-time 10 "${router_wanip_site}" | \ #/usr/bin/sed -E -n -e '/STRING: /{s/^.+ ([[:digit:]\.]+).*$/\1/p;q;}')" time="$(/bin/date ${time_format})" if [[ -n "${wanip}" ]]; then old_wanip="$(/usr/bin/sed -E -n -e '$,$s/^.+ ([[:digit:]\.]+).*$/\1/p' "${wanip_record_file}")" if [[ "${wanip}" == "${old_wanip}" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${printf_format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}" fi continue fi printf "${printf_format}" "${time}" "${name}" "${wanip}" >> "${wanip_record_file}" else last_line_closed="$(/usr/bin/sed -E -n -e '$,$s/^.+ (closed).*$/\1/p' "${wanip_record_file}")" if [[ -n "${last_line_closed}" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${printf_format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}" fi continue fi printf "${printf_format}" "${time}" "${name}" "connection closed" >> "${wanip_record_file}" fi else last_line_offline="$(/usr/bin/sed -E -n -e '$,$s/^.+ (offline).*$/\1/p' "${wanip_record_file}")" time="$(/bin/date ${time_format})" if [[ -n "$last_line_offline" ]]; then if [[ "${name}" != "${old_name}" ]]; then printf "${printf_format}" "${time}" "${name}" "offline" >> "${wanip_record_file}" fi continue fi printf "${printf_format}" "${time}" "${name}" "offline" >> "${wanip_record_file}" fi if [[ $(/usr/bin/stat -f %z "${wanip_record_file}") -gt 31457280 ]]; then newfile="${wanip_record_file}-$(/bin/date ${time_format})" /bin/mv "${wanip_record_file}" "${newfile}" fi done exit 0