Auto-restart Raspberry Pi when its target server is not reachable

Raspberry Pi logo

My security camera has been running great for months. Issues have arisen only when the wireless router gets restarted or the wireless connection gets dropped for some other reason. The problem is that the Raspberry Pi doesn't automatically reconnect after a wlan connection is dropped. In that case, I usually had to unplug the RPi to restart it manually, because I couldn't reach it via ssh any longer.

When searching for a solution to my problem, I came across this post: https://www.raspberrypi.org/forums/viewtopic.php?t=16054. It provides a bash script to reset the WLAN connection after it gets dropped. I tried a version of it that I scheduled to run with cron. However, it still didn't solve the Raspberry Pi's problem with reconnecting to the "mother server" after a network issue.

The solution that has been working for a while now is to have the RPi ping its target server on a regular basis. If the ping is unsuccessful, it restarts (in the hope that this will re-establish the network connection.) The Python program I pasted below performs the pinging, logs the results to a file for later inspection, and performs a system reboot if necessary. The program runs once every 30 minutes, as defined in a crontab.

The Python code: networkchecker.py

  1. #!/usr/bin/env python
  2. #
  3. # networkChecker
  4. #
  5. # Checks whether a server is available to the Raspberry Pi.
  6. # If it's not available, the RasPi is told to restart
  7. # To be run periodically (cron job)
  8. #
  9. # AJH 2014
  10.  
  11.  
  12. import argparse
  13. import os
  14. from datetime import datetime
  15. import logging
  16.  
  17.  
  18. def check_for_sudo():
  19. """Check whether we are dealing with the root user."""
  20. if os.geteuid() == 0:
  21. return True
  22. else:
  23. return False
  24.  
  25. def get_current_time():
  26. d = datetime.now()
  27. time = {}
  28. time["timestring_long"] = "{0}-{1:02d}-{2:02d} at {3:02d}:{4:02d}:{5:02d}".format(d.year, d.month, d.day, d.hour, d.minute, d.second)
  29. time["timestring_short"] = "{:02d}:{:02d}:{:02d}".format(d.hour, d.minute, d.second)
  30. return time
  31.  
  32. def setup_logging(logfilename):
  33. # Set up a log file
  34. logging.basicConfig(filename=str(logfilename), format='%(levelname)s:%(message)s', level=logging.INFO)
  35. ct = get_current_time()
  36. logging.debug(" networkChecker -- START -- {}".format(ct['timestring_long']))
  37.  
  38. def ping_the_server(args):
  39. """Send a ping to the server"""
  40. cmd = "ping -q -c {} {}".format(args.n, args.host)
  41. response = os.system(cmd)
  42. logging.debug("Sending command: {}".format(cmd))
  43. return response
  44.  
  45. def main(args):
  46. """Check whether server is up"""
  47. # set up log file
  48. if not args.logfile:
  49. setup_logging("{}/networkchecker.log".format(os.getcwd()))
  50. else:
  51. setup_logging(args.logfile)
  52. is_sudo = check_for_sudo()
  53. if is_sudo:
  54. logging.debug("Running as sudo user")
  55. if not is_sudo:
  56. logging.debug("Not running as sudo user")
  57. # send a ping to the server
  58. response = ping_the_server(args)
  59. if response == 0:
  60. ct = get_current_time()
  61. logging.info("{0} is up. ({1})".format(args.host, ct['timestring_long']))
  62. #everything is fine, don't do anything else
  63. else:
  64. logging.info("{0} is not reachable".format(args.host))
  65. # Restart Raspberry Pi when host is not reachable;
  66. # this should hopefully make network drive available again
  67. if is_sudo and args.restart:
  68. ct = get_current_time()
  69. logging.info("System shutting down now for restart at {}".format(ct['timestring_long']))
  70. try:
  71. os.system("sudo shutdown -r now") # this has to be run as sudo
  72. except:
  73. logging.info("Shutdown command unsuccessful.")
  74. elif args.restart and not is_sudo:
  75. logging.debug("Not shutting down. Sudo permissions required")
  76. ct = get_current_time()
  77. logging.debug(" networkChecker -- STOP -- {}".format(ct['timestring_long']))
  78.  
  79.  
  80. if __name__ == '__main__':
  81. parser = argparse.ArgumentParser(description='Network Checker')
  82. parser.add_argument('-s', '--server', dest='host', help="Server to ping at regular intervals")
  83. parser.add_argument('-n', '--number', dest='n', default=10, help="Number of pings to send")
  84. parser.add_argument('-r', '--restart', dest='restart', help="Restart RasPi if server is down", action="store_true")
  85. parser.add_argument('-l', '--log', dest='logfile', help="Log file name and path")
  86. args = parser.parse_args()
  87. main(args)

Notes: You can change the logging level in line 34 from level=logging.INFO to level=logging.DEBUG to display more detailed information during setup.

Using cron to run the command on a regular basis

After placing the script onto the Raspberry Pi, I set up a cron job to run it every 30 minutes. This has to be performed for the sudo crontab, because it involves a restart command that would otherwise fail. The command sudo crontab -e opens the sudo cron file in an editor (usually nano).

At the bottom of the file, add the line shown here if it should be run every 30 minutes:

# m h  dom mon dow   command
*/30 * * * * /home/pi/bin/networkChecker.py -s SERVER_IP_ADDRESS -n 20 -l LOG_FILE_LOCATION -r

Note that the full path to the script is given (/home/pi/bin/networkChecker.py, in my case) because path variables are set differently for the sudo user. The script might not be found if the program location is not in the sudo PATH variable. SERVER_IP_ADDRESS is the one that's going to be pinged. The "-n" parameter defines how many pings to send, and "-l" defines the log file path and name. (E.g. /var/log/networkchecker.log; make sure it's writeable for the process that executes the script.) Adding the "-r" parameter specifies that a reboot is going to be performed. Otherwise, actions will only be logged. (Useful for debugging.)

Category: 
Code
File attachments: