diff -rN -u old-tahoe-maybestarted-71/src/allmydata/client.py new-tahoe-maybestarted-71/src/allmydata/client.py
--- old-tahoe-maybestarted-71/src/allmydata/client.py	2007-11-08 10:53:54.000000000 -0800
+++ new-tahoe-maybestarted-71/src/allmydata/client.py	2007-11-08 10:53:54.000000000 -0800
@@ -136,6 +136,7 @@
         ic.setServiceParent(self)
 
         self.register_control()
+        self.log("The node is now running.")
 
     def register_control(self):
         c = ControlServer()
diff -rN -u old-tahoe-maybestarted-71/src/allmydata/introducer.py new-tahoe-maybestarted-71/src/allmydata/introducer.py
--- old-tahoe-maybestarted-71/src/allmydata/introducer.py	2007-11-08 10:53:54.000000000 -0800
+++ new-tahoe-maybestarted-71/src/allmydata/introducer.py	2007-11-08 10:53:54.000000000 -0800
@@ -58,12 +58,17 @@
         service.Service.startService(self)
         self.introducer_reconnector = self.tub.connectTo(self.introducer_furl,
                                                          self._got_introducer)
+        # the reconnector does all the actual work. We do a separate
+        # getReference here just for logging the first attempt
+        def connect_succeeded(res):
+            self.log("Initial Introducer connection succeeded: "
+                     "we are now on the grid.")
         def connect_failed(failure):
+            self.log(str(failure))
             self.log("\n\nInitial Introducer connection failed: "
                      "perhaps it's down\n")
-            self.log(str(failure))
         d = self.tub.getReference(self.introducer_furl)
-        d.addErrback(connect_failed)
+        d.addCallbacks(connect_succeeded, connect_failed)
 
     def log(self, msg):
         self.parent.log(msg)
diff -rN -u old-tahoe-maybestarted-71/src/allmydata/scripts/runner.py new-tahoe-maybestarted-71/src/allmydata/scripts/runner.py
--- old-tahoe-maybestarted-71/src/allmydata/scripts/runner.py	2007-11-08 10:53:54.000000000 -0800
+++ new-tahoe-maybestarted-71/src/allmydata/scripts/runner.py	2007-11-08 10:53:54.000000000 -0800
@@ -34,6 +34,7 @@
 
     command = config.subCommand
     so = config.subOptions
+    so['quiet'] = config['quiet']
 
     if config['quiet']:
         stdout = StringIO()
diff -rN -u old-tahoe-maybestarted-71/src/allmydata/scripts/startstop_node.py new-tahoe-maybestarted-71/src/allmydata/scripts/startstop_node.py
--- old-tahoe-maybestarted-71/src/allmydata/scripts/startstop_node.py	2007-11-08 10:53:54.000000000 -0800
+++ new-tahoe-maybestarted-71/src/allmydata/scripts/startstop_node.py	2007-11-08 10:53:54.000000000 -0800
@@ -28,6 +28,13 @@
         ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
         ]
 
+def do_start2(basedir, config):
+    from allmydata.scripts import startup
+    fileutil.make_dirs(os.path.join(basedir, "logs"))
+    startup.start(basedir, config)
+
+
+
 def do_start(basedir, profile=False, out=sys.stdout, err=sys.stderr):
     print >>out, "STARTING", basedir
     if os.path.exists(os.path.join(basedir, "client.tac")):
@@ -125,8 +132,11 @@
 
 def start(config, stdout, stderr):
     rc = 0
-    for basedir in config['basedirs']:
-        rc = do_start(basedir, config['profile'], stdout, stderr) or rc
+    if len(config['basedirs']) > 1:
+        for basedir in config['basedirs']:
+            rc = do_start(basedir, config['profile'], stdout, stderr) or rc
+    else:
+        rc = do_start2(config['basedirs'][0], config)
     return rc
 
 def stop(config, stdout, stderr):
diff -rN -u old-tahoe-maybestarted-71/src/allmydata/scripts/startup.py new-tahoe-maybestarted-71/src/allmydata/scripts/startup.py
--- old-tahoe-maybestarted-71/src/allmydata/scripts/startup.py	1969-12-31 16:00:00.000000000 -0800
+++ new-tahoe-maybestarted-71/src/allmydata/scripts/startup.py	2007-11-08 10:53:54.000000000 -0800
@@ -0,0 +1,210 @@
+
+import os, sys, time
+from twisted.internet import task, defer, reactor
+from twisted.python.runtime import platformType
+from twisted.python.failure import Failure
+from twisted.protocols.basic import LineOnlyReceiver
+
+class ClientTimeoutError(Exception):
+    pass
+class ClientFailedToConnectError(Exception):
+    pass
+
+class FakeTransport:
+    disconnecting = False
+
+class LogWatcher(LineOnlyReceiver):
+    POLL_INTERVAL = 0.1
+    TIMEOUT_DELAY = 10.0
+    delimiter = os.linesep
+
+    def __init__(self, logfile, processtype):
+        self.logfile = logfile
+        self.in_startup = False
+        self.transport = FakeTransport()
+        self.f = None
+        self.processtype = processtype
+
+    def start(self):
+        # return a Deferred that fires when the startup process has finished.
+        # It errbacks with TimeoutError if the finish line has not been seen
+        # within 10 seconds, and with ClientFailedToConnectError if the error
+        # line was seen. If the logfile could not be opened, it errbacks with
+        # an IOError.
+        self.running = True
+        d = defer.maybeDeferred(self._start)
+        return d
+
+    def _start(self):
+        self.d = defer.Deferred()
+        try:
+            self.f = open(self.logfile, "rb")
+            self.f.seek(0, 2) # start watching from the end
+        except IOError:
+            pass
+        reactor.callLater(self.TIMEOUT_DELAY, self.timeout)
+        self.poller = task.LoopingCall(self.poll)
+        self.poller.start(self.POLL_INTERVAL)
+        return self.d
+
+    def timeout(self):
+        self.d.errback(ClientTimeoutError())
+
+    def finished(self, results):
+        self.running = False
+        self.in_startup = False
+        self.d.callback(results)
+
+    def lineReceived(self, line):
+        if not self.running:
+            return
+        if "Node constructed.  tahoe version:" in line:
+            self.in_startup = True
+        if self.processtype == "client":
+            if "Initial Introducer connection succeeded:" in line:
+                print "CHILD:", line
+                self.finished(self.processtype)
+                return
+            if "Initial Introducer connection failed:" in line:
+                print "CHILD:", line
+                self.finished(Failure(ClientFailedToConnectError()))
+                return
+        else:
+            if "The node is now running." in line:
+                print "CHILD:", line
+                self.finished(self.processtype)
+                return
+
+        if self.in_startup:
+            print "CHILD:", line
+
+    def poll(self):
+        if not self.f:
+            try:
+                self.f = open(self.logfile, "rb")
+            except IOError:
+                return
+        while True:
+            data = self.f.read(1000)
+            if not data:
+                return
+            self.dataReceived(data)
+
+class Follower:
+    def follow(self, logfile, processtype):
+        self.rc = 0
+        print "Following twistd.log until startup finished.."
+        lw = LogWatcher(logfile, processtype)
+        d = lw.start()
+        d.addCallbacks(self._success, self._failure)
+        reactor.run()
+        return self.rc
+
+    def _success(self, processtype):
+        print "The %s appears to have (re)started correctly." % processtype
+        self.rc = 0
+        reactor.stop()
+
+    def _failure(self, why):
+        if why.check(ClientTimeoutError):
+            print """
+The node took more than 10 seconds to start, so we were unable to confirm
+that it started correctly. Please 'tail logs/twistd.log' and look for a line
+that says 'The node is now running.' to verify correct startup.
+"""
+        else:
+            print """
+Unable to confirm that the node started correctly. You may need to stop it,
+modify the configuration, and restart.
+"""
+            print why
+        self.rc = 1
+        reactor.stop()
+
+def get_node_type(basedir):
+    if os.path.exists(os.path.join(basedir, "client.tac")):
+        tac = "client.tac"
+        processtype = "client"
+    elif os.path.exists(os.path.join(basedir, "tahoe-client.tac")):
+        tac = "tahoe-client.tac"
+        processtype = "client"
+    elif os.path.exists(os.path.join(basedir, "introducer.tac")):
+        tac = "introducer.tac"
+        processtype = "introducer"
+    elif os.path.exists(os.path.join(basedir, "tahoe-introducer.tac")):
+        tac = "tahoe-introducer.tac"
+        processtype = "introducer"
+    else:
+        tac = None
+        processtype = None
+    return tac, processtype
+
+def launch(basedir, config):
+    # we are in a child process, so nothing we do will affect the parent
+
+    # see if we can launch the application without actually having to
+    # spawn twistd, since spawning processes correctly is a real hassle
+    # on windows.
+    err = sys.stderr
+
+    tac, processtype = get_node_type(basedir)
+    if processtype == None:
+        print >>err, "%s does not look like a node directory" % basedir
+        if not os.path.isdir(basedir):
+            print >>err, " in fact, it doesn't look like a directory at all!"
+        return 1
+
+    os.chdir(basedir)
+
+    argv = ["twistd",
+            "--python", tac,
+            "--logfile", os.path.join("logs", "twistd.log"),
+            ]
+    if platformType == "win32":
+        argv.append("--reactor=win32")
+    sys.argv = argv
+    #print "in child, argv is", argv
+
+    if True:
+        # this approach requires twisted-2.5.0
+        from twisted.scripts import twistd
+        run = twistd.run
+    else:
+        # this is copied from bin/twistd. twisted-2.0.0 uses _twistw.
+        if platformType == "win32":
+            from twisted.scripts import _twistw
+            run = _twistw.run
+        else:
+            from twisted.scripts import twistd
+            run = twistd.run
+    #print "in child, about to call run()"
+    run() # this never returns, right?
+    sys.exit(0) # just in case it does
+
+
+def start(basedir, config):
+    if config['quiet']:
+        return launch(basedir, config)
+
+    # we probably can't do this os.fork under windows
+    if platformType == "win32":
+        return launch(basedir, config)
+
+    # fork a child to launch the daemon, while the parent process tails the
+    # logfile
+    if os.fork():
+        # this is the parent
+        logfile = os.path.join(basedir, "logs", "twistd.log")
+        tac, processtype = get_node_type(basedir)
+        rc = Follower().follow(logfile, processtype)
+        sys.exit(rc)
+    # this is the child: give the logfile-watching parent a chance to start
+    # watching it before we start the daemon. We want to make extra sure that
+    # the child never returns.
+    try:
+        time.sleep(0.2)
+        launch(basedir, config)
+    except:
+        sys.exit(-1)
+
+

