From c21d30c320ee8bd281965eb8e979c5f678b0de5d Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@allmydata.com>
Date: Tue, 11 Mar 2008 19:20:10 -0700
Subject: [PATCH] client: publish a 'stub client' announcement to the
 introducer, to provide version/nickname information for each client

---
 src/allmydata/client.py     | 31 +++++++++++++++++++++++++------
 src/allmydata/interfaces.py |  7 +++++++
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/src/allmydata/client.py b/src/allmydata/client.py
index 732d2074..ba857d77 100644
--- a/src/allmydata/client.py
+++ b/src/allmydata/client.py
@@ -3,8 +3,10 @@ import os, stat, time, re
 from allmydata.interfaces import RIStorageServer
 from allmydata import node
 
+from zope.interface import implements
 from twisted.internet import reactor
 from twisted.application.internet import TimerService
+from foolscap import Referenceable
 from foolscap.logging import log
 
 import allmydata
@@ -21,7 +23,7 @@ from allmydata.dirnode import NewDirectoryNode
 from allmydata.mutable import MutableFileNode, MutableWatcher
 from allmydata.stats import StatsProvider
 from allmydata.interfaces import IURI, INewDirectoryURI, \
-     IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI
+     IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI, RIStubClient
 
 KiB=1024
 MiB=1024*KiB
@@ -29,6 +31,9 @@ GiB=1024*MiB
 TiB=1024*GiB
 PiB=1024*TiB
 
+class StubClient(Referenceable):
+    implements(RIStubClient)
+
 class Client(node.Node, testutil.PollMixin):
     PORTNUMFILE = "client.port"
     STOREDIR = 'storage'
@@ -63,11 +68,7 @@ class Client(node.Node, testutil.PollMixin):
         run_helper = self.get_config("run_helper")
         if run_helper:
             self.init_helper()
-        helper_furl = self.get_config("helper.furl")
-        self.add_service(Uploader(helper_furl))
-        self.add_service(Downloader())
-        self.add_service(Checker())
-        self.add_service(MutableWatcher())
+        self.init_client()
         # ControlServer and Helper are attached after Tub startup
 
         hotline_file = os.path.join(self.basedir,
@@ -148,6 +149,24 @@ class Client(node.Node, testutil.PollMixin):
         d.addCallback(_publish)
         d.addErrback(log.err, facility="tahoe.init", level=log.BAD)
 
+    def init_client(self):
+        helper_furl = self.get_config("helper.furl")
+        self.add_service(Uploader(helper_furl))
+        self.add_service(Downloader())
+        self.add_service(Checker())
+        self.add_service(MutableWatcher())
+        def _publish(res):
+            # we publish an empty object so that the introducer can count how
+            # many clients are connected and see what versions they're
+            # running.
+            sc = StubClient()
+            furl = self.tub.registerReference(sc)
+            ri_name = RIStubClient.__remote_name__
+            self.introducer_client.publish(furl, "stub_client", ri_name)
+        d = self.when_tub_ready()
+        d.addCallback(_publish)
+        d.addErrback(log.err, facility="tahoe.init", level=log.BAD)
+
     def init_control(self):
         d = self.when_tub_ready()
         def _publish(res):
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index b7767f17..7c533d31 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -160,6 +160,13 @@ class IIntroducerClient(Interface):
         """Returns a boolean, True if we are currently connected to the
         introducer, False if not."""
 
+class RIStubClient(RemoteInterface):
+    """Each client publishes a service announcement for a dummy object called
+    the StubClient. This object doesn't actually offer any services, but the
+    announcement helps the Introducer keep track of which clients are
+    subscribed (so the grid admin can keep track of things like the size of
+    the grid and the client versions in use. This is the (empty)
+    RemoteInterface for the StubClient."""
 
 class RIBucketWriter(RemoteInterface):
     def write(offset=Offset, data=ShareData):
-- 
2.45.2