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