#!/usr/bin/python # # This file is Copyright (c) 2010 by the GPSD project # BSD terms apply: see the file COPYING in the distribution root for details. # # Hotplug script for gpsd by Eric S. Raymond, March 2005 # This script is part of the gpsd distribution: see http://gpsd.berlios.de # Can be called like "gpsd.hotplug [add|remove] /dev/ttyUSB0" for test # purposes. import sys, time, os, syslog, glob, socket, stat CONTROL_SOCKET = os.getenv('GPSD_SOCKET') or "/var/run/gpsd.sock" GPSD_OPTIONS = os.getenv('GPSD_OPTIONS') or "" WHEREAMI = __file__ def gpsd_control_connect(complain=True): "Acquire a connection to the GPSD control socket." if not os.path.exists(CONTROL_SOCKET): syslog.syslog("socket %s doesn't exist" % CONTROL_SOCKET) return None try: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) sock.connect(CONTROL_SOCKET) except socket.error, msg: if complain: syslog.syslog("socket %s creation failure: %s" % (CONTROL_SOCKET, msg)) if sock: sock.close() sock = None #else: # syslog.syslog("socket %s created OK" % CONTROL_SOCKET) return sock def gpsd_control(action, argument): "Pass a command to gpsd; start the daemon if not already running." syslog.syslog("gpsd_control(action=%s, arg=%s)" % (action, argument)) connect = gpsd_control_connect(complain=False) if connect: syslog.syslog("reached a running gpsd") elif action == 'add': gpsdcmd = "gpsd %s -F %s" % (GPSD_OPTIONS, CONTROL_SOCKET) syslog.syslog("launching %s" % gpsdcmd) os.system(gpsdcmd) connect = gpsd_control_connect(complain=True) if not connect: syslog.syslog("can't reach gpsd") return None # We've got a live connection to the gpsd control socket. No # need to parse the response, because gpsd will lock on to the # device if it's really a GPS and ignore it if it's not. if action == 'add': # Force the group-read & group-write bits on, so gpsd will still be # able to use this device after dropping root privileges. os.chmod(argument, stat.S_IMODE(os.stat(argument)[stat.ST_MODE])|0660) connect.sendall("+%s\r\n" % argument) connect.recv(12) elif action == 'remove': connect.sendall("-%s\r\n" % argument) connect.recv(12) elif action == 'send': connect.sendall("%s\r\n" % argument) connect.recv(12) connect.close() #syslog.syslog("gpsd_control ends") return action def hotplug(action, devpath): #syslog.syslog("ACTION=%s DEVPATH=%s" % (action,devpath)) if not devpath: syslog.syslog("No device") else: subnodes = glob.glob("/sys" + devpath + "/*") subnodes = map(os.path.basename, subnodes) subnodes = filter(lambda s: s.startswith("ttyUSB"), subnodes) if len(subnodes) == 0: syslog.syslog("no ttyUSB device under " + devpath) return elif len(subnodes) > 1: syslog.syslog("too many ttyUSB devices under " + devpath) return else: tty = "/dev/" + subnodes[0] syslog.syslog("waiting for " + tty) while not os.path.exists(tty): time.sleep(1) syslog.syslog(tty + " has gone active") gpsd_control(action, tty) remover = os.getenv("REMOVER") #syslog.syslog("REMOVER=%s" % remover) fp = open(remover, "w") fp.write(WHEREAMI + " remove " + tty) fp.close() os.chmod(remover, stat.S_IRUSR|stat.S_IXUSR|stat.S_IRGRP|stat.S_IXGRP) return if __name__ == '__main__': # In recent versions of udev, the gpsd script runs in series with # the task that creates the real /dev/ttyUSB0 device # node. Unfortunately, the gpsd script runs BEFORE the creation of # the node, and the node is not created until after you kill the # gpsd script, because the gpsd script waits forever for the node # to appear. # # This is a race condition, and is best fixed by running the # actual wait/hotplug portion in the background. pid = os.fork() if not pid: syslog.openlog('gpsd.hotplug', 0, syslog.LOG_DAEMON) try: if len(sys.argv) == 1: # Called as hotplug script hotplug(os.getenv("ACTION"), os.getenv("DEVPATH")) else: # Called by hand for testing gpsd_control(sys.argv[1], sys.argv[2]) except: (exc_type, exc_value, exc_traceback) = sys.exc_info() syslog.syslog("gpsd.hotplug: exception %s yields %s" % (exc_type, exc_value)) raise exc_type, exc_value, exc_traceback #syslog.syslog("gpsd.hotplug ends") syslog.closelog()