2 import os, sys, signal, time
3 from allmydata.scripts.common import BasedirMixin, BaseOptions
4 from allmydata.util import fileutil, find_exe
5 from allmydata.util.assertutil import precondition
6 from allmydata.util.encodingutil import listdir_unicode, quote_output
8 class StartOptions(BasedirMixin, BaseOptions):
10 ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."],
11 ["syslog", None, "Tell the node to log to syslog, not a file."],
14 class StopOptions(BasedirMixin, BaseOptions):
17 class RestartOptions(BasedirMixin, BaseOptions):
19 ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."],
20 ["syslog", None, "Tell the node to log to syslog, not a file."],
23 class RunOptions(BasedirMixin, BaseOptions):
24 default_nodedir = u"."
25 allow_multiple = False
28 ["node-directory", "d", None, "Specify the directory of the node to be run. [default, for 'tahoe run' only: current directory]"],
29 ["multiple", "m", None, "['tahoe run' cannot accept multiple node directories]"],
32 def do_start(basedir, opts, out=sys.stdout, err=sys.stderr):
33 print >>out, "STARTING", quote_output(basedir)
34 if not os.path.isdir(basedir):
35 print >>err, "%s does not look like a directory at all" % quote_output(basedir)
37 for fn in listdir_unicode(basedir):
38 if fn.endswith(u".tac"):
42 print >>err, "%s does not look like a node directory (no .tac file)" % quote_output(basedir)
46 elif "introducer" in tac:
47 nodetype = "introducer"
49 nodetype = "unknown (%s)" % tac
51 cmd = find_exe.find_exe('twistd')
53 # If 'twistd' wasn't on $PATH, maybe we're running from source and
54 # Twisted was built as one of our dependencies. If so, we're at
55 # BASEDIR/src/allmydata/scripts/startstop_node.py, and it's at
56 # BASEDIR/support/$BINDIR/twistd
58 TAHOEDIR = up(up(up(up(os.path.abspath(__file__)))))
59 if sys.platform == "win32":
63 bindir = os.path.join(TAHOEDIR, "support", bin_dir)
65 maybe = os.path.join(bindir, "twistd")
66 if os.path.exists(maybe):
68 oldpath = os.environ.get("PATH", "").split(os.pathsep)
69 os.environ["PATH"] = os.pathsep.join(oldpath + [bindir])
70 # sys.path and $PYTHONPATH are taken care of by the extra code in
74 if os.path.exists(maybe):
75 cmd = [sys.executable, maybe]
76 oldpath = os.environ.get("PATH", "").split(os.pathsep)
77 os.environ["PATH"] = os.pathsep.join(oldpath + [bindir])
78 # sys.path and $PYTHONPATH are taken care of by the extra code in
82 print "Can't find twistd (it comes with Twisted). Aborting."
85 cmd.extend(["-y", tac])
87 cmd.append("--syslog")
88 elif nodetype in ("client", "introducer"):
89 fileutil.make_dirs(os.path.join(basedir, "logs"))
90 cmd.extend(["--logfile", os.path.join("logs", "twistd.log")])
92 cmd.extend(["--profile=profiling_results.prof", "--savestats",])
96 rc = os.system(' '.join(cmd))
100 print >>out, "%s node probably started" % nodetype
103 print >>err, "%s node probably not started" % nodetype
106 def do_stop(basedir, out=sys.stdout, err=sys.stderr):
107 print >>out, "STOPPING", quote_output(basedir)
108 pidfile = os.path.join(basedir, "twistd.pid")
109 if not os.path.exists(pidfile):
110 print >>err, "%s does not look like a running node directory (no twistd.pid)" % quote_output(basedir)
111 # we define rc=2 to mean "nothing is running, but it wasn't me who
114 pid = open(pidfile, "r").read()
117 # kill it hard (SIGKILL), delete the twistd.pid file, then wait for the
118 # process itself to go away. If it hasn't gone away after 20 seconds, warn
119 # the user but keep waiting until they give up.
121 os.kill(pid, signal.SIGKILL)
122 except OSError, oserr:
125 # the process didn't exist, so wipe the pid file
132 except EnvironmentError:
139 # poll once per second until we see the process is no longer running
143 print >>out, "process %d is dead" % pid
148 print >>err, ("It looks like pid %d is still running "
149 "after %d seconds" % (pid,
150 (time.time() - start)))
151 print >>err, "I will keep watching it until you interrupt me."
155 print >>err, "pid %d still running after %d seconds" % \
156 (pid, (time.time() - start))
159 # we define rc=1 to mean "I think something is still running, sorry"
162 def start(config, stdout, stderr):
164 for basedir in config['basedirs']:
165 rc = do_start(basedir, config, stdout, stderr) or rc
168 def stop(config, stdout, stderr):
170 for basedir in config['basedirs']:
171 rc = do_stop(basedir, stdout, stderr) or rc
174 def restart(config, stdout, stderr):
176 for basedir in config['basedirs']:
177 rc = do_stop(basedir, stdout, stderr) or rc
179 print >>stderr, "ignoring couldn't-stop"
182 print >>stderr, "not restarting"
184 for basedir in config['basedirs']:
185 rc = do_start(basedir, config, stdout, stderr) or rc
188 def run(config, stdout, stderr):
189 from twisted.internet import reactor
190 from twisted.python import log, logfile
191 from allmydata import client
193 basedir = config['basedirs'][0]
194 precondition(isinstance(basedir, unicode), basedir)
196 if not os.path.isdir(basedir):
197 print >>stderr, "%s does not look like a directory at all" % quote_output(basedir)
199 for fn in listdir_unicode(basedir):
200 if fn.endswith(u".tac"):
204 print >>stderr, "%s does not look like a node directory (no .tac file)" % quote_output(basedir)
206 if "client" not in tac:
207 print >>stderr, ("%s looks like it contains a non-client node (%s).\n"
208 "Use 'tahoe start' instead of 'tahoe run'."
209 % (quote_output(basedir), tac))
214 # set up twisted logging. this will become part of the node rsn.
215 logdir = os.path.join(basedir, 'logs')
216 if not os.path.exists(logdir):
218 lf = logfile.LogFile('tahoesvc.log', logdir)
221 # run the node itself
222 c = client.Client(basedir)
223 reactor.callLater(0, c.startService) # after reactor startup
230 ["start", None, StartOptions, "Start a node (of any type)."],
231 ["stop", None, StopOptions, "Stop a node."],
232 ["restart", None, RestartOptions, "Restart a node."],
233 ["run", None, RunOptions, "Run a node synchronously."],