From 3177bc9e67077cdaa2769cf44a8d539528ca4a3f Mon Sep 17 00:00:00 2001 From: James Meyer Date: Wed, 9 Jan 2013 16:40:57 -0600 Subject: LinHES-config: autocard.py Added the ability to insert cards into the database. Fixed a few bugs for dvb and hdhr refs #892 --- abs/core/LinHES-config/PKGBUILD | 4 +- abs/core/LinHES-config/autocard.py | 503 +++++++++++++++++++++++++++++++++---- 2 files changed, 460 insertions(+), 47 deletions(-) diff --git a/abs/core/LinHES-config/PKGBUILD b/abs/core/LinHES-config/PKGBUILD index 2ebafe8..f09662c 100755 --- a/abs/core/LinHES-config/PKGBUILD +++ b/abs/core/LinHES-config/PKGBUILD @@ -1,6 +1,6 @@ pkgname=LinHES-config pkgver=2.3 -pkgrel=205 +pkgrel=206 conflicts=(MythVantage-config MythVantage-config-dev LinHES-config-dev LinHes-config ) pkgdesc="Install and configure your system" depends=('bc' 'libstatgrab' 'mysql-python' 'expect' 'curl' 'dnsutils' 'parted' @@ -150,7 +150,7 @@ md5sums=('26e20dba8d1bea96a07131ef945fd2f7' 'ab2aa42c2947148c2b1cac0ade6d1d55' 'd429b59d3cfb37b0624c6a4a71a7c2c0' 'f6a935c35123fdc7c259c01cbc794a64' - '656625c8fee50f5b1d8d29eaa80c5982' + 'd36d2e1a363e94bb8ba1e724c8d982fc' 'd3490e93f313bbbcbc6c3693c56b9c17' '3d1e4a119f38cff0498bf8a67e94e4b3' '71fd2d0d448fc2fcd15415a1beed7109' diff --git a/abs/core/LinHES-config/autocard.py b/abs/core/LinHES-config/autocard.py index eda2286..3d11fe0 100644 --- a/abs/core/LinHES-config/autocard.py +++ b/abs/core/LinHES-config/autocard.py @@ -11,16 +11,95 @@ 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.find_description() + 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 @@ -46,7 +125,7 @@ class v4l_tuners(): if pos >=0: splitline= line.split(':',1) Businfo=splitline[1].strip() - return Cardtype + return Cardtype,Driver def parse_full_udev(self): udev_props = [] @@ -97,6 +176,11 @@ class v4l_tuners(): 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='' @@ -134,8 +218,72 @@ class v4l_tuners(): #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 @@ -148,6 +296,8 @@ class v4l_tuners(): 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 @@ -164,18 +314,61 @@ class v4l_tuners(): 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/model' %self.device + 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 @@ -187,17 +380,26 @@ class hdhr_tuners(): 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): @@ -259,37 +461,26 @@ class dvb_tuners(): 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() - 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('''"''') + 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(".", "") - tuner_hash="%s%s%s" %(card_type,k,i) + 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 ): @@ -298,11 +489,52 @@ class dvb_tuners(): return text def create_udev_rule(self): - #ACTION=="add",KERNEL=="dvb?.frontend?",SUBSYSTEM=="dvb",SUBSYSTEMS=="pci",DRIVERS=="saa7164",KERNELS=="0000:05:00.0",RUN+="/tmp/udev_link.sh dvb_5 $env{DEVNAME}" 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 @@ -320,9 +552,32 @@ class dvb_tuners(): 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" @@ -333,8 +588,10 @@ def gather_hdhr(tuner_list): print "HDHOMERUN not detected" else: for line in lines: - hdrdevice=line.strip().split()[2] - tuner_list.append(hdhr_tuners(hdrdevice)) + 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 @@ -360,7 +617,6 @@ def gather_v4l(tuner_list): print "no v4l cards found" return tuner_list - def gather_dvb(tuner_list): try: filelist = os.listdir('/dev/dvb/') @@ -376,7 +632,7 @@ def gather_dvb(tuner_list): except: print "no dvb cards found" return tuner_list - +#------------------END OF TUNER GATHERING----------------- def gather_tuners(): @@ -385,8 +641,17 @@ def gather_tuners(): 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]) - #for i in tuner_list: #if i.get_card_type() == "hdhr": #pass #print "hdhr : %s " %(i.get_dev_node()) @@ -429,6 +694,16 @@ def gather_tuners(): #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' @@ -443,6 +718,7 @@ def write_udev_file(rule_list): f.close() except: print "Error creating %s" %udevfile + trigger_udev() def write_udev_map(tuner_list): udevfile = '/etc/udev/mv-persistent-video.description' @@ -455,21 +731,27 @@ def write_udev_map(tuner_list): for i in tuner_list: if i.get_card_type() == "hdhr": pass - line="hdhr:%s:%s" %(i.get_description(),i.get_dev_node()) + 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:/dev/vstatic/%s " %(i.get_description(),i.get_tuner_hash()) + 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:/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:/dev/dvb/adapter_static_%s " %(i.get_description(),i.get_tuner_hash()) + line="dvb:%s: %s " %(i.get_description(),i.get_static_device()) f.write(line) f.write("\n") elif i.get_card_type() == "ceton": @@ -485,26 +767,157 @@ def write_udev_map(tuner_list): 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): - tuner_list = gather_tuners() - rule_list = [] + listcards = False + udev = False + insertdb = False - for i in tuner_list: - rule_list.append(i.get_udev_rule()) + if argv==[] or "help" in argv: + usuage() + sys.exit(0) - write_udev_file(rule_list) - write_udev_map(tuner_list) + 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() -if __name__ == "__main__": - main(sys.argv[1:]) + 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") -- cgit v0.12