#!/usr/bin/python2 # import MySQL module #jm import MySQLdb import sys import getopt import socket import os , re import time import string import glob from string import letters from string import digits from xml.dom.minidom import parseString import errno, fcntl import urllib2,urllib #from httplib import HTTP #from urlparse import urlparse #http://code.activestate.com/recipes/576891/ class ApplicationLock: ''' Ensures application is running only once, by using a lock file. Ensure call to lock works. Then call unlock at program exit. You cannot read or write to the lock file, but for some reason you can remove it. Once removed, it is still in a locked state somehow. Another application attempting to lock against the file will fail, even though the directory listing does not show the file. Mysterious, but we are glad the lock integrity is upheld in such a case. Instance variables: lockfile -- Full path to lock file lockfd -- File descriptor of lock file exclusively locked ''' def __init__ (self, lockfile): self.lockfile = lockfile self.lockfd = None def lock (self): ''' Creates and holds on to the lock file with exclusive access. Returns True if lock successful, False if it is not, and raises an exception upon operating system errors encountered creating the lock file. ''' try: # # Create or else open and trucate lock file, in read-write mode. # # A crashed app might not delete the lock file, so the # os.O_CREAT | os.O_EXCL combination that guarantees # atomic create isn't useful here. That is, we don't want to # fail locking just because the file exists. # # Could use os.O_EXLOCK, but that doesn't exist yet in my Python # self.lockfd = os.open (self.lockfile, os.O_TRUNC | os.O_CREAT | os.O_RDWR) # Acquire exclusive lock on the file, but don't block waiting for it fcntl.flock (self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB) # Writing to file is pointless, nobody can see it os.write (self.lockfd, "My Lockfile") return True except (OSError, IOError), e: # Lock cannot be acquired is okay, everything else reraise exception if e.errno in (errno.EACCES, errno.EAGAIN): return False else: raise def unlock (self): try: # FIRST unlink file, then close it. This way, we avoid file # existence in an unlocked state os.unlink (self.lockfile) # Just in case, let's not leak file descriptors os.close (self.lockfd) except (OSError, IOError), e: # Ignore error destroying lock file. See class doc about how # lockfile can be erased and everything still works normally. pass #------- START OF TUNER CLASSES----------- class v4l_tuners(): def __init__(self, device): self.device = device self.full_attribs = self.find_full_attribs() self.full_udev_attribs = self.find_full_udev_attribs() self.description,self.driver = self.find_description() self.udev_props = self.parse_full_udev() self.tuner_hash = self.find_tuner_hash() self.udev_rule = self.create_udev_rule() #self.staticdevice is set in create_udev_rule def find_full_attribs(self): cmd = 'v4l2-ctl -D -d' + self.device return os.popen(cmd).readlines() def find_full_udev_attribs(self): cmd = 'udevadm info -a -p $(udevadm info -q path -n %s)' %self.device return os.popen(cmd).readlines() def find_description(self): for line in self.full_attribs: #print line pos = string.find(line,"Driver name") if pos >=0: splitline= line.split(':') Driver=splitline[1].strip() pos = string.find(line,"Card type") if pos >=0: splitline= line.split(':') Cardtype=splitline[1].strip() pos = string.find(line,"Bus info") if pos >=0: splitline= line.split(':',1) Businfo=splitline[1].strip() return Cardtype,Driver def parse_full_udev(self): udev_props = [] #ATTRS{serial}=="00A2023E"\n', #SUBSYSTEMS=="usb"\n', #SUBSYSTEM=="video4linux"\n', #KERNELS=="0000:08:08.0"\n', #DRIVERS=="hdpvr"\n' #DRIVERS=="ivtv"\n', #ATTR{index}=="0" match = ['SUBSYSTEM', 'SUBSYSTEMS', 'DRIVERS', 'KERNELS', 'ATTRS{serial}', 'ATTR{name}', 'ATTR{index}' ] for item in match: regex = re.compile('%s.*$' %item) for line in self.full_udev_attribs: m = regex.search(line.strip()) if m: #print line udev_props.append(line.strip()) break #throw out kernels for usb subsystems. if 'SUBSYSTEMS=="usb"' in udev_props: #print "found usb" new_udev_props=[] for index, item in enumerate(udev_props): #print item,index if item.startswith("DRIVERS=="): continue elif item.startswith("KERNELS=="): continue else: new_udev_props.append(item) udev_props = new_udev_props return udev_props def is_hdpvr(self): if self.description == "Hauppauge HD PVR": return True return False def is_mpeg(self): if self.driver in ["ivtv","saa7164[0]","saa7164"]: return True return False def find_tuner_hash(self): #tuner hash will be driven by card_type pci id and index i='' k='' tuner_hash='' card_type = self.get_card_type() if card_type == "v4l_hdpvr": for index, item in enumerate(self.udev_props): if item.startswith("ATTRS{serial}=="): hex_serial=item.split("==")[1].strip('''"''') serial = int(hex_serial, 16) tuner_hash="hdpvr_%s" %serial else: for index, item in enumerate(self.udev_props): if item.startswith("KERNELS=="): k=item.split("==")[1].strip('''"''') k=k.replace(":", "_") k=k.replace("0", "") k=k.replace(".", "") if item.startswith("ATTR{index}=="): i=item.split("==")[1].strip('''"''') tuner_hash="%s%s%s" %(card_type,k,i) return tuner_hash def escapeSpecialCharacters ( self, text, characters ): for character in characters: text = text.replace( character, '\\' + character ) return text def create_udev_rule(self): #DRIVERS=="ivtv", ATTR{name}=="ivtv? encoder MPG", KERNELS=="0000:08:09.0" SYMLINK+="myth/aivtv2" line = ','.join(self.udev_props) line += ' SYMLINK+="vstatic/%s"' %(self.get_tuner_hash()) self.staticdevice = "/dev/vstatic/%s" %(self.get_tuner_hash()) return self.escapeSpecialCharacters(line,'[]') def card_mpeg_values(self): values = { 'CardType' : 'MPEG' , 'VideoDevice' : self.staticdevice, 'Defaultinput' : 'Television', 'HostName' : self.localhostname, 'dvb_wait_for_seqstart' :'1', 'signal_timeout' :'1000', 'channel_timeout' :'12000', 'diseqcid' :'NULL', 'dvb_eitscan' :'1' } return values def card_hdpvr_values(self): values = { 'CardType' : 'HDPVR' , 'VideoDevice' : self.staticdevice, 'Defaultinput' : 'Television', 'HostName' : self.localhostname, 'dvb_wait_for_seqstart' :'1', 'signal_timeout' :'1000', 'channel_timeout' :'12000', 'diseqcid' :'NULL', 'dvb_eitscan' :'1' } return values def do_insert(self,values): try: header = { 'User-Agent' : "autocard" } url="http://127.0.0.1:6544/Capture/AddCaptureCard/" data = urllib.urlencode(values) req = urllib2.Request(url, data, header) self.response = urllib2.urlopen(req) except: print " **Insert of %s failed" %self.staticdevice def insert_myth(self,myth_tuner_list): #check if tuner is already in the db if self.staticdevice in myth_tuner_list: print " *%s %s already in mythdb" %(self.get_card_type(),self.staticdevice) else: #determine values to use based on card type print " Inserting %s" %self.staticdevice if self.is_hdpvr() == True: values = self.card_hdpvr_values() self.do_insert(values) elif self.is_mpeg() == True: values = self.card_mpeg_values() self.do_insert(values) else: print " Unknown V4l type, not adding to myth database" return def set_hostname(self,hostname): self.localhostname = hostname def set_tuner_index(self,index): #only used by dvb, but here to be complete self.tuner_index = index def get_dev_node(self): return self.device def get_full_attribs(self): return self.full_attribs def get_full_udev_attribs(self): return self.full_udev_attribs def get_card_type(self): if self.is_hdpvr() == True : rc = "v4l_hdpvr" elif self.is_mpeg() == True: rc = "v4l_mpeg" else: rc = "v4l" return rc def get_description(self): return self.description def get_udev_probs(self): return self.udev_props def get_tuner_hash(self): return self.tuner_hash def get_udev_rule(self): return self.udev_rule def get_static_device(self): return self.staticdevice #----- class hdhr_tuners(): def __init__(self, device): self.device = device self.description = self.find_description() self.staticdevice = device def find_description(self): command = '/usr/bin/hdhomerun_config %s get /sys/hwmodel' %self.device results=os.popen(command,'r') return results.readline().strip() def insert_myth(self,hdhr_list): if self.device in hdhr_list: print " *hdhr %s already in mythtdb" %self.device else: print " Inserting HDHR %s" %self.device self.do_insert() pass def do_insert(self): try: header = { 'User-Agent' : "autocard" } url="http://127.0.0.1:6544/Capture/AddCaptureCard/" values = self.card_hdhr_values() data = urllib.urlencode(values) req = urllib2.Request(url, data, header) self.response = urllib2.urlopen(req) except: print " **Insert of %s failed" %self.device def card_hdhr_values(self): values = { 'CardType' : 'HDHOMERUN' , 'VideoDevice' : self.device, 'Defaultinput' : 'Television', 'HostName' : self.localhostname, 'dvb_wait_for_seqstart' :'1', 'signal_timeout' :'1000', 'channel_timeout' :'3000', 'diseqcid' :'NULL', 'dvb_eitscan' :'1' } return values def set_hostname(self,hostname): self.localhostname = hostname def set_tuner_index(self,index): #only used by dvb, but here to be complete self.tuner_index = index def get_dev_node(self): return self.device def get_card_type(self): return "hdhr" def get_description(self): return self.description def get_udev_rule(self): return def get_tuner_hash(self): return self.device def get_static_device(self): return self.staticdevice #----- class dvb_tuners(): def __init__(self, device): self.device = device self.tuner_index = 1 self.dvb_number = self.find_dvb_number() self.description = self.find_description() self.full_udev_attribs = self.find_full_udev_attribs() self.udev_props = self.parse_full_udev() self.tuner_hash = self.find_tuner_hash() self.udev_rule = self.create_udev_rule() #self.staticdevice is set in create_udev_rule def find_description(self): command = '/usr/bin/dvb-fe-tool -g -a %s' %self.dvb_number results=os.popen(command,'r') line = results.readline().strip() d = line.split('''(''') return d[0] def find_full_udev_attribs(self): cmd = 'udevadm info -a -p $(udevadm info -q path -n %s)' %self.device return os.popen(cmd).readlines() def find_dvb_number(self): #/dev/dvb/adapter2/frontend0 d=self.device.split("/")[3] dvb_number=d.partition("adapter")[2] return dvb_number def parse_full_udev(self): udev_props = ['KERNEL=="dvb?.frontend?"'] #ATTRS{serial}=="00A2023E"\n', #SUBSYSTEMS=="usb"\n', #SUBSYSTEM=="video4linux"\n', #KERNELS=="0000:08:08.0"\n', #DRIVERS=="hdpvr"\n' #DRIVERS=="ivtv"\n', #ATTR{index}=="0" #KERNEL=="dvb1.frontend0" match = ['SUBSYSTEM', 'SUBSYSTEMS', 'DRIVERS', 'KERNELS', 'ATTRS{serial}', 'ATTR{name}', 'ATTR{index}' ] for item in match: regex = re.compile('%s.*$' %item) for line in self.full_udev_attribs: m = regex.search(line.strip()) if m: #print line udev_props.append(line.strip()) break #throw out kernels for usb subsystems. if 'SUBSYSTEMS=="usb"' in udev_props: #print "found usb" new_udev_props=[] for index, item in enumerate(udev_props): #print item,index if item.startswith("DRIVERS=="): continue elif item.startswith("KERNELS=="): continue else: new_udev_props.append(item) udev_props = new_udev_props return udev_props def find_tuner_hash(self): #tuner hash will be driven by card_type pci id and index i='' k='' tuner_hash='' card_type = self.get_card_type() for index, item in enumerate(self.udev_props): if item.startswith("KERNELS=="): k=item.split("==")[1].strip('''"''') k=k.replace(":", "_") k=k.replace("0", "") k=k.replace(".", "") if item.startswith("ATTR{index}=="): i=item.split("==")[1].strip('''"''') tuner_hash="%s%s%s" %(card_type,k,i) return tuner_hash def escapeSpecialCharacters ( self, text, characters ): for character in characters: text = text.replace( character, '\\' + character ) return text def create_udev_rule(self): line = ','.join(self.udev_props) line += ' RUN+="/usr/MythVantage/bin/udev_link.sh %s $env{DEVNAME} "' %(self.get_tuner_hash()) self.staticdevice = "/dev/dvb/adapter_static_%s_%s/frontend0" %(self.get_tuner_hash(),self.tuner_index) return self.escapeSpecialCharacters(line,'[]') def insert_myth(self,myth_tuner_list): if self.staticdevice in myth_tuner_list: print " *dvb %s already in mythtdb" %self.staticdevice else: print " Inserting DVB Tuner %s" %self.staticdevice self.do_insert() pass def do_insert(self): try: header = { 'User-Agent' : "autocard" } url="http://127.0.0.1:6544/Capture/AddCaptureCard/" values = self.card_dvb_values() data = urllib.urlencode(values) req = urllib2.Request(url, data, header) self.response = urllib2.urlopen(req) except: print " **Insert of %s failed" %self.staticdevice def card_dvb_values(self): values = { 'CardType' : 'DVB' , 'VideoDevice' : self.staticdevice, 'Defaultinput' : 'Television', 'HostName' : self.localhostname, 'dvb_wait_for_seqstart' :'1', 'signal_timeout' :'1000', 'channel_timeout' :'12000', 'diseqcid' :'NULL', 'dvb_eitscan' :'1' } return values def set_tuner_index(self,index): self.tuner_index = index self.tuner_hash = self.find_tuner_hash() self.udev_rule = self.create_udev_rule() def set_hostname(self,hostname): self.localhostname = hostname def get_dev_node(self): return self.device def get_card_type(self): return "dvb" def get_description(self): return self.description def get_full_udev_attribs(self): return self.full_udev_attribs def get_tuner_hash(self): return self.tuner_hash def get_udev_rule(self): return self.udev_rule def get_static_device(self): return self.staticdevice #------------------END TUNER CLASSES----------------------- #------------------START OF TUNER GATHERING----------------- def find_hdhr_tuner(hdhrdevice): htuner_list=[] command="/usr/bin/hdhomerun_config %s get /tuner%s/status" #loop over 4 possible tuners for i in range(4): c=command %(hdhrdevice,i) results=os.popen(c,'r') lines=results.readlines() try: #if the first line starts with error, assume it's not a tuner and #there are no more tuners for this device. if lines[0].strip().startswith("ERROR"): break else: t="%s-%s" %(hdhrdevice,i) htuner_list.append(t) except: pass return htuner_list def gather_hdhr(tuner_list): command="/usr/bin/hdhomerun_config --discover" results=os.popen(command,'r') lines=results.readlines() try: if lines[0].strip().split()[0] == "no": print "HDHOMERUN not detected" else: for line in lines: hdhrdevice=line.strip().split()[2] tuners = find_hdhr_tuner(hdhrdevice) for t in tuners: tuner_list.append(hdhr_tuners(t)) except: print "Error finding HDHOMERUN" return tuner_list def gather_v4l(tuner_list): try: filelist = os.listdir('/dev/v4l/by-path/') except OSError: pass #fakelist=['/dev/v4l/video3', 'ivtv', 'WinTV PVR 500 (unit #2)', '0000:04:09.0'] #cardlist.append(fakelist) try: p = re.compile('^/dev/video?\d$') filelist = glob.glob("/dev/v4l/by-path/*video*") for device in filelist: Device=os.path.realpath(device) if not p.search(Device): continue #print "real device node" #print Device tuner_list.append(v4l_tuners(Device)) except IOError: print "no v4l cards found" return tuner_list def gather_dvb(tuner_list): try: filelist = os.listdir('/dev/dvb/') except OSError: pass try: for d in filelist: if os.path.islink("/dev/dvb/"+d): continue Device=os.path.realpath("/dev/dvb/"+d+"/frontend0") tuner_list.append(dvb_tuners(Device)) except: print "no dvb cards found" return tuner_list #------------------END OF TUNER GATHERING----------------- def gather_tuners(): tuner_list = [] tuner_list = gather_hdhr(tuner_list) tuner_list = gather_v4l(tuner_list) tuner_list = gather_dvb(tuner_list) #tuner_list = gather_ceton(tuner_list) #set the number of times we have seen a specific tuner_hash #This will be used by dvb when tuner_hashs conflict tuner_count = {} for i in tuner_list: th = i.get_tuner_hash() if th in tuner_count: tuner_count[th] += 1 else: tuner_count[th] = 1 i.set_tuner_index(tuner_count[th]) #if i.get_card_type() == "hdhr": #pass #print "hdhr : %s " %(i.get_dev_node()) #print "\t" , i.get_description() #elif i.get_card_type() == "v4l": #pass #print "V4L : %s " %(i.get_dev_node()) #print "\t" , i.get_description() ##print "\t" , i.get_udev_probs() #print "\t" , i.get_tuner_hash() ##print "\t" , i.get_udev_rule() ##print "\t" , i.get_full_udev_attribs() #elif i.get_card_type() == "v4l_hdpvr": #pass #print "hdpvr : %s " %(i.get_dev_node()) #print "\t" , i.get_description() ##print "\t" , i.get_udev_probs() #print "\t" , i.get_tuner_hash() ##print "\t" , i.get_udev_rule() ##print "\t" , i.get_full_udev_attribs() #elif i.get_card_type() == "dvb": #pass #print "dvb : %s " %(i.get_dev_node()) #print "\t" , i.get_description() #print "\t" , i.get_tuner_hash() ##print "\t" , i.get_full_udev_attribs() ##for y in i.get_full_udev_attribs(): ## print y ##print "\t" , i.get_udev_rule() #elif i.get_card_type() == "ceton": #print "ceton : %s " %(i.get_dev_node()) #else: #print i return tuner_list def trigger_udev(): print "Reloading udev rules" command = '/usr/bin/udevadm control --reload' results=os.popen(command,'r') print "Triggering udev events" command = '/usr/bin/udevadm trigger' results=os.popen(command,'r') def write_udev_file(rule_list): udevfile = '/etc/udev/rules.d/99-mv-persistent-video.rules' try: f = open(udevfile, 'w') line = "#Do not edit this file, it is auto generated by autocard.py \n\n" f.write(line) for rule in rule_list: if rule: line = "%s \n" %(rule) f.write(line) f.close() except: print "Error creating %s" %udevfile trigger_udev() def write_udev_map(tuner_list): udevfile = '/etc/udev/mv-persistent-video.description' try: f = open(udevfile, 'w') line = "#Do not edit this file, it is auto generated by autocard.py \n" f.write(line) line = "#This file is used to generate the static tuner card web page\n" f.write(line) for i in tuner_list: if i.get_card_type() == "hdhr": pass line="hdhr:%s:%s" %(i.get_description(),i.get_static_device()) f.write(line) f.write("\n") elif i.get_card_type() == "v4l_mpeg": pass line="V4L_mpeg:%s: %s " %(i.get_description(),i.get_static_device()) f.write(line) f.write("\n") elif i.get_card_type() == "v4l": pass line="V4L:%s:%s " %(i.get_description(),i.get_static_device()) f.write(line) f.write("\n") elif i.get_card_type() == "v4l_hdpvr": pass #line="hdpvr:%s:/dev/vstatic/%s " %(i.get_description(),i.get_tuner_hash()) line="hdpvr:%s: %s " %(i.get_description(),i.get_static_device()) f.write(line) f.write("\n") elif i.get_card_type() == "dvb": line="dvb:%s: %s " %(i.get_description(),i.get_static_device()) f.write(line) f.write("\n") elif i.get_card_type() == "ceton": line="ceton:%s:/dev/vstatic/%s " %(i.get_description(),i.get_tuner_hash()) f.write(line) f.write("\n") else: print i line = i f.write(line) f.write("\n") except: print "Error creating %s" %udevfile f.close() def checkURL(url): try: connection = urllib2.urlopen(url,None,3) code=connection.getcode() connection.close() if code == 200: rc=0 #except urllib2.HTTPError, e: except: rc=1 return rc def getURL(url): dom = None try: connection = urllib2.urlopen(url,None,3) data = connection.read() connection.close() dom = parseString(data) #except urllib2.HTTPError, e: except: pass return dom def find_hdhr_in_use(): hdhr_list = [] url='http://127.0.0.1:6544/Capture/GetCaptureCardList?CardType=HDHOMERUN' data = getURL(url) if data: xmlTags = data.getElementsByTagName('VideoDevice') for i in xmlTags: xmlTag=i.toxml() xmlData=xmlTag.replace('','').replace('','') hdhr_list.append(xmlData) return hdhr_list def find_in_use_card_list(): in_use_tuner_list=[] url='http://127.0.0.1:6544/Capture/GetCaptureCardList?HostName=%s' %localhostname data = getURL(url) if data: xmlTags = data.getElementsByTagName('VideoDevice') for i in xmlTags: xmlTag=i.toxml() xmlData=xmlTag.replace('','').replace('','') in_use_tuner_list.append(xmlData) return in_use_tuner_list def usuage(): help=''' Autocard.py is a program that will take the guess work out of setting up tuner/capture cards. There are 4 options: help : This help screen print: Will find and printout any detected capture cards, including network based tuners like the hdhomerun_config udev : This option creates a set of static device nodes for the local capture cards. Rules are based on pci/usb path so moving the card into a different expansion slot will nullify the udev rule HDPVR devices use the serial number as the primary key for the udev rule_list insertdb : This option will insert any detected cards into the MythTV database using the static device generated by udev. This option will also generate udev rules. Tuners that are already defined will not be readded. all : The same as using print udev insertdb EX: autocard.py print autocard.py insertdb ''' print help def main(argv): listcards = False udev = False insertdb = False if argv==[] or "help" in argv: usuage() sys.exit(0) if "print" in argv: listcards = True if "udev" in argv: udev = True if "insertdb" in argv: insertdb = True udev = True if "all" in argv: udev = True insertdb = True listcards = True tuner_list = gather_tuners() rule_list = [] for i in tuner_list: #setting the hostname for each tuner i.set_hostname(localhostname) rule_list.append(i.get_udev_rule()) if listcards == True: print i.get_card_type() print "----------------" print "\t" , i.get_description() print "\t" , i.get_static_device() print("Writing out tuner map") write_udev_map(tuner_list) if udev == True: print("Writing out udev rules") write_udev_file(rule_list) if insertdb == True: be_ready = False i = 0 while i < 10 or be_ready : if checkURL('http://127.0.0.1:6544') == 0: be_ready = True break i = i + 1 print "Waiting for MythTV backend connection: %s/10" %i time.sleep(5) if be_ready == True : #find all HDHR in_use_hdhr_list = find_hdhr_in_use() #find other cards local in_use_local_card_list = find_in_use_card_list() print "Adding cards to mythtv database" for i in tuner_list: if i.get_card_type() == "hdhr": i.insert_myth(in_use_hdhr_list) else: i.insert_myth(in_use_local_card_list) if __name__ == "__main__": localhostname = socket.gethostname() applock = ApplicationLock ('/var/run/autocard.lock') if (applock.lock ()): #print ("Obtained lock") main(sys.argv[1:]) #print ("Unlocking") applock.unlock () else: print ("Unable to obtain lock, exiting")