source: rc/puzzlebox_brainstorms_server.py @ 4

Last change on this file since 4 was 4, checked in by sc, 12 years ago

server:

  • initial checkin
File size: 22.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Puzzlebox - Brainstorms - Server
5#
6# Copyright Puzzlebox Productions, LLC (2010)
7#
8# Portions of this code have been previously
9# released under the GNU Pulic License (GPL) version 2
10# and is Copyright Steven M. Castellotti (2010)
11# For more information please refer to http://www.gnu.org/copyleft/gpl.htm
12#
13# Last Update: 2010.01.27
14#
15#####################################################################
16
17import os, signal, time
18import cPickle as pickle
19
20from twisted.internet import reactor, protocol, defer
21
22#import puzzlebox_configuration
23import puzzlebox_master_control_client
24#import puzzlebox_logger
25
26
27#####################################################################
28# Globals
29#####################################################################
30
31DEBUG = 1
32
33#SERVER_HOST = puzzlebox_configuration.SERVER_HOST
34#SERVER_PORT = puzzlebox_configuration.SERVER_PORT
35SERVER_HOST = '127.0.0.1'
36SERVER_PORT = 8194
37
38#MAX_COMPONENTS = puzzlebox_configuration.MAX_COMPONENTS
39#GTKMOZ_PROFILE_DIR = puzzlebox_configuration.GTKMOZ_PROFILE_DIR
40#AUTOBIT_TIMEOUT = puzzlebox_configuration.AUTOBIT_TIMEOUT
41
42
43#####################################################################
44# Classes
45#####################################################################
46
47class puzzlebox_master_control(protocol.ServerFactory):
48
49        def __init__(self, log, DEBUG=DEBUG):
50
51                self.protocol = puzzlebox_master_control_server_protocol
52
53                self.log = log
54                self.DEBUG = DEBUG
55
56                self.registry = {}
57                #self.registry['components'] = {}
58                #self.registry['windows'] = {}
59
60                #self.csc = puzzlebox_master_control_client.puzzlebox_master_control_client( \
61                                #self.log, \
62                                #puzzlebox_configuration.CONTENT_SET_HOST, \
63                                #puzzlebox_configuration.CONTENT_SET_PORT, \
64                                #service_name = 'MCP', \
65                                #target_name = 'CS')
66
67
68        ##################################################################
69
70        def process_instruction(self, instruction):
71
72                #self.log.debug("Received instruction: %s" % instruction)
73                #if self.DEBUG:
74                        #print "Received instruction: %s" % instruction
75
76                d = defer.Deferred()
77                response = 'instruction received'
78
79                if 'command' in instruction:
80
81                        response = '%s instruction received' % instruction['command']
82
83
84                        if 'information' in instruction:
85                                information = instruction['information']
86
87
88                        #if instruction['command'] == 'register_component':
89
90                                #if information['pid'] in self.registry['components']:
91                                        #self.log.error("Duplicate component registering - replacing old component (assumed dead).")
92                                        #self.log.error("Old component: %s" % self.registry['components'][information['pid']])
93                                        #self.log.error("New Component: %s" % information)
94                                       
95                                        #if self.DEBUG:
96                                                #print "Duplicate component registering - replacing old component (assumed dead)."
97                                                #print "Old component: %s" % self.registry['components'][information['pid']]
98                                                #print "New Component: %s" % information
99
100                                #self.registry['components'][information['pid']] = information
101
102
103                        #elif instruction['command'] == 'unregister_component':
104
105                                #if information['pid'] in self.registry['components']:
106                                        #self.log.debug("Removing component from registry: %s" % self.registry['components'][information['pid']])
107                                       
108                                        #if self.DEBUG:
109                                                #print "Removing component from registry: %s" % \
110                                                   #self.registry['components'][information['pid']]
111                                       
112                                        #del(self.registry['components'][information['pid']])
113
114                                #else:
115                                        #self.log.error("Unknown component attempted to unregister")
116                                        #if self.DEBUG:
117                                                #print "Unknown component attempted to unregister"
118
119
120                        #elif instruction['command'] == 'update_component':
121
122                                #if information['pid'] in self.registry['components']:
123
124                                        #component = self.registry['components'][information['pid']]
125
126                                        #for key in information:
127                                                #component[key] = information[key]
128
129                                #else:
130                                        #self.log.error("Unknown component attempted to update: %s" % information)
131                                        #if self.DEBUG:
132                                                #print "Unknown component attempted to update: %s" % information
133
134
135                        #elif instruction['command'] == 'restart_component':
136                               
137                                #self.restart_component(information['pid'])
138
139
140                        #elif instruction['command'] == 'load_url':
141
142                                #found_component = False
143
144                                #for each in self.registry['components']:
145
146                                        #component = self.registry['components'][each]
147
148                                        #if component['type'] == information['type'] and \
149                                                #component['display_pos_x'] == information['display_pos_x'] and \
150                                                #component['display_pos_y'] == information['display_pos_y'] and \
151                                                #component['window_size_x'] == information['window_size_x'] and \
152                                                #component['window_size_y'] == information['window_size_y']:
153
154                                                #found_component = True
155
156                                                #if component['active']:
157                                                        #self.log.debug("Matched an active component, activating callback.")
158                                                        #if self.DEBUG:
159                                                                #print "Matched an active component, activating callback."
160                                                        #component['callback'].callback("browser exit")
161
162                                                #component['callback'] = d
163
164                                                #try:
165                                                        #component['autobit'].cancel()
166                                                #except:
167                                                        #pass
168
169                                                #if int(information['enable_autobit']) == 1:
170                                                        #self.log.debug("Window %i loading with autobit enabled." % information['window_id'])
171                                                       
172                                                        #if self.DEBUG:
173                                                                #print "Window %i loading with autobit enabled." % \
174                                                                        #information['window_id']
175                                                       
176                                                        #component['autobit'] = \
177                                                                #reactor.callLater(AUTOBIT_TIMEOUT, self.restart_component, each)
178
179                                                #component['active'] = True
180
181                                                #for key in information:
182                                                        #component[key] = information[key]
183
184                                                #component['source'] = 'MCP'
185                                                #component['target'] = component['type']
186
187                                                #dw = self.csc.send_instruction(instruction, component=component)
188
189                                                #dw.addCallback(self.process_component_response, instruction)
190                                               
191                                                #break
192                               
193                                #if not found_component:
194                                       
195                                        #self.log.debug("No component found matching load_url information, spawning new.")
196                                        #if self.DEBUG:
197                                                #print "No component found matching load_url information, spawning new."
198                                        #self.spawn_new_component(instruction, d)
199
200                                #response = None
201
202
203                        #elif instruction['command'] == 'animation_started':
204
205                                #window_id = information['window_id']
206
207                                #self.log.debug("Got animation started for window id %i" % window_id)
208                                #if self.DEBUG:
209                                        #print "Got animation started for window id %i" % window_id
210
211                                #for each in self.registry['components']:
212                                        #if 'window_id' in self.registry['components'][each] and \
213                                                        #int(self.registry['components'][each]['window_id']) == int(window_id):
214
215                                                #self.log.debug("Found window %i" % window_id)
216                                                #if self.DEBUG:
217                                                        #print "Found window %i" % window_id
218                                               
219                                                #if 'autobit' in self.registry['components'][each]:
220                                                        #self.log.debug("Window %i cancelling autobit restart." % window_id)
221                                                        #if self.DEBUG:
222                                                                #print "Window %i cancelling autobit restart." % window_id
223
224                                                        #try:
225                                                                #self.registry['components'][each]['autobit'].cancel()
226                                                        #except:
227                                                                #self.log.error("Window %i error trying to cancel autobit restart" % window_id)
228                                                                #if self.DEBUG:
229                                                                        #print "Window %i error trying to cancel autobit restart" % window_id
230
231                                                #self.log.debug("Window %i white flash fix unhiding browser." % window_id)
232                                                #tinstr = {'command': 'unhide'}
233                                                #self.csc.send_instruction(tinstr, component=self.registry['components'][each])
234
235
236                        #elif instruction['command'] == 'hide_all_components':
237
238                                #dlist = []
239
240                                #for each in self.registry['components']:
241
242                                        #component = self.safe_component(self.registry['components'][each])
243
244                                        #tinstr = {'command': 'hide'}
245
246                                        #dlist.append(self.csc.send_instruction(tinstr, component))
247
248                                #if dlist:
249                                        #response = None
250                                        #dl = defer.DeferredList(dlist)
251                                        #dl.addCallback(d.callback)
252
253
254                        #elif instruction['command'] == 'register_window':
255
256                                #if information['window_id'] in self.registry['windows']:
257                                        #self.log.error("Duplicate window attempted to register: %s" % information)
258                                        #if self.DEBUG:
259                                                #print "Duplicate window attempted to register: %s" % information
260
261                                #else:
262                                        #self.registry['windows'][information['window_id']] = information
263
264
265                        #elif instruction['command'] == 'get_window_data':
266
267                                #if information['window_id'] == 'all':
268
269                                        #response = self.registry['windows']
270
271                                #elif information['window_id'] in self.registry['windows']:
272
273                                        #response = self.registry['windows'][information['window_id']]
274
275                                #else:
276                                        #self.log.error("Attempt to request data for unknown window: %s" % information)
277                                        #if self.DEBUG:
278                                                #print "Attempt to request data for unknown window: %s" % information
279
280
281                        #elif instruction['command'] == 'unregister_all_windows':
282
283                                #self.registry['windows'] = {}
284
285
286                        #elif instruction['command'] == 'exit_content_set':
287
288                                #self.log.debug("Requesting current content set to exit.")
289                                #if self.DEBUG:
290                                        #print "Requesting current content set to exit."
291                                #self.csc.exit_content_set()
292
293
294                        #elif instruction['command'] == 'change_content_set':
295
296                                #self.log.debug("Requesting to change content set to content_set_id %i" % information['content_set_id'])
297                                #if self.DEBUG:
298                                        #print "Requesting to change content set to content_set_id %i" % \
299                                           #information['content_set_id']
300                                #self.csc.change_content_set(information)
301
302
303                        #elif instruction['command'] == 'end_current_entry':
304
305                                #self.log.debug("Requesting to end content in window_id %i" % information['window_id'])
306                                #if self.DEBUG:
307                                        #print "Requesting to end content in window_id %i" % \
308                                           #information['window_id']
309                                #self.csc.end_current_entry(information)
310
311
312                        #elif instruction['command'] == 'request_screenshot':
313
314                                #self.log.debug("Requesting content set to take screenshot")
315                                #if self.DEBUG:
316                                        #print "Requesting content set to take screenshot"
317                                #self.csc.request_screenshot()
318
319
320                        #elif instruction['command'] == 'upload_screenshot':
321
322                                #self.log.debug("Requesting content set to upload system screenshot")
323                                #if self.DEBUG:
324                                        #print "Requesting content set to upload system screenshot"
325                                #self.csc.upload_screenshot()
326
327
328                        #elif instruction['command'] == 'export_schedule':
329
330                                #self.log.debug("Requesting content set to export content_set_sequence_schedule_id %i" % \
331                                                #information['content_set_sequence_schedule_id'])
332                                #if self.DEBUG:
333                                        #print "Requesting content set to export content_set_sequence_schedule_id %i" % \
334                                                #information['content_set_sequence_schedule_id']
335                                #self.csc.export_schedule(information)
336
337
338                        #elif instruction['command'] == 'check_health':
339
340                                #response = 'health_okay'
341
342
343                        #elif instruction['command'] == 'get_registry':
344
345                                # unfortunately registry needs to have volatile components removed
346                                # before it can be passed via pickle, e.g. the deferred objects in callback.
347                                # may end up changing the structure around so it's less hassle to pass
348
349                                #safe_registry = {}
350                                #safe_registry['components'] = {}
351                                #safe_registry['windows'] = {}
352                               
353                                #for each in self.registry['components']:
354                                        #safe_registry['components'][each] = \
355                                                        #self.safe_component(self.registry['components'][each])
356
357                                #for each in self.registry['windows']:
358                                        #safe_registry['windows'][each] = self.registry['windows'][each].copy()
359
360                                #response = safe_registry
361
362
363                        #elif instruction['command'] == '/usr/bin/puzzlebox_xorg_reset.py':
364
365                                #response = "EXECUTE OK"
366
367
368                        else:
369
370                                #self.log.error("Unrecognized command received: %s" % instruction)
371                                if self.DEBUG:
372                                        print "Unrecognized command received: %s" % instruction
373                                reponse = 'unrecognized instruction'
374
375
376                if response:
377                        d.callback(response)
378
379                return d
380
381
382        ##################################################################
383
384        #def process_component_response(self, result, instruction):
385
386                #result, component = result[0], result[1]
387
388                #if result == 'OK':
389
390                        #self.log.debug('%s responded OK' % component['type'])
391                        #if self.DEBUG:
392                                #print '%s responded OK' % component['type']
393
394
395                #elif result == 'FAILED_TO_CONNECT' or result == 'NO_REPLY':
396
397                        #self.log.error('%s responded %s' % (component['type'], result))
398                        #if self.DEBUG:
399                                #print '%s responded %s' % (component['type'], result)
400
401                        #if instruction['command'] == 'duration_expire':
402                                ## not sure what else to do here, since duration expire is meant to make the browser go away
403                                ## if it is already gone, no real point in bringing it back?
404                                #self.log.debug("Failed to connect for duration_expire... well whatever then.")
405                                #if self.DEBUG:
406                                        #print "Failed to connect for duration_expire... well whatever then."
407
408                        #elif instruction['command'] == 'load_url':
409                                #self.log.error("Failed to connect for load_url, restarting the web_browser.")
410                                #if self.DEBUG:
411                                        #print "Failed to connect for load_url, restarting the web_browser."
412                                #self.restart_component(component[pid])
413
414                #else:
415                        #self.log.error("%s" % result)
416                        #if self.DEBUG:
417                                #print "%s" % result
418
419
420        ##################################################################
421
422        #def safe_component(self, component):
423
424                ## remove volatile data from a component so it can be safely passed through pickle
425               
426                #tcomp = component.copy()
427                #tcomp['callback'] = None
428                #tcomp['autobit'] = None
429               
430                #return tcomp
431
432
433        ##################################################################
434
435        #def restart_component(self, pid):
436
437                #if pid in self.registry['components']:
438                        #try:
439                                #os.kill(pid, signal.SIGKILL)
440                                #self.log.debug("Killed component %s" % pid)
441                                #if self.DEBUG:
442                                        #print "Killed component %s" % pid
443                                #return True
444                        #except:
445                                #self.log.error("Failed to kill component %s" % pid)
446                                #if self.DEBUG:
447                                        #print "Failed to kill component %s" % pid
448                                #return False
449
450
451        ##################################################################
452
453        #def process_connection_lost(self, instruction):
454
455                #if not instruction:
456
457                        #self.log.debug("Connection lost with no instruction?")
458                        #if self.DEBUG:
459                                #print "Connection lost with no instruction?"
460                        #return
461
462                #else:
463               
464                        #if instruction['command'] == 'load_url':
465                       
466                                #found_component = False
467                                #information = instruction['information']
468
469                                #for each in self.registry['components']:
470
471                                        #component = self.registry['components'][each]
472
473                                        ## should only ever be 1 of each type of component per window_id
474                                        #if component['type'] == information['type'] and \
475                                           #component['window_id'] == information['window_id']:
476
477                                                #found_component = True
478
479                                                #component['active'] = False
480                                                #component['last_active'] = time.time()
481
482                                                #component['source'] = 'MCP'
483                                                #component['target'] = component['type']
484
485                                                #instruction['command'] = 'duration_expire'
486                                                #instruction['information'] = None
487
488                                                #dw = self.csc.send_instruction(instruction, component=component)
489
490                                                #dw.addCallback(self.process_component_response, instruction)
491                                               
492                                                #break
493
494                                #if not found_component:
495
496                                        #self.log.debug("Connection lost for a non-existent component? Weird.")
497                                        #if self.DEBUG:
498                                                #print "Connection lost for a non-existent component? Weird."
499
500
501        ##################################################################
502
503        #def stop_oldest_component(self):
504                ## return True if a component can be stopped
505                #oldest = {'last_active': time.time()}
506                #for each in self.registry['components']:
507                        #component = self.registry['components'][each]
508                       
509                        #if not component['active'] and \
510                                        #component['last_active'] < oldest['last_active']:
511                                #oldest = component
512
513                #if 'pid' in oldest:
514                        ## in this case restart just means kill since the component is not currently active
515                        #return self.restart_component(oldest['pid'])
516
517                #return False
518
519
520        ##################################################################
521
522        #def spawn_new_component(self, instruction, d):
523
524                #if len(self.registry['components']) >= MAX_COMPONENTS and \
525                                #not self.stop_oldest_component():
526                        #self.log.error("Max components exceeded, not spawning any new ones.")
527                        #if self.DEBUG:
528                                #print "Max components exceeded, not spawning any new ones."
529                        #return
530
531                #if instruction['command'] == 'load_url':
532
533                        #information = instruction['information']
534
535##                      command = '/usr/bin/screen -S web_browser ' + \
536##                                              '%s %i %i %i %i %i %i %i %i %i %s' % \
537                        #command = '%s %i %i %i %i %i %i %i %i %i %i %s' % \
538                                                #('/usr/bin/puzzlebox_web_browser.py', \
539                                                #information['window_id'], \
540                                                #information['display_pos_x'], \
541                                                #information['display_pos_y'], \
542                                                #information['window_size_x'], \
543                                                #information['window_size_y'], \
544                                                #information['display_fullscreen'], \
545                                                #information['refresh_interval'], \
546                                                #information['duration'], \
547                                                #information['hide_on_duration_expire'], \
548                                                #information['autobit_white_flash_fix'], \
549                                                #information['url'])
550
551                        #args = command.strip().split()
552                        #file = args[0]
553
554                        #self.log.debug("Spawning process from command: [%s]" % command)
555                        #if self.DEBUG:
556                                #print "Spawning process from command: [%s]" % command
557
558                        #pp = puzzlebox_mcp_browser_pp(self.log, instruction, self.registry, \
559                                                #self.spawn_new_component, self.restart_component, d, self.DEBUG)
560                        #process = reactor.spawnProcess(pp, file, args, os.environ, usePTY=True)
561
562                #else:
563                        #self.log.debug("Not spawning process because command is '%s'" % instruction['command'])
564                        #if self.DEBUG:
565                                #print "Not spawning process because command is '%s'" % instruction['command']
566
567
568#####################################################################
569# Process Protocol
570#####################################################################
571
572#class puzzlebox_mcp_browser_pp(protocol.ProcessProtocol):
573
574        #def __init__(self, log, \
575                          #instruction, \
576                          #registry, \
577                          #spawn_new_component, \
578                          #restart_component, \
579                          #d, \
580                          #DEBUG=DEBUG):
581               
582                #self.log = log
583                #self.instruction = instruction
584                #self.information = self.instruction['information']
585                #self.registry = registry
586                #self.spawn_new_component = spawn_new_component
587                #self.restart_component = restart_component
588                #self.d = d
589                #self.DEBUG = DEBUG
590
591
592        ##################################################################
593
594        #def connectionMade(self):
595                #self.pid = self.transport.pid
596                #self.registry['components'][self.pid] = self.instruction['information']
597                #self.registry['components'][self.pid]['pid'] = self.pid
598                #self.registry['components'][self.pid]['active'] = True
599                #self.registry['components'][self.pid]['callback'] = self.d
600
601                #if int(self.information['enable_autobit']) == 1:
602                        #self.log.debug("Window %i started with autobit enabled." % self.information['window_id'])
603                        #if self.DEBUG:
604                                #print "Window %i started with autobit enabled." % self.information['window_id']
605                        #self.registry['components'][self.pid]['autobit'] = \
606                                #reactor.callLater(AUTOBIT_TIMEOUT, self.restart_component, self.pid)
607
608                #self.log.debug("Sub-process started with pid %i" % self.pid)
609                #if self.DEBUG:
610                        #print "Sub-process started with pid %i" % self.pid
611
612
613        ##################################################################
614
615        #def childDataReceived(self, childFD, data):
616       
617                ## attempt to log any errors... might not work for exceptions
618                #self.log.error(data)
619               
620                #if self.DEBUG:
621                        #print data
622
623
624        ##################################################################
625
626        #def processEnded(self, status_object):
627
628                #self.log.debug("Sub-process with pid %i ended" % self.pid)
629                #if self.DEBUG:
630                        #print "Sub-process with pid %i ended" % self.pid
631
632                ## this is kind of unnecessary as i could just use
633                ## component = self.instruction['information']
634                ## but for now i want to make sure it still exists in registry too
635                #if self.pid in self.registry['components']:
636
637                        #component = self.registry['components'][self.pid]
638
639                        #if component['active']:
640                                #self.log.debug("Component is still active, respawning...")
641                                #if self.DEBUG:
642                                        #print "Component is still active, respawning..."
643##                              for key in self.instruction['information']:
644##                                      self.instruction['information'][key] = component[key]
645                                #self.spawn_new_component(self.instruction, component['callback'])
646                        #else:
647                                #self.log.debug("Component inactive, not respawning.")
648                                #if self.DEBUG:
649                                        #print "Component inactive, not respawning."
650
651                        ## clean up the old component data in either case
652                        ## and remove gtkmoz profile
653                        #del(self.registry['components'][self.pid])
654                        #profile_dir = os.path.join(GTKMOZ_PROFILE_DIR, "%s" % self.pid)
655                        #if os.path.exists(profile_dir):
656                                #command = 'rm -rf %s' % profile_dir
657                                #self.log.debug("Executing command: [%s]" % command)
658                                #if self.DEBUG:
659                                        #print "Executing command: [%s]" % command
660                                #os.system(command)
661
662                #else:
663                        #self.log.error("Could not find component associated with this sub-process. Odd.")
664                        #if self.DEBUG:
665                                #print "Could not find component associated with this sub-process. Odd."
666
667
668#####################################################################
669# Protocol
670#####################################################################
671
672class puzzlebox_master_control_server_protocol(protocol.Protocol):
673
674        def __init__(self):
675                self.instruction = {}
676                self.data_chunk = ""
677
678
679        ##################################################################
680
681        def dataReceived(self, data):
682
683                self.data_chunk += data
684                try:
685                        self.instruction = pickle.loads(self.data_chunk)
686                except Exception, e:
687                        self.factory.log.error("Partial data received (or error: %s)." % e)
688                else:
689                        self.data_chunk = ""
690
691                        d = self.factory.process_instruction(self.instruction.copy())
692                        d.addCallback(self.send_response)
693
694
695        ##################################################################
696
697        def send_response(self, response):
698
699                #if response == "browser exit":
700                        #self.instruction['command'] = response
701
702                response = pickle.dumps(response)
703
704                self.transport.write(response)
705
706
707        ##################################################################
708
709        def connectionLost(self, reason):
710       
711                self.factory.process_connection_lost(self.instruction)
712
713
714#####################################################################
715# Main
716#####################################################################
717
718if __name__ == '__main__':
719
720        #log = puzzlebox_logger.puzzlebox_logger(logfile='master_control')
721        log = None
722
723        # Collect default settings and command line parameters
724        server_host = SERVER_HOST
725        server_port = SERVER_PORT
726       
727        for each in sys.argv:
728               
729                if each.startswith("--host="):
730                        server_host = each[ len("--host="): ]
731                if each.startswith("--port="):
732                        server_port = each[ len("--port="): ]
733
734        mcp = puzzlebox_master_control(log, DEBUG)
735        reactor.listenTCP(port=server_port, factory=mcp, interface=server_host)
736        reactor.run()
737
Note: See TracBrowser for help on using the repository browser.