How to run a script when there is a change in your local IP?
According to the man page for NetmorkManager, one of the events is
dhcp4-change
The DHCPv4 lease has changed (renewed, rebound, etc).
I think you can simply change
up)
to
dhcp4-change|up)
I am providing a script that listens on dbus signals, which will allow you to react faster than if you were to poll for changes on your current network configuration. It helps on systems where scripts /etc/ are not executed when you would like them to (like on my 14.04 system).
my enter/exit hooks.d don't work
NetworkManager starts dhclient with the flag -sf /usr/lib/NetworkManager/nm-dhcp-client.action
which seems to override the normal enter/exit hook behaviour. The default behavior with dhclient is to call scripts in /etc/dhcp/dhclient-{enter,exit}-hooks.d
. Those don't get called at all on my system.
my NetworkManager dispatcher.d scripts don't work either
NM does however invoke a different set of scripts, in /etc/NetworkManager/dispatcher.d
, to inform of various events. The NetworkManager (8) man page defines dhcp4-change
and dhcp6-change
actions which would seem to do exactly what you want. Despite what the manpage says, on my system at least, only up
and down
actions get invoked. I can't get those scripts to fire on anything else. So this is not a great avenue to monitor IP changes either.
so, snoop directly on dbus signals emitted by NM
nm-dhcp-client.action
(source), from the command line, simply converts all the environment variables set by dhclient into a dbus signal. Those environment variables are defined in man dhclient-script
(8). One of particular interest is $new_ip_address
. What you could do, as suggested by @Bernhard, is to monitor the signal and act accordingly based on its contents.
Here is a program that will snoop all event data signalled by that binary:
#!/bin/bash -e
#
# This script listens for the org.freedesktop.nm_dhcp_client signal.
# The signal is emitted every time dhclient-script would execute.
# It has the same contents as the environment passed to
# dhclient-script (8). Refer to manpage for variables of interest.
#
# "org.freedesktop.nm_dhcp_client" is an undocumented signal name,
# as far as I could tell. it is emitted by nm-dhcp-client.action,
# which is from the NetworkManager package source code.
#
# detail: todo cleanup subprocess on exit. if the parent exits,
# the subprocess will linger until it tries to print
# at which point it will get SIGPIPE and clean itself.
# trap on bash's EXIT signal to do proper cleanup.
mkfifo /tmp/monitor-nm-change
(
dbus-monitor --system "type='signal',interface='org.freedesktop.nm_dhcp_client'"
) > /tmp/monitor-nm-change &
exec </tmp/monitor-nm-change
rm /tmp/monitor-nm-change
while read EVENT; do
#change this condition to the event you're interested in
if echo "$EVENT" | grep -q BOUND6; then
# do something interesting
echo "current ipv6 addresses:"
ip addr show | grep inet6
fi
done
The output of dbus-monitor is not straightforward to parse in scripts. Perhaps it is easier to trigger on the presence of a certain keyword(s), e.g. new_ip_address
, and from there use different tools to get the information that changed (e.g. ip or ifconfig).
# example output data from dbus-monitor for that signal
...
dict entry(
string "new_routers"
variant array of bytes "192.168.2.11"
)
dict entry(
string "new_subnet_mask"
variant array of bytes "255.255.255.0"
)
dict entry(
string "new_network_number"
variant array of bytes "192.168.2.0"
)
dict entry(
string "new_ip_address"
variant array of bytes "192.168.2.4"
)
dict entry(
string "pid"
variant array of bytes "12114"
)
dict entry(
string "reason"
variant array of bytes "REBOOT"
)
dict entry(
string "interface"
variant array of bytes "eth0"
)
...
Give it a shot!
Polling approach with python script. Basic idea is to continuously parse output of ip -4 -o add show <INTERFACE>
and compare current result with previous iteration
#!/usr/bin/env python3
import subprocess
import sys
def get_ip():
# Simple function that parses output
# of ip command and returns interface ip
# replace wlan7 with your interface
command = 'ip -4 -o addr show wlan7'.split()
ip = None
try:
ip = subprocess.check_output(command).decode().split()[3]
except IndexError:
return
finally:
if ip:
return ip
def main():
# do while loop
# Exits only when change occurs
address = get_ip()
while address == get_ip():
address = get_ip()
# Trigger script once we're out of loop
subprocess.call(['zenity','--info','--text','IP CHANGED'])
if __name__ == '__main__':
# use while loop if yout want this script to run
# continuously
while True:
try:
main()
except KeyboardInterrupt:
sys.exit()