import os, sys, signal, time
-from allmydata.scripts.common import BasedirMixin, BaseOptions
-from allmydata.util import fileutil, find_exe
+from allmydata.scripts.common import BasedirOptions
+from allmydata.util import fileutil
+from allmydata.util.assertutil import precondition
+from allmydata.util.encodingutil import listdir_unicode, quote_output
-class StartOptions(BasedirMixin, BaseOptions):
- optParameters = [
- ["basedir", "C", None, "which directory to start the node in"],
- ]
+
+class StartOptions(BasedirOptions):
optFlags = [
- ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
- ["syslog", None, "tell the node to log to syslog, not a file"],
+ ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."],
+ ["syslog", None, "Tell the node to log to syslog, not a file."],
]
-class StopOptions(BasedirMixin, BaseOptions):
- optParameters = [
- ["basedir", "C", None, "which directory to stop the node in"],
- ]
+ def getSynopsis(self):
+ return "Usage: %s [global-opts] start [options] [NODEDIR]" % (self.command_name,)
-class RestartOptions(BasedirMixin, BaseOptions):
- optParameters = [
- ["basedir", "C", None, "which directory to restart the node in"],
- ]
+
+class StopOptions(BasedirOptions):
+ def getSynopsis(self):
+ return "Usage: %s [global-opts] stop [options] [NODEDIR]" % (self.command_name,)
+
+
+class RestartOptions(BasedirOptions):
optFlags = [
- ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
- ["syslog", None, "tell the node to log to syslog, not a file"],
+ ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."],
+ ["syslog", None, "Tell the node to log to syslog, not a file."],
]
-class RunOptions(BasedirMixin, BaseOptions):
+ def getSynopsis(self):
+ return "Usage: %s [global-opts] restart [options] [NODEDIR]" % (self.command_name,)
+
+
+class RunOptions(BasedirOptions):
default_nodedir = u"."
- optParameters = [
- ["basedir", "C", None, "which directory to run the node in, CWD by default"],
- ]
+ def getSynopsis(self):
+ return "Usage: %s [global-opts] run [options] [NODEDIR]" % (self.command_name,)
+
-def do_start(basedir, opts, out=sys.stdout, err=sys.stderr):
- print >>out, "STARTING", basedir
+def start(opts, out=sys.stdout, err=sys.stderr):
+ basedir = opts['basedir']
+ print >>out, "STARTING", quote_output(basedir)
if not os.path.isdir(basedir):
- print >>err, "%s does not look like a directory at all" % basedir
+ print >>err, "%s does not look like a directory at all" % quote_output(basedir)
return 1
- for fn in os.listdir(basedir):
- if fn.endswith(".tac"):
- tac = fn
+ for fn in listdir_unicode(basedir):
+ if fn.endswith(u".tac"):
+ tac = str(fn)
break
else:
- print >>err, "%s does not look like a node directory (no .tac file)" % basedir
+ print >>err, "%s does not look like a node directory (no .tac file)" % quote_output(basedir)
return 1
if "client" in tac:
nodetype = "client"
else:
nodetype = "unknown (%s)" % tac
- cmd = find_exe.find_exe('twistd')
- if not cmd:
- # If 'twistd' wasn't on $PATH, maybe we're running from source and
- # Twisted was built as one of our dependencies. If so, we're at
- # BASEDIR/src/allmydata/scripts/startstop_node.py, and it's at
- # BASEDIR/support/$BINDIR/twistd
- up = os.path.dirname
- TAHOEDIR = up(up(up(up(os.path.abspath(__file__)))))
- if sys.platform == "win32":
- bin_dir = "Scripts"
- else:
- bin_dir = "bin"
- bindir = os.path.join(TAHOEDIR, "support", bin_dir)
-
- maybe = os.path.join(bindir, "twistd")
- if os.path.exists(maybe):
- cmd = [maybe]
- oldpath = os.environ.get("PATH", "").split(os.pathsep)
- os.environ["PATH"] = os.pathsep.join(oldpath + [bindir])
- # sys.path and $PYTHONPATH are taken care of by the extra code in
- # 'setup.py trial'
- else:
- maybe = maybe+'.py'
- if os.path.exists(maybe):
- cmd = [sys.executable, maybe]
- oldpath = os.environ.get("PATH", "").split(os.pathsep)
- os.environ["PATH"] = os.pathsep.join(oldpath + [bindir])
- # sys.path and $PYTHONPATH are taken care of by the extra code in
- # 'setup.py trial'
-
- if not cmd:
- print "Can't find twistd (it comes with Twisted). Aborting."
- sys.exit(1)
-
- cmd.extend(["-y", tac])
+ args = ["twistd", "-y", tac]
if opts["syslog"]:
- cmd.append("--syslog")
+ args.append("--syslog")
elif nodetype in ("client", "introducer"):
fileutil.make_dirs(os.path.join(basedir, "logs"))
- cmd.extend(["--logfile", os.path.join("logs", "twistd.log")])
+ args.extend(["--logfile", os.path.join("logs", "twistd.log")])
if opts["profile"]:
- cmd.extend(["--profile=profiling_results.prof", "--savestats",])
- curdir = os.getcwd()
- try:
- os.chdir(basedir)
- rc = os.system(' '.join(cmd))
- finally:
- os.chdir(curdir)
- if rc == 0:
- print >>out, "%s node probably started" % nodetype
- return 0
- else:
- print >>err, "%s node probably not started" % nodetype
- return 1
-
-def do_stop(basedir, out=sys.stdout, err=sys.stderr):
- print >>out, "STOPPING", basedir
+ args.extend(["--profile=profiling_results.prof", "--savestats",])
+ # now we're committed
+ os.chdir(basedir)
+ from twisted.scripts import twistd
+ sys.argv = args
+ twistd.run()
+ # run() doesn't return: the parent does os._exit(0) in daemonize(), so
+ # we'll never get here. If application setup fails (e.g. ImportError),
+ # run() will raise an exception.
+
+def stop(config, out=sys.stdout, err=sys.stderr):
+ basedir = config['basedir']
+ print >>out, "STOPPING", quote_output(basedir)
pidfile = os.path.join(basedir, "twistd.pid")
if not os.path.exists(pidfile):
- print >>err, "%s does not look like a running node directory (no twistd.pid)" % basedir
+ print >>err, "%s does not look like a running node directory (no twistd.pid)" % quote_output(basedir)
# we define rc=2 to mean "nothing is running, but it wasn't me who
# stopped it"
return 2
# we define rc=1 to mean "I think something is still running, sorry"
return 1
-def start(config, stdout, stderr):
- rc = 0
- for basedir in config['basedirs']:
- rc = do_start(basedir, config, stdout, stderr) or rc
- return rc
-
-def stop(config, stdout, stderr):
- rc = 0
- for basedir in config['basedirs']:
- rc = do_stop(basedir, stdout, stderr) or rc
- return rc
-
def restart(config, stdout, stderr):
- rc = 0
- for basedir in config['basedirs']:
- rc = do_stop(basedir, stdout, stderr) or rc
+ rc = stop(config, stdout, stderr)
if rc == 2:
print >>stderr, "ignoring couldn't-stop"
rc = 0
if rc:
print >>stderr, "not restarting"
return rc
- for basedir in config['basedirs']:
- rc = do_start(basedir, config, stdout, stderr) or rc
- return rc
+ return start(config, stdout, stderr)
def run(config, stdout, stderr):
from twisted.internet import reactor
from allmydata import client
basedir = config['basedir']
- if basedir is None:
- basedir = '.'
+ precondition(isinstance(basedir, unicode), basedir)
+
+ if not os.path.isdir(basedir):
+ print >>stderr, "%s does not look like a directory at all" % quote_output(basedir)
+ return 1
+ for fn in listdir_unicode(basedir):
+ if fn.endswith(u".tac"):
+ tac = str(fn)
+ break
else:
- os.chdir(basedir)
+ print >>stderr, "%s does not look like a node directory (no .tac file)" % quote_output(basedir)
+ return 1
+ if "client" not in tac:
+ print >>stderr, ("%s looks like it contains a non-client node (%s).\n"
+ "Use 'tahoe start' instead of 'tahoe run'."
+ % (quote_output(basedir), tac))
+ return 1
+
+ os.chdir(basedir)
# set up twisted logging. this will become part of the node rsn.
logdir = os.path.join(basedir, 'logs')