#!/usr/bin/python # -*- coding: utf-8 -*- # coding=UTF-8 # iTalc master launcher using avahi # Written by Stéphane Graber from xml.dom import minidom import subprocess, re, socket, os, sys # The md5 module is deprecated in Python 2.5 try: from hashlib import md5 except ImportError: from md5 import md5 def getValueFromConfigFile(filename,key,default): file=open(filename) for line in file: if line.startswith(key): try: param=line.split("=")[1] param=param.strip() if not param.isdigit(): param=param.strip('"') except: param=default file.close() return param def getLocalIPs(): "Scan ifconfig output for local IPV4 addresses" # Saving current LANG SYSTEM_LANG=os.environ["LANG"] # Apparently ifconfig has localizations according to the following line, # but with LANG=es_ES.UTF-8 the output looks the same as LANG=C os.environ["LANG"]="C" # Set environ LANG to C ip=[] output=subprocess.Popen("/sbin/ifconfig",stdout=subprocess.PIPE) output.wait() for line in output.stdout.readlines(): line=line.strip() if line.startswith("inet addr"): ip.append(line.split(" ")[1].split(":")[1]) # Restoring LANG os.environ["LANG"]=SYSTEM_LANG return ip def getHostPort(): isdhost="127.0.0.1" isdport=getValueFromConfigFile(configfile,"ICA_ISDPORT","5800") if "LTSP_CLIENT" in os.environ: xprop=subprocess.Popen(["/usr/bin/xprop","-root","ica_ltsp"],stdout=subprocess.PIPE) xprop.wait() if xprop.stdout.read().split(" ")[2].strip() == "1": isdhost=os.environ["LTSP_CLIENT"] else: isdport=str(int(os.environ["LTSP_CLIENT"].split(".")[3])+11000) return [isdhost,isdport] def getSettings(): "Find settings file and read paths" global section_keypathsprivate global section_keypathspublic global section_paths global path_adminprivatekey global path_adminpublickey global path_globalconfig global path_supporterprivatekey global path_supporterpublickey global path_teacherprivatekey global path_teacherpublickey global settingsfile file=open(settingsfile) mode="" for line in file: line = line.strip() if line == section_keypathsprivate: mode=section_keypathsprivate elif line == section_keypathspublic: mode=section_keypathspublic elif line == section_paths: mode=section_paths else: if mode==section_keypathsprivate: if line.startswith("admin"): try: path_adminprivatekey=line.split("=")[1] path_adminprivatekey=path_adminprivatekey.strip() except: path_adminprivatekey="/etc/italc/keys/private/admin/key" elif line.startswith("teacher"): try: path_teacherprivatekey=line.split("=")[1] path_teacherprivatekey=path_teacherprivatekey.strip() except: path_teacherprivatekey="/etc/italc/keys/private/teacher/key" elif line.startswith("supporter"): try: path_supporterprivatekey=line.split("=")[1] path_supporterprivatekey=path_supporterprivatekey.strip() except: path_supporterprivatekey="/etc/italc/keys/private/supporter/key" elif mode==section_keypathspublic: if line.startswith("admin"): try: path_adminpublickey=line.split("=")[1] path_adminpublickey=path_adminpublickey.strip() except: path_adminpublickey="/etc/italc/keys/public/admin/key" elif line.startswith("teacher"): try: path_teacherpublickey=line.split("=")[1] path_teacherpublickey=path_teacherpublickey.strip() except: path_teacherpublickey="/etc/italc/keys/public/teacher/key" elif line.startswith("supporter"): try: path_supporterpublickey=line.split("=")[1] path_supporterpublickey=path_supporterpublickey.strip() except: path_supporterpublickey="/etc/italc/keys/public/supporter/key" elif mode==section_paths: if line.startswith("globalconfig"): try: path_globalconfig=line.split("=")[1] path_globalconfig=path_globalconfig.strip() except: path_globalconfig="/etc/italc/globalconfig.xml" file.close() return # openSUSE uses /etc/sysconfig/ica to define the ports configfile="/etc/sysconfig/ica" # Empty config file to use if it doesn't already exist skeleton=""" """ settingsfile="/etc/settings/iTALC Solutions/iTALC.conf" section_keypathsprivate="[keypathsprivate]" section_keypathspublic="[keypathspublic]" section_paths="[paths]" path_teacherpublickey="/etc/italc/keys/public/teacher/key" path_adminpublickey="/etc/italc/keys/public/admin/key" path_supporterpublickey="/etc/italc/keys/public/supporter/key" path_teacherprivatekey="/etc/italc/keys/private/teacher/key" path_adminprivatekey="/etc/italc/keys/private/admin/key" path_supporterprivatekey="/etc/italc/keys/private/supporter/key" path_globalconfig="/etc/italc/globalconfig.xml" try: getSettings() except: print "Settings not found in "+settingsfile+", using defaults." try: confdir=os.environ.get("HOME")+"/.italc/" except: sys.exit('Invalid or undefined env variable \"HOME\"') try: file=open(path_teacherpublickey,"r") md5_1=md5(file.read()).hexdigest() file.close() file=open(path_adminpublickey,"r") md5_2=md5(file.read()).hexdigest() file.close() file=open(path_supporterpublickey,"r") md5_3=md5(file.read()).hexdigest() file.close() except: sys.exit('iTalc keys not correctly installed ('+path_teacherpublickey+')('+path_adminpublickey+')('+path_supporterpublickey) if not os.access(path_teacherprivatekey,os.R_OK): md5_1="0" if not os.access(path_adminprivatekey,os.R_OK): md5_2="0" if not os.access(path_supporterprivatekey,os.R_OK): md5_3="0" access="none" else: access="supporter" else: access="admin" else: access="teacher" try: xmldoc=minidom.parse(confdir+"globalconfig.xml") body=xmldoc.getElementsByTagName("globalclientconfig")[0].getElementsByTagName("body")[0] classrooms=body.getElementsByTagName("classroom") except: mkdir=subprocess.Popen(["/bin/mkdir","-p",confdir]) mkdir.wait() try: config=open(confdir+"globalconfig.xml","w+") try: file=open(path_globalconfig) for line in file: config.write(line) except: print("Globalconfig in "+path_globalconfig+" not found, using skeleton.") config.write(skeleton) config.close() except: sys.exit('Unable to write to config file') xmldoc=minidom.parse(confdir+"globalconfig.xml") body=xmldoc.getElementsByTagName("globalclientconfig")[0].getElementsByTagName("body")[0] classrooms=body.getElementsByTagName("classroom") # Scan for an existing classroom and delete it for classroom in classrooms: if classroom.getAttribute("name") == "Auto-detected computers": body.removeChild(classroom) # Create the Auto-detected computers classroom avahi=xmldoc.createElement("classroom") avahi.setAttribute("name","Auto-detected computers") avahi.setAttribute("forcevisible","yes") body.appendChild(avahi) # Add computers to the classroom client_list=subprocess.Popen(["/usr/bin/avahi-browse","-trp","_italc._tcp"],stdout=subprocess.PIPE) client_list.wait() count=0 local_addr=getLocalIPs() isdhost,isdport=getHostPort() if isdhost not in local_addr: local_addr=[isdhost] for line in client_list.stdout.readlines(): if line.startswith("="): try: param=line.split(";") comment=re.findall('"(.*)" "(.*)" "(.*)" "(.*)"\n',param[9])[0] if (comment[1] == md5_1 or comment[2] == md5_2 or comment[3] == md5_3) and (param[7] not in local_addr or str(int(isdport)+100) != param[8]): # Make sure we have a running VNC server connection=socket.socket(socket.AF_INET,socket.SOCK_STREAM) connection.connect((param[7],int(param[8]))) connection.close() # Get MAC address mac=subprocess.Popen(("/sbin/arp", param[7], "-n", "-a"),stdout=subprocess.PIPE) mac.wait() mac=mac.stdout.read().strip().split(" ")[3] if not re.match("^..:..:..:..:..:..$",mac): mac="" # Generate the new node client=xmldoc.createElement("client") client.setAttribute("id",str(count)) client.setAttribute("localip",param[7]+":"+param[8]) client.setAttribute("mac",mac) client.setAttribute("name",comment[0]) client.setAttribute("type","0") avahi.appendChild(client) count+=1 except: print 'Ignoring a client, invalid data received' try: file=open(confdir+"globalconfig.xml","w") xmldoc.writexml(file) file.close() except: exit('Failed to save updated config') #print "Starting italc as "+access+" ("+isdhost+":"+isdport+")" subprocess.Popen(["/usr/bin/italc","-isdport",isdport,"-isdhost",isdhost,"-role",access])