2 import os, sys, signal, time
3 from twisted.python import usage
4 from allmydata.scripts.common import BasedirMixin
5 from allmydata.util import fileutil, find_exe
7 class StartOptions(BasedirMixin, usage.Options):
9 ["basedir", "C", None, "which directory to start the node in"],
12 ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
13 ["syslog", None, "tell the node to log to syslog, not a file"],
16 class StopOptions(BasedirMixin, usage.Options):
18 ["basedir", "C", None, "which directory to stop the node in"],
21 class RestartOptions(BasedirMixin, usage.Options):
23 ["basedir", "C", None, "which directory to restart the node in"],
26 ["force", "f", "if the node is not already running, start it "
27 "instead of complaining that you should have used 'start' instead "
29 ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
30 ["syslog", None, "tell the node to log to syslog, not a file"],
33 class RunOptions(usage.Options):
35 ["basedir", "C", None, "which directory to run the node in, CWD by default"],
38 def do_start(basedir, opts, out=sys.stdout, err=sys.stderr):
39 print >>out, "STARTING", basedir
40 if not os.path.isdir(basedir):
41 print >>err, "%s does not look like a directory at all" % basedir
43 for fn in os.listdir(basedir):
44 if fn.endswith(".tac"):
48 print >>err, "%s does not look like a node directory (no .tac file)" % basedir
52 elif "introducer" in tac:
53 nodetype = "introducer"
55 nodetype = "unknown (%s)" % tac
57 cmd = find_exe.find_exe('twistd')
59 # If 'twistd' wasn't on $PATH, maybe we're running from source and
60 # Twisted was built as one of our dependencies. If so, we're at
61 # BASEDIR/src/allmydata/scripts/startstop_node.py, and it's at
62 # BASEDIR/support/$BINDIR/twistd
64 TAHOEDIR = up(up(up(up(os.path.abspath(__file__)))))
65 if sys.platform == "win32":
69 bindir = os.path.join(TAHOEDIR, "support", bin_dir)
71 maybe = os.path.join(bindir, "twistd")
72 if os.path.exists(maybe):
74 oldpath = os.environ.get("PATH", "").split(os.pathsep)
75 os.environ["PATH"] = os.pathsep.join(oldpath + [bindir])
76 # sys.path and $PYTHONPATH are taken care of by the extra code in
80 if os.path.exists(maybe):
81 cmd = [sys.executable, maybe]
82 oldpath = os.environ.get("PATH", "").split(os.pathsep)
83 os.environ["PATH"] = os.pathsep.join(oldpath + [bindir])
84 # sys.path and $PYTHONPATH are taken care of by the extra code in
88 print "Can't find twistd (it comes with Twisted). Aborting."
91 cmd.extend(["-y", tac])
93 cmd.append("--syslog")
94 elif nodetype in ("client", "introducer"):
95 fileutil.make_dirs(os.path.join(basedir, "logs"))
96 cmd.extend(["--logfile", os.path.join("logs", "twistd.log")])
98 cmd.extend(["--profile=profiling_results.prof", "--savestats",])
102 rc = os.system(' '.join(cmd))
106 print >>out, "%s node probably started" % nodetype
109 print >>err, "%s node probably not started" % nodetype
112 def do_stop(basedir, out=sys.stdout, err=sys.stderr):
113 print >>out, "STOPPING", basedir
114 pidfile = os.path.join(basedir, "twistd.pid")
115 if not os.path.exists(pidfile):
116 print >>err, "%s does not look like a running node directory (no twistd.pid)" % basedir
118 pid = open(pidfile, "r").read()
121 # kill it hard (SIGKILL), delete the twistd.pid file, then wait for the
122 # process itself to go away. If it hasn't gone away after 20 seconds, warn
123 # the user but keep waiting until they give up.
125 os.kill(pid, signal.SIGKILL)
126 except OSError, oserr:
129 # the process didn't exist, so wipe the pid file
136 except EnvironmentError:
143 # poll once per second until we see the process is no longer running
147 print >>out, "process %d is dead" % pid
152 print >>err, ("It looks like pid %d is still running "
153 "after %d seconds" % (pid,
154 (time.time() - start)))
155 print >>err, "I will keep watching it until you interrupt me."
159 print >>err, "pid %d still running after %d seconds" % \
160 (pid, (time.time() - start))
165 def start(config, stdout, stderr):
167 for basedir in config['basedirs']:
168 rc = do_start(basedir, config, stdout, stderr) or rc
171 def stop(config, stdout, stderr):
173 for basedir in config['basedirs']:
174 rc = do_stop(basedir, stdout, stderr) or rc
177 def restart(config, stdout, stderr):
179 for basedir in config['basedirs']:
180 rc = do_stop(basedir, stdout, stderr) or rc
181 if rc == 2 and config['force']:
182 print >>stderr, "ignoring couldn't-stop"
185 print >>stderr, "not restarting"
187 for basedir in config['basedirs']:
188 rc = do_start(basedir, config, stdout, stderr) or rc
191 def run(config, stdout, stderr):
192 from twisted.internet import reactor
193 from twisted.python import log, logfile
194 from allmydata import client
196 basedir = config['basedir']
202 # set up twisted logging. this will become part of the node rsn.
203 logdir = os.path.join(basedir, 'logs')
204 if not os.path.exists(logdir):
206 lf = logfile.LogFile('tahoesvc.log', logdir)
209 # run the node itself
210 c = client.Client(basedir)
211 reactor.callLater(0, c.startService) # after reactor startup
218 ["start", None, StartOptions, "Start a node (of any type)."],
219 ["stop", None, StopOptions, "Stop a node."],
220 ["restart", None, RestartOptions, "Restart a node."],
221 ["run", None, RunOptions, "Run a node synchronously."],