dbus service: Bug fix, restoring attribute 'service name' for cloudeebus.Agent
[contrib/cloudeebus.git] / cloudeebus / cloudeebus.py
1 #!/usr/bin/env python
2
3 # Cloudeebus
4 #
5 # Copyright 2012 Intel Corporation.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19 # Luc Yriarte <luc.yriarte@intel.com>
20 # Christophe Guiraud <christophe.guiraud@intel.com>
21 # Frederic Paut <frederic.paut@intel.com>
22 #
23
24
25 import argparse, dbus, json, sys
26
27 from twisted.internet import glib2reactor
28 # Configure the twisted mainloop to be run inside the glib mainloop.
29 # This must be done before importing the other twisted modules
30 glib2reactor.install()
31 from twisted.internet import reactor, defer
32
33 from autobahn.websocket import listenWS
34 from autobahn.wamp import exportRpc, WampServerFactory, WampCraServerProtocol
35
36 from dbus.mainloop.glib import DBusGMainLoop
37
38 import gobject
39 import re
40 import dbus.service
41 gobject.threads_init()
42
43 from dbus import glib
44 glib.init_threads()
45
46 # enable debug log
47 from twisted.python import log
48
49 # XML parser module
50 from xml.etree.ElementTree import XMLParser
51
52 ###############################################################################
53
54 VERSION = "0.5.99"
55 OPENDOOR = False
56 CREDENTIALS = {}
57 WHITELIST = []
58 SERVICELIST = []
59 NETMASK =  []
60
61 ###############################################################################
62 def ipV4ToHex(mask):
63     ## Convert an ip or an IP mask (such as ip/24 or ip/255.255.255.0) in hex value (32bits)
64     maskHex = 0
65     byte = 0
66     if mask.rfind(".") == -1:
67         if (int(mask) < 32):
68             maskHex = (2**(int(mask))-1)
69             maskHex = maskHex << (32-int(mask))
70         else:
71             raise Exception("Illegal mask (larger than 32 bits) " + mask)
72     else:
73         maskField = mask.split(".")
74         # Check if mask has four fields (byte)
75         if len(maskField) != 4:
76             raise Exception("Illegal ip address / mask (should be 4 bytes) " + mask)
77         for maskQuartet in maskField:
78             byte = int(maskQuartet)
79             # Check if each field is really a byte
80             if byte > 255:
81                 raise Exception("Illegal ip address / mask (digit larger than a byte) " + mask)              
82             maskHex += byte
83             maskHex = maskHex << 8
84         maskHex = maskHex >> 8
85     return maskHex
86
87 ###############################################################################
88 class DbusCache:
89     '''
90     Global cache of DBus connexions and signal handlers
91     '''
92     def __init__(self):
93         self.dbusConnexions = {}
94         self.signalHandlers = {}
95
96
97     def reset(self):
98         '''
99         Disconnect signal handlers before resetting cache.
100         '''
101         self.dbusConnexions = {}
102         # disconnect signal handlers
103         for key in self.signalHandlers:
104             self.signalHandlers[key].disconnect()
105         self.signalHandlers = {}
106
107
108     def dbusConnexion(self, busName):
109         if not self.dbusConnexions.has_key(busName):
110             if busName == "session":
111                 self.dbusConnexions[busName] = dbus.SessionBus()
112             elif busName == "system":
113                 self.dbusConnexions[busName] = dbus.SystemBus()
114             else:
115                 raise Exception("Error: invalid bus: %s" % busName)
116         return self.dbusConnexions[busName]
117
118
119
120 ###############################################################################
121 class DbusSignalHandler:
122     '''
123     signal hash id as busName#senderName#objectName#interfaceName#signalName
124     '''
125     def __init__(self, busName, senderName, objectName, interfaceName, signalName):
126         self.id = "#".join([busName, senderName, objectName, interfaceName, signalName])
127         # connect handler to signal
128         self.bus = cache.dbusConnexion(busName)
129         self.bus.add_signal_receiver(self.handleSignal, signalName, interfaceName, senderName, objectName)
130         
131     
132     def disconnect(self):
133         names = self.id.split("#")
134         self.bus.remove_signal_receiver(self.handleSignal, names[4], names[3], names[1], names[2])
135
136
137     def handleSignal(self, *args):
138         '''
139         publish dbus args under topic hash id
140         '''
141         factory.dispatch(self.id, json.dumps(args))
142
143
144
145 ###############################################################################
146 class DbusCallHandler:
147     '''
148     deferred reply to return dbus results
149     '''
150     def __init__(self, method, args):
151         self.pending = False
152         self.request = defer.Deferred()
153         self.method = method
154         self.args = args
155
156
157     def callMethod(self):
158         '''
159         dbus method async call
160         '''
161         self.pending = True
162         self.method(*self.args, reply_handler=self.dbusSuccess, error_handler=self.dbusError)
163         return self.request
164
165
166     def dbusSuccess(self, *result):
167         '''
168         return JSON string result array
169         '''
170         self.request.callback(json.dumps(result))
171         self.pending = False
172
173
174     def dbusError(self, error):
175         '''
176         return dbus error message
177         '''
178         self.request.errback(Exception(error.get_dbus_message()))
179         self.pending = False
180
181
182
183 ################################################################################       
184 class ExecCode:
185     '''
186     Execute DynDBusClass generated code
187     '''
188     def __init__(self, globalCtx, localCtx) :
189         self.exec_string = ""
190         self.exec_code = None
191         self.exec_code_valid = 1
192         self.indent_level = 0
193         self.indent_increment = 1
194         self.line = 0
195         self.localCtx = localCtx
196         self.globalCtx = globalCtx
197         
198
199     def append_stmt(self, stmt) :
200         self.exec_code_valid = 0
201         self.line += 1
202         for x in range(0,self.indent_level):
203             self.exec_string = self.exec_string + ' '            
204         self.exec_string = self.exec_string + stmt + '\n'
205
206     def indent(self) :
207         self.indent_level = self.indent_level + self.indent_increment
208
209     def dedent(self) :
210         self.indent_level = self.indent_level - self.indent_increment
211     
212     # compile : Compile exec_string into exec_code using the builtin
213     # compile function. Skip if already in sync.
214     def compile(self) :
215         if not self.exec_code_valid :
216             self.exec_code = compile(self.exec_string, "<string>", "exec")
217         self.exec_code_valid = True
218
219     def execute(self) :
220         if not self.exec_code_valid :
221             self.compile()
222         exec(self.exec_code, self.globalCtx, self.localCtx)
223
224
225
226 ################################################################################       
227 class XmlCbParser: # The target object of the parser
228     maxDepth = 0
229     depth = 0
230     def __init__(self, dynDBusClass):
231         self.dynDBusClass = dynDBusClass
232         
233     def start(self, tag, attrib):   # Called for each opening tag.
234         if (tag == 'node'):
235             return
236         # Set interface name
237         if (tag == 'interface'):
238             self.dynDBusClass.set_interface(attrib['name'])
239             return
240         # Set method name
241         if (tag == 'method'):
242             self.current = tag
243             self.dynDBusClass.def_method(attrib['name'])
244             return
245         if (tag == 'signal'):
246             self.current = tag
247             self.dynDBusClass.def_signal(attrib['name'])
248             return
249
250         # Set signature (in/out & name) for method
251         if (tag == 'arg'):
252             if (self.current == 'method'):
253                 if (attrib.has_key('direction') == False):
254                     attrib['direction'] = "in"
255                 self.dynDBusClass.add_signature(attrib['name'],
256                                                 attrib['direction'],
257                                                 attrib['type'])
258                 return
259             if (self.current == 'signal'):
260                 if (attrib.has_key('name') == False):
261                     attrib['name'] = 'value'
262                 self.dynDBusClass.add_signature(attrib['name'], 'in',
263                                                 attrib['type'])
264                 return
265     def end(self, tag):             # Called for each closing tag.
266         if (tag == 'method'):
267             self.dynDBusClass.add_dbus_method()
268             self.dynDBusClass.add_body_method()
269             self.dynDBusClass.end_method()
270         if (tag == 'signal'):
271             self.dynDBusClass.add_dbus_signal()
272             self.dynDBusClass.add_body_signal()
273             self.dynDBusClass.end_method()
274            
275     def data(self, data):
276         pass            # We do not need to do anything with data.
277     def close(self):    # Called when all data has been parsed.
278         return self.maxDepth
279
280
281        
282 ###############################################################################
283 def createClassName(objectPath):
284     return re.sub('/', '_', objectPath[1:])
285
286 ################################################################################       
287 class DynDBusClass():
288     def __init__(self, className, globalCtx, localCtx):
289         self.xmlCB = XmlCbParser(self)
290         self.signature = {}
291         self.class_code = ExecCode(globalCtx, localCtx)  
292         self.class_code.indent_increment = 4
293         self.class_code.append_stmt("import dbus")
294         self.class_code.append_stmt("\n")
295         self.class_code.append_stmt("class " + className + "(dbus.service.Object):")
296         self.class_code.indent()
297         
298         ## Overload of __init__ method 
299         self.def_method("__init__")
300         self.add_method("bus, callback=None, objPath='/sample', busName='org.cloudeebus'")
301         self.add_stmt("self.bus = bus")
302         self.add_stmt("self.objPath = objPath")
303         self.add_stmt("self.callback = callback")        
304         self.add_stmt("dbus.service.Object.__init__(self, conn=bus, bus_name=busName)")
305         self.end_method()
306                
307         ## Create 'add_to_connection' method 
308         self.def_method("add_to_connection")
309         self.add_method("connection=None, path=None")
310         self.add_stmt("dbus.service.Object.add_to_connection(self, connection=self.bus, path=self.objPath)")
311         self.end_method()
312                
313         ## Create 'remove_from_connection' method 
314         self.def_method("remove_from_connection")
315         self.add_method("connection=None, path=None")
316         self.add_stmt("dbus.service.Object.remove_from_connection(self, connection=None, path=self.objPath)")
317         self.end_method()
318                
319     def createDBusServiceFromXML(self, xml):
320         self.parser = XMLParser(target=self.xmlCB)
321         self.parser.feed(xml)
322         self.parser.close()
323     
324     def set_interface(self, ifName):
325         self.ifName = ifName
326         
327     def def_method(self, methodName):
328         self.methodToAdd = methodName
329         self.signalToAdd = None
330         self.args_str = str()
331         self.signature = {}
332         self.signature['name'] = str()
333         self.signature['in'] = str()                
334         self.signature['out'] = str()                        
335
336     def def_signal(self, signalName):
337         self.methodToAdd = None
338         self.signalToAdd = signalName
339         self.args_str = str()
340         self.signature = {}
341         self.signature['name'] = str()
342         self.signature['in'] = str()                
343         self.signature['out'] = str()                        
344
345     def add_signature(self, name, direction, signature):
346         if (direction == 'in'):
347             self.signature['in'] += signature
348             if (self.signature['name'] != str()):
349                 self.signature['name'] += ", "
350             self.signature['name'] += name
351         if (direction == 'out'):
352             self.signature['out'] = signature                        
353         
354     def add_method(self, args = None, async_success_cb = None, async_err_cb = None):
355         async_cb_str = str()
356         if (self.methodToAdd != None):
357             name = self.methodToAdd
358         else:
359             name = self.signalToAdd
360         if (args != None):
361             self.args_str = args
362         if (async_success_cb != None):
363             async_cb_str = async_success_cb
364         if (async_err_cb != None):
365             if (async_cb_str != str()):
366                 async_cb_str += ", "
367             async_cb_str += async_err_cb
368                         
369         parameters = self.args_str
370         if (async_cb_str != str()):
371             if (parameters != str()):
372                 parameters += ", "
373             parameters +=async_cb_str       
374         
375         if (parameters != str()):
376             self.class_code.append_stmt("def " + name + "(self, %s):" % parameters)               
377         else:
378             self.class_code.append_stmt("def " + name + "(self):")
379         self.class_code.indent()
380         
381     def end_method(self):
382         self.class_code.append_stmt("\n")
383         self.class_code.dedent()
384         
385     def add_dbus_method(self):
386         decorator = '@dbus.service.method("' + self.ifName + '"'
387         if (self.signature.has_key('in') and self.signature['in'] != str()):
388                 decorator += ", in_signature='" + self.signature['in'] + "'"
389         if (self.signature.has_key('out') and self.signature['out'] != str()):
390                 decorator += ", out_signature='" + self.signature['out'] + "'"
391         decorator += ", async_callbacks=('dbus_async_cb', 'dbus_async_err_cb')"            
392         decorator += ")"
393         self.class_code.append_stmt(decorator)
394         if (self.signature.has_key('name') and self.signature['name'] != str()):
395             self.add_method(self.signature['name'], async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
396         else:
397             self.add_method(async_success_cb='dbus_async_cb', async_err_cb='dbus_async_err_cb')
398
399     def add_dbus_signal(self):
400         decorator = '@dbus.service.signal("' + self.ifName + '"'
401         if (self.signature.has_key('in') and self.signature['in'] != str()):
402                 decorator += ", signature='" + self.signature['in'] + "'"
403         decorator += ")"            
404         self.class_code.append_stmt(decorator)
405         if (self.signature.has_key('name') and self.signature['name'] != str()):
406             self.add_method(self.signature['name'])
407         else:
408             self.add_method()
409
410     def add_body_method(self):
411         if (self.methodToAdd != None):
412             if (self.args_str != str()):
413                 self.class_code.append_stmt("self.callback('" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb, %s)" % self.args_str)
414             else:        
415                 self.class_code.append_stmt("self.callback('" + self.methodToAdd + "', self.objPath, '"  + self.ifName + "', " + "dbus_async_cb, dbus_async_err_cb)")
416
417     def add_body_signal(self):
418         self.class_code.append_stmt("return") ## TODO: Remove and fix with code ad hoc
419         self.class_code.append_stmt("\n")
420
421     def add_stmt(self, stmt) :
422         self.class_code.append_stmt(stmt)
423         
424     def declare(self) :
425         self.class_code.execute()
426
427
428
429 ###############################################################################
430 class CloudeebusService:
431     '''
432     support for sending DBus messages and registering for DBus signals
433     '''
434     def __init__(self, permissions):
435         self.permissions = {};
436         self.permissions['permissions'] = permissions['permissions']
437         self.permissions['authextra'] = permissions['authextra']
438         self.permissions['services'] = permissions['services']
439         self.proxyObjects = {}
440         self.proxyMethods = {}
441         self.pendingCalls = []
442         self.dynDBusClasses = {} # DBus class source code generated dynamically (a list because one by classname)
443         self.services = {}  # DBus service created
444         self.serviceAgents = {} # Instantiated DBus class previously generated dynamically, for now, one by classname
445         self.servicePendingCalls = {} # JS methods called (and waiting for a Success/error response), containing 'methodId', (successCB, errorCB)
446         self.localCtx = locals()
447         self.globalCtx = globals()
448
449
450     def proxyObject(self, busName, serviceName, objectName):
451         '''
452         object hash id as busName#serviceName#objectName
453         '''
454         id = "#".join([busName, serviceName, objectName])
455         if not self.proxyObjects.has_key(id):
456             if not OPENDOOR:
457                 # check permissions, array.index throws exception
458                 self.permissions['permissions'].index(serviceName)
459             bus = cache.dbusConnexion(busName)
460             self.proxyObjects[id] = bus.get_object(serviceName, objectName)
461         return self.proxyObjects[id]
462
463
464     def proxyMethod(self, busName, serviceName, objectName, interfaceName, methodName):
465         '''
466         method hash id as busName#serviceName#objectName#interfaceName#methodName
467         '''
468         id = "#".join([busName, serviceName, objectName, interfaceName, methodName])
469         if not self.proxyMethods.has_key(id):
470             obj = self.proxyObject(busName, serviceName, objectName)
471             self.proxyMethods[id] = obj.get_dbus_method(methodName, interfaceName)
472         return self.proxyMethods[id]
473
474
475     @exportRpc
476     def dbusRegister(self, list):
477         '''
478         arguments: bus, sender, object, interface, signal
479         '''
480         if len(list) < 5:
481             raise Exception("Error: expected arguments: bus, sender, object, interface, signal)")
482         
483         if not OPENDOOR:
484             # check permissions, array.index throws exception
485             self.permissions['permissions'].index(list[1])
486         
487         # check if a handler exists
488         sigId = "#".join(list)
489         if cache.signalHandlers.has_key(sigId):
490             return sigId
491         
492         # create a handler that will publish the signal
493         dbusSignalHandler = DbusSignalHandler(*list)
494         cache.signalHandlers[sigId] = dbusSignalHandler
495         
496         return dbusSignalHandler.id
497
498
499     @exportRpc
500     def dbusSend(self, list):
501         '''
502         arguments: bus, destination, object, interface, message, [args]
503         '''
504         # clear pending calls
505         for call in self.pendingCalls:
506             if not call.pending:
507                 self.pendingCalls.remove(call)
508         
509         if len(list) < 5:
510             raise Exception("Error: expected arguments: bus, destination, object, interface, message, [args])")
511         
512         # parse JSON arg list
513         args = []
514         if len(list) == 6:
515             args = json.loads(list[5])
516         
517         # get dbus proxy method
518         method = self.proxyMethod(*list[0:5])
519         
520         # use a deferred call handler to manage dbus results
521         dbusCallHandler = DbusCallHandler(method, args)
522         self.pendingCalls.append(dbusCallHandler)
523         return dbusCallHandler.callMethod()
524
525
526     @exportRpc
527     def emitSignal(self, list):
528         '''
529         arguments: agentObjectPath, signalName, result (to emit)
530         '''
531         objectPath = list[0]
532         className = re.sub('/', '_', objectPath[1:])
533         signalName = list[1]
534         result = list[2]
535         if (self.serviceAgents.has_key(className) == True):
536             exe_str = "self.serviceAgents['"+ className +"']."+ signalName + "(" + str(result) + ")"
537             eval(exe_str, self.globalCtx, self.localCtx)
538         else:
539             raise Exception("No object path " + objectPath)
540
541     @exportRpc
542     def returnMethod(self, list):
543         '''
544         arguments: methodId, callIndex, success (=true, error otherwise), result (to return)
545         '''
546         methodId = list[0]
547         callIndex = list[1]
548         success = list[2]
549         result = list[3]
550         if (self.servicePendingCalls.has_key(methodId)):
551             cb = self.servicePendingCalls[methodId]['calls'][callIndex]
552             if cb is None:
553                 raise Exception("No pending call " + str(callIndex) + " for methodID " + methodId)
554             if (success):                
555                 successCB = cb["successCB"]
556                 if (result != None):
557                     successCB(result)
558                 else:
559                     successCB()                    
560             else:     
561                 errorCB = cb["errorCB"]        
562                 if (result != None):
563                     errorCB(result)
564                 else:
565                     errorCB()
566             self.servicePendingCalls[methodId]['calls'][callIndex] = None
567             self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] - 1
568             if self.servicePendingCalls[methodId]['count'] == 0:
569                 del self.servicePendingCalls[methodId]
570         else:
571             raise Exception("No methodID " + methodId)
572
573     def srvCB(self, name, objPath, ifName, async_succes_cb, async_error_cb, *args):
574         methodId = self.srvName + "#" + objPath + "#" + ifName + "#" + name
575         cb = { 'successCB': async_succes_cb, 
576                'errorCB': async_error_cb}
577         if methodId not in self.servicePendingCalls:
578             self.servicePendingCalls[methodId] = {'count': 0, 'calls': []}
579             
580         try:
581             pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
582         except Exception, e:                
583             args = eval( str(args).replace("dbus.Byte", "dbus.Int16") )
584             pendingCallStr = json.dumps({'callIndex': len(self.servicePendingCalls[methodId]['calls']), 'args': args})
585                
586         self.servicePendingCalls[methodId]['calls'].append(cb)
587         self.servicePendingCalls[methodId]['count'] = self.servicePendingCalls[methodId]['count'] + 1
588         factory.dispatch(methodId, pendingCallStr)
589                     
590     @exportRpc
591     def serviceAdd(self, list):
592         '''
593         arguments: busName, srvName
594         '''
595         busName = list[0]
596         self.bus =  cache.dbusConnexion( busName )
597         self.srvName = list[1]
598         if not OPENDOOR and (SERVICELIST == [] or SERVICELIST != [] and self.permissions['services'] == None):
599             SERVICELIST.index(self.srvName)
600             
601         if (self.services.has_key(self.srvName) == False):
602             self.services[self.srvName] = dbus.service.BusName(name = self.srvName, bus = self.bus)
603         return self.srvName
604
605     @exportRpc
606     def serviceRelease(self, list):
607         '''
608         arguments: busName, srvName
609         '''
610         self.srvName = list[0]
611         if (self.services.has_key(self.srvName) == True):
612             self.services.pop(self.srvName)
613             return self.srvName
614         else:
615             raise Exception(self.srvName + " does not exist")
616                    
617     @exportRpc
618     def serviceAddAgent(self, list):
619         '''
620         arguments: objectPath, xmlTemplate
621         '''
622         srvName = list[0]
623         agentObjectPath = list[1]
624         xmlTemplate = list[2]
625         className = createClassName(agentObjectPath)
626         if (self.dynDBusClasses.has_key(className) == False):
627             self.dynDBusClasses[className] = DynDBusClass(className, self.globalCtx, self.localCtx)
628             self.dynDBusClasses[className].createDBusServiceFromXML(xmlTemplate)
629             self.dynDBusClasses[className].declare()
630
631         ## Class already exist, instanciate it if not already instanciated
632         if (self.serviceAgents.has_key(className) == False):
633             self.serviceAgents[className] = eval(className + "(self.bus, callback=self.srvCB, objPath='"+agentObjectPath+"', busName='"+srvName+"')", self.globalCtx, self.localCtx)
634             
635         self.serviceAgents[className].add_to_connection()
636         return (agentObjectPath)
637                     
638     @exportRpc
639     def serviceDelAgent(self, list):
640         '''
641         arguments: objectPath, xmlTemplate
642         '''
643         agentObjectPath = list[0]
644         className = createClassName(agentObjectPath)
645         
646         print 'PY Try to remove ' + className
647
648         if (self.serviceAgents.has_key(className)):
649             self.serviceAgents[className].remove_from_connection()
650             self.serviceAgents.pop(className)
651         else:
652             raise Exception(agentObjectPath + " doesn't exist!")
653         
654         return (agentObjectPath)
655                     
656     @exportRpc
657     def getVersion(self):
658         '''
659         return current version string
660         '''
661         return VERSION
662
663
664
665 ###############################################################################
666 class CloudeebusServerProtocol(WampCraServerProtocol):
667     '''
668     connexion and session authentication management
669     '''
670     
671     def onSessionOpen(self):
672         # CRA authentication options
673         self.clientAuthTimeout = 0
674         self.clientAuthAllowAnonymous = OPENDOOR
675         # CRA authentication init
676         WampCraServerProtocol.onSessionOpen(self)
677     
678     
679     def getAuthPermissions(self, key, extra):
680          return {'permissions': extra.get("permissions", None),
681                  'authextra': extra.get("authextra", None),
682                  'services': extra.get("services", None)}   
683     
684     def getAuthSecret(self, key):
685         secret = CREDENTIALS.get(key, None)
686         if secret is None:
687             return None
688         # secret must be of str type to be hashed
689         return str(secret)
690     
691
692     def onAuthenticated(self, key, permissions):
693         if not OPENDOOR:
694             # check net filter
695             if NETMASK != []:
696                 ipAllowed = False
697                 for netfilter in NETMASK:
698                     ipHex=ipV4ToHex(self.peer.host)
699                     ipAllowed = (ipHex & netfilter['mask']) == netfilter['ipAllowed'] & netfilter['mask']
700                     if ipAllowed:
701                         break
702                 if not ipAllowed:
703                     raise Exception("host " + self.peer.host + " is not allowed!")
704             # check authentication key
705             if key is None:
706                 raise Exception("Authentication failed")
707             # check permissions, array.index throws exception
708             if (permissions['permissions'] != None):
709                 for req in permissions['permissions']:
710                     WHITELIST.index(req);
711             # check allowed service creation, array.index throws exception
712             if (permissions['services'] != None):
713                 for req in permissions['services']:
714                     SERVICELIST.index(req);
715         # create cloudeebus service instance
716         self.cloudeebusService = CloudeebusService(permissions)
717         # register it for RPC
718         self.registerForRpc(self.cloudeebusService)
719         # register for Publish / Subscribe
720         self.registerForPubSub("", True)
721     
722     
723     def connectionLost(self, reason):
724         WampCraServerProtocol.connectionLost(self, reason)
725         if factory.getConnectionCount() == 0:
726             cache.reset()
727
728
729
730 ###############################################################################
731
732 if __name__ == '__main__':
733     
734     cache = DbusCache()
735
736     parser = argparse.ArgumentParser(description='Javascript DBus bridge.')
737     parser.add_argument('-v', '--version', action='store_true', 
738         help='print version and exit')
739     parser.add_argument('-d', '--debug', action='store_true', 
740         help='log debug info on standard output')
741     parser.add_argument('-o', '--opendoor', action='store_true',
742         help='allow anonymous access to all services')
743     parser.add_argument('-p', '--port', default='9000',
744         help='port number')
745     parser.add_argument('-c', '--credentials',
746         help='path to credentials file')
747     parser.add_argument('-w', '--whitelist',
748         help='path to whitelist file (DBus services to use)')
749     parser.add_argument('-s', '--servicelist',
750         help='path to servicelist file (DBus services to export)')
751     parser.add_argument('-n', '--netmask',
752         help='netmask,IP filter (comma separated.) eg. : -n 127.0.0.1,192.168.2.0/24,10.12.16.0/255.255.255.0')
753     
754     args = parser.parse_args(sys.argv[1:])
755
756     if args.version:
757         print("Cloudeebus version " + VERSION)
758         exit(0)
759     
760     if args.debug:
761         log.startLogging(sys.stdout)
762     
763     OPENDOOR = args.opendoor
764     
765     if args.credentials:
766         jfile = open(args.credentials)
767         CREDENTIALS = json.load(jfile)
768         jfile.close()
769     
770     if args.whitelist:
771         jfile = open(args.whitelist)
772         WHITELIST = json.load(jfile)
773         jfile.close()
774         
775     if args.servicelist:
776         jfile = open(args.servicelist)
777         SERVICELIST = json.load(jfile)
778         jfile.close()
779         
780     if args.netmask:
781         iplist = args.netmask.split(",")
782         for ip in iplist:
783             if ip.rfind("/") != -1:
784                 ip=ip.split("/")
785                 ipAllowed = ip[0]
786                 mask = ip[1]
787             else:
788                 ipAllowed = ip
789                 mask = "255.255.255.255" 
790             NETMASK.append( {'ipAllowed': ipV4ToHex(ipAllowed), 'mask' : ipV4ToHex(mask)} )
791
792     uri = "ws://localhost:" + args.port
793     
794     factory = WampServerFactory(uri, debugWamp = args.debug)
795     factory.protocol = CloudeebusServerProtocol
796     factory.setProtocolOptions(allowHixie76 = True)
797     
798     listenWS(factory)
799     
800     DBusGMainLoop(set_as_default=True)
801     
802     reactor.run()