From 8162cc84680a7afdbf61cb93dc6f7d7b93e20123 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Tue, 16 May 2006 16:32:08 -0400 Subject: [PATCH] Do not rely on dbus auto-activation. --- README | 4 + configure.ac | 3 +- sugar/Makefile.am | 2 +- sugar/__uninstalled__.py | 3 +- sugar/browser/Makefile.am | 14 - .../com.redhat.Sugar.Browser.service.in | 3 - sugar/p2p/MostlyReliablePipe.py | 319 ------------------ sugar/session/Makefile.am | 4 + sugar/session/__init__.py | 0 sugar/session/session.py | 18 + sugar/shell/Makefile.am | 16 - sugar/shell/com.redhat.Sugar.Shell.service.in | 3 - sugar/shell/shell.py | 5 - sugar/sugar | 36 +- 14 files changed, 44 insertions(+), 386 deletions(-) delete mode 100644 sugar/browser/com.redhat.Sugar.Browser.service.in delete mode 100644 sugar/p2p/MostlyReliablePipe.py create mode 100644 sugar/session/Makefile.am create mode 100644 sugar/session/__init__.py create mode 100644 sugar/session/session.py delete mode 100644 sugar/shell/com.redhat.Sugar.Shell.service.in diff --git a/README b/README index 5d8d3a47..4458b744 100644 --- a/README +++ b/README @@ -24,6 +24,10 @@ Once installed you can run sugar with To run the python sources from your source tree run + $ sugar/sugar + +You can also run the components separately: + $ source ./setup-run-from-source.sh # needs bash in the top-level directory. Icons and other resources are still loaded diff --git a/configure.ac b/configure.ac index 10a350da..aca70654 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([Sugar],[0.3],[],[sugar]) +AC_INIT([Sugar],[0.2],[],[sugar]) AC_PREREQ([2.59]) @@ -24,4 +24,5 @@ sugar/chat/Makefile sugar/p2p/Makefile sugar/p2p/model/Makefile sugar/shell/Makefile +sugar/session/Makefile ]) diff --git a/sugar/Makefile.am b/sugar/Makefile.am index 626a56b0..cb4eb03f 100644 --- a/sugar/Makefile.am +++ b/sugar/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = chat browser p2p shell +SUBDIRS = chat browser p2p shell session bin_SCRIPTS = sugar diff --git a/sugar/__uninstalled__.py b/sugar/__uninstalled__.py index 4ffafc83..dd7818ed 100644 --- a/sugar/__uninstalled__.py +++ b/sugar/__uninstalled__.py @@ -1,10 +1,9 @@ import os +basedir = os.path.dirname(os.path.dirname(__file__)) data_dirs = [ 'sugar/browser', 'sugar/chat' ] def internal_get_data_file(filename): - basedir = os.path.dirname(os.path.dirname(__file__)) - for data_dir in data_dirs: path = os.path.abspath(os.path.join(basedir, data_dir, filename)) if os.path.isfile(path): diff --git a/sugar/browser/Makefile.am b/sugar/browser/Makefile.am index b4331778..b6440e5f 100644 --- a/sugar/browser/Makefile.am +++ b/sugar/browser/Makefile.am @@ -8,19 +8,5 @@ icon_DATA = \ fold.png \ unfold.png -# Dbus service file -servicedir = $(datadir)/dbus-1/services -service_in_files = com.redhat.Sugar.Browser.service.in -service_DATA = $(service_in_files:.service.in=.service) - -# Rule to make the service file with bindir expanded -$(service_DATA): $(service_in_files) Makefile - @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ - EXTRA_DIST = \ - $(service_in_files) \ - $(service_DATA) \ $(icon_DATA) - -DISTCLEANFILES = \ - $(service_DATA) diff --git a/sugar/browser/com.redhat.Sugar.Browser.service.in b/sugar/browser/com.redhat.Sugar.Browser.service.in deleted file mode 100644 index 654095a6..00000000 --- a/sugar/browser/com.redhat.Sugar.Browser.service.in +++ /dev/null @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=com.redhat.Sugar.Browser -Exec=@bindir@/sugar browser diff --git a/sugar/p2p/MostlyReliablePipe.py b/sugar/p2p/MostlyReliablePipe.py deleted file mode 100644 index f85a815a..00000000 --- a/sugar/p2p/MostlyReliablePipe.py +++ /dev/null @@ -1,319 +0,0 @@ -import socket -import time -import sha -import struct -import StringIO -import binascii - -import pygtk -pygtk.require('2.0') -import gtk, gobject - - -_MTU = 482 -_HEADER_LEN = 30 -_MAGIC = 0xbaea4304 -_TTL = 120 # 2 minutes - -def _stringify_sha(sha): - print_sha = "" - for char in sha: - print_sha = print_sha + binascii.b2a_hex(char) - return print_sha - -def _sha_data(data): - sha_hash = sha.new() - sha_hash.update(data) - return sha_hash.digest() - -class MessageSegment(object): - # 4: magic (0xbaea4304) - # 2: segment number - # 2: total segments - # 2: message sequence number - #20: total data sha1 - _HEADER_TEMPLATE = "! IHHH20s" - - def _new_from_parts(self, msg_seq_num, segno, total_segs, data, master_sha): - """Construct a new message segment from individual attributes.""" - if not data: - raise ValueError("Must have valid data.") - if segno > 65535: - raise ValueError("Segment number cannot be more than 65535.") - if segno < 1: - raise ValueError("Segment number must be greater than zero.") - if total_segs > 65535: - raise ValueError("Message cannot have more than 65535 segments.") - if total_segs < 1: - raise ValueError("Message must have at least one segment.") - if msg_seq_num < 1: - raise ValueError("Message sequence number must be greater than 0.") - self._stime = time.time() - self._data = data - self._data_len = len(data) - self._master_sha = master_sha - self._segno = segno - self._total_segs = total_segs - self._msg_seq_num = msg_seq_num - self._addr = None - - # Make the header - self._header = struct.pack(self._HEADER_TEMPLATE, _MAGIC, self._segno, - self._total_segs, self._msg_seq_num, self._master_sha) - - def _new_from_data(self, addr, data): - """Verify and construct a new message segment from network data.""" - if len(data) < _HEADER_LEN + 1: - raise ValueError("Message is less then minimum required length") - stream = StringIO.StringIO(data) - self._stime = None - self._addr = addr - - # Determine and verify the length of included data - stream.seek(0, 2) - header_size = struct.calcsize(self._HEADER_TEMPLATE) - self._data_len = stream.tell() - header_size - if self._data_len < 1: - raise ValueError("Message must have some data.") - if self._data_len > _MTU: - raise ValueError("Data length must not be larger than the MTU (%s)." % _MTU) - stream.seek(0) - - # Read the header attributes - (magic, segno, total_segs, msg_seq_num, master_sha) = struct.unpack(self._HEADER_TEMPLATE, - stream.read(header_size)) - - # Sanity checks on the message attributes - if magic != _MAGIC: - raise ValueError("Message does not have the correct magic.") - if segno < 1: - raise ValueError("Segment number must be greater than 0.") - if segno > total_segs: - raise ValueError("Segment number cannot be larger than message segment total.") - if total_segs < 1: - raise ValueError("Message must have at least one segment.") - if msg_seq_num < 1: - raise ValueError("Message sequence number must be greater than 0.") - - self._segno = segno - self._total_segs = total_segs - self._msg_seq_num = msg_seq_num - self._master_sha = master_sha - - # Reconstruct the data - self._data = struct.unpack("! %ds" % self._data_len, stream.read(self._data_len))[0] - - def new_from_parts(msg_seq_num, segno, total_segs, data, master_sha): - """Static constructor for creation from individual attributes.""" - segment = MessageSegment() - segment._new_from_parts(msg_seq_num, segno, total_segs, data, master_sha) - return segment - new_from_parts = staticmethod(new_from_parts) - - def new_from_data(addr, data): - """Static constructor for creation from a packed data stream.""" - segment = MessageSegment() - segment._new_from_data(addr, data) - return segment - new_from_data = staticmethod(new_from_data) - - def stime(self): - return self._stime - - def addr(self): - return self._addr - - def segment_number(self): - return self._segno - - def total_segments(self): - return self._total_segs - - def message_sequence_number(self): - return self._msg_seq_num - - def data(self): - return self._data - - def master_sha(self): - return self._master_sha - - def segment(self): - """Return a correctly formatted message that can be immediately sent.""" - return self._header + self._data - -class MostlyReliablePipe(object): - """Implement Mostly-Reliable UDP. We don't actually care about guaranteeing - delivery or receipt, just a better effort than no effort at all.""" - - def __init__(self, local_addr, remote_addr, port, data_cb, user_data=None): - self._local_addr = local_addr - self._remote_addr = remote_addr - self._port = port - self._data_cb = data_cb - self._user_data = user_data - self._started = False - self._worker = 0 - self._seq_counter = 0 - - self._outgoing = [] - self._sent = [] - - self._incoming = {} # (message sha, # of segments) -> [segment1, segment2, ...] - - self._setup_listener() - self._setup_sender() - - def _setup_sender(self): - """Setup the send socket for multicast.""" - self._send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Make the socket multicast-aware, and set TTL. - self._send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Change TTL (=20) to suit - - def _setup_listener(self): - """Set up the listener socket for multicast traffic.""" - # Listener socket - self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - # Set some options to make it multicast-friendly - self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) - - def start(self): - """Let the listener socket start listening for network data.""" - # Set some more multicast options - self._listen_sock.bind((self._local_addr, self._port)) # Bind to all interfaces - self._listen_sock.settimeout(2) - intf = socket.gethostbyname(socket.gethostname()) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, - socket.inet_aton(intf) + socket.inet_aton('0.0.0.0')) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, - socket.inet_aton(self._remote_addr) + socket.inet_aton('0.0.0.0')) - - # Watch the listener socket for data - gobject.io_add_watch(self._listen_sock, gobject.IO_IN, self._handle_incoming_data) - gobject.timeout_add(120000, self._segment_ttl_worker) - - self._started = True - - def _segment_ttl_worker(self): - """Cull already-sent message segments that are past their TTL.""" - now = time.time() - for segment in self._sent[:]: - if segment.stime() < now - _MSG_TTL: - self._sent.remove(segment) - return True - - def _dispatch_message(self, addr, message): - """Send complete message data to the owner's data callback.""" - self._data_cb(addr, message, self._user_data) - - def _process_incoming(self, segment): - """Handle a new message segment. First checks if there is only one - segment to the message, and if the checksum from the header matches - that computed from the data, dispatches it. Otherwise, it adds the - new segment to the list of other segments for that message, and - checks to see if the message is complete. If all segments are present, - the message is reassembled and dispatched.""" - - string_sha = _stringify_sha(segment.master_sha()) - nsegs = segment.total_segments() - addr = segment.addr() - segno = segment.segment_number() - - # Short-circuit single-segment messages - if segno == 1 and nsegs == 1: - # Ensure the header's master sha actually equals the data's sha - if string_sha == _stringify_sha(_sha_data(segment.data())): - self._dispatch_message(addr, segment.data()) - return - - # Otherwise, track the new segment - msg_seq_num = segment.message_sequence_number() - msg_key = (addr[0], msg_seq_num, string_sha, nsegs) - if not self._incoming.has_key(msg_key): - self._incoming[msg_key] = {} - - # Look for a dupe, and if so, drop the new segment - if self._incoming[msg_key].has_key(segno): - return - self._incoming[msg_key][segno] = segment - - # Dispatch the message if all segments are present and the sha is correct - if len(self._incoming[msg_key]) == nsegs: - all_data = '' - for i in range(1, nsegs + 1): - all_data = all_data + self._incoming[msg_key][i].data() - if string_sha == _stringify_sha(_sha_data(all_data)): - self._dispatch_message(addr, all_data) - del self._incoming[msg_key] - - def _handle_incoming_data(self, source, condition): - """Handle incoming network data by making a message segment out of it - sending it off to the processing function.""" - if not (condition & gobject.IO_IN): - return True - msg = {} - data, addr = source.recvfrom(_MTU + _HEADER_LEN) - try: - segment = MessageSegment.new_from_data(addr, data) - self._process_incoming(segment) - except ValueError, exc: - pass - return True - - def send(self, data): - """Break data up into chunks and queue for later transmission.""" - if not self._started: - raise Exception("Can't send anything until started!") - - self._seq_counter = self._seq_counter + 1 - if self._seq_counter > 65535: - self._seq_counter = 1 - - # Pack the data into network byte order - template = "! %ds" % len(data) - data = struct.pack(template, data) - master_sha = _sha_data(data) - - # Split up the data into segments - left = length = len(data) - nmessages = length / _MTU - if length % _MTU > 0: - nmessages = nmessages + 1 - msg_num = 1 - while left > 0: - msg = MessageSegment.new_from_parts(self._seq_counter, msg_num, - nmessages, data[:_MTU], master_sha) - self._outgoing.append(msg) - msg_num = msg_num + 1 - data = data[_MTU:] - left = left - _MTU - if len(self._outgoing) > 0 and self._worker == 0: - self._worker = gobject.idle_add(self._send_worker) - - def _send_worker(self): - """Send all queued segments that have yet to be transmitted.""" - self._worker = 0 - for segment in self._outgoing: - data = segment.segment() - self._send_sock.sendto(data, (self._remote_addr, self._port)) - self._sent = self._outgoing - self._outgoing = [] - return False - - -def got_data(addr, data, user_data=None): - print "Data (%s): %s" % (addr, data) - -def main(): - pipe = MostlyReliablePipe('', '224.0.0.222', 2293, got_data) - pipe.start() - pipe.send('The quick brown fox jumps over the lazy dog') - gtk.main() - - -if __name__ == "__main__": - main() - diff --git a/sugar/session/Makefile.am b/sugar/session/Makefile.am new file mode 100644 index 00000000..db749876 --- /dev/null +++ b/sugar/session/Makefile.am @@ -0,0 +1,4 @@ +sugardir = $(pythondir)/sugar/session +sugar_PYTHON = \ + __init__.py \ + session.py diff --git a/sugar/session/__init__.py b/sugar/session/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sugar/session/session.py b/sugar/session/session.py new file mode 100644 index 00000000..c3043af7 --- /dev/null +++ b/sugar/session/session.py @@ -0,0 +1,18 @@ +import os +import sys + +import pygtk +pygtk.require('2.0') +import gtk + +from sugar.shell import shell + +def start(): + shell.main() + + activities = ['sugar/chat/chat', 'sugar/browser/browser'] + + for activity in activities: + os.spawnvp(os.P_NOWAIT, 'python', [ 'python', '-m', activity ]) + + gtk.main() diff --git a/sugar/shell/Makefile.am b/sugar/shell/Makefile.am index f7652957..a768aad6 100644 --- a/sugar/shell/Makefile.am +++ b/sugar/shell/Makefile.am @@ -3,19 +3,3 @@ sugar_PYTHON = \ __init__.py \ activity.py \ shell.py - -# Dbus service file -servicedir = $(datadir)/dbus-1/services -service_in_files = com.redhat.Sugar.Shell.service.in -service_DATA = $(service_in_files:.service.in=.service) - -# Rule to make the service file with bindir expanded -$(service_DATA): $(service_in_files) Makefile - @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ - -EXTRA_DIST = \ - $(service_in_files) \ - $(service_DATA) - -DISTCLEANFILES = \ - $(service_DATA) diff --git a/sugar/shell/com.redhat.Sugar.Shell.service.in b/sugar/shell/com.redhat.Sugar.Shell.service.in deleted file mode 100644 index 2a069a17..00000000 --- a/sugar/shell/com.redhat.Sugar.Shell.service.in +++ /dev/null @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=com.redhat.Sugar.Shell -Exec=@bindir@/sugar shell diff --git a/sugar/shell/shell.py b/sugar/shell/shell.py index 0dd3ea95..a72a864f 100755 --- a/sugar/shell/shell.py +++ b/sugar/shell/shell.py @@ -294,8 +294,3 @@ def main(): activityContainer = ActivityContainer(service, session_bus) activityContainer.show() - - gtk.main() - -if __name__ == "__main__": - main() diff --git a/sugar/sugar b/sugar/sugar index 4c6ec886..5d421183 100755 --- a/sugar/sugar +++ b/sugar/sugar @@ -3,26 +3,18 @@ import sys import os -if len(sys.argv) == 1: - # FIXME Start a session - - # We are lucky and this - # currently behave as we want. - # The chat depends on the - # web browser, so both activities - # are spanned. But obviously we - # need something better. - - from sugar.chat import chat - chat.main() -elif sys.argv[1] == 'shell': - from sugar.shell import shell - shell.main() -elif sys.argv[1] == 'chat': - from sugar.chat import chat - chat.main() -elif sys.argv[1] == 'browser': - from sugar.browser import browser - browser.main() +basedir = os.path.dirname(os.path.dirname(__file__)) +if os.path.isfile(os.path.join(basedir, 'sugar', '__uninstalled__.py')): + if basedir == '': + print "Running sugar from current directory..." + else: + print "Running sugar from " + basedir + " ..." + sys.path.append(basedir) + os.environ['PYTHONPATH'] = basedir else: - print "Unknown activity" + print "Running the installed sugar..." + +from sugar.session import session + +session.start() +