]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/startstop_node.py
36138e00995949cb16e091c3f6de9aed6eac577f
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / startstop_node.py
1
2 import os, sys, signal, time
3 from allmydata.scripts.common import BasedirOptions
4 from allmydata.util import fileutil
5 from allmydata.util.assertutil import precondition
6 from allmydata.util.encodingutil import listdir_unicode, quote_output
7
8
9 class StartOptions(BasedirOptions):
10     optFlags = [
11         ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."],
12         ["syslog", None, "Tell the node to log to syslog, not a file."],
13         ]
14
15     def getSynopsis(self):
16         return "Usage:  %s start [options] [NODEDIR]" % (self.command_name,)
17
18
19 class StopOptions(BasedirOptions):
20     def getSynopsis(self):
21         return "Usage:  %s stop [options] [NODEDIR]" % (self.command_name,)
22
23
24 class RestartOptions(BasedirOptions):
25     optFlags = [
26         ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."],
27         ["syslog", None, "Tell the node to log to syslog, not a file."],
28         ]
29
30     def getSynopsis(self):
31         return "Usage:  %s restart [options] [NODEDIR]" % (self.command_name,)
32
33
34 class RunOptions(BasedirOptions):
35     default_nodedir = u"."
36
37     def getSynopsis(self):
38         return "Usage:  %s run [options] [NODEDIR]" % (self.command_name,)
39
40
41 def start(opts, out=sys.stdout, err=sys.stderr):
42     basedir = opts['basedir']
43     print >>out, "STARTING", quote_output(basedir)
44     if not os.path.isdir(basedir):
45         print >>err, "%s does not look like a directory at all" % quote_output(basedir)
46         return 1
47     for fn in listdir_unicode(basedir):
48         if fn.endswith(u".tac"):
49             tac = str(fn)
50             break
51     else:
52         print >>err, "%s does not look like a node directory (no .tac file)" % quote_output(basedir)
53         return 1
54     if "client" in tac:
55         nodetype = "client"
56     elif "introducer" in tac:
57         nodetype = "introducer"
58     else:
59         nodetype = "unknown (%s)" % tac
60
61     args = ["twistd", "-y", tac]
62     if opts["syslog"]:
63         args.append("--syslog")
64     elif nodetype in ("client", "introducer"):
65         fileutil.make_dirs(os.path.join(basedir, "logs"))
66         args.extend(["--logfile", os.path.join("logs", "twistd.log")])
67     if opts["profile"]:
68         args.extend(["--profile=profiling_results.prof", "--savestats",])
69     # now we're committed
70     os.chdir(basedir)
71     from twisted.scripts import twistd
72     sys.argv = args
73     twistd.run()
74     # run() doesn't return: the parent does os._exit(0) in daemonize(), so
75     # we'll never get here. If application setup fails (e.g. ImportError),
76     # run() will raise an exception.
77
78 def stop(config, out=sys.stdout, err=sys.stderr):
79     basedir = config['basedir']
80     print >>out, "STOPPING", quote_output(basedir)
81     pidfile = os.path.join(basedir, "twistd.pid")
82     if not os.path.exists(pidfile):
83         print >>err, "%s does not look like a running node directory (no twistd.pid)" % quote_output(basedir)
84         # we define rc=2 to mean "nothing is running, but it wasn't me who
85         # stopped it"
86         return 2
87     pid = open(pidfile, "r").read()
88     pid = int(pid)
89
90     # kill it hard (SIGKILL), delete the twistd.pid file, then wait for the
91     # process itself to go away. If it hasn't gone away after 20 seconds, warn
92     # the user but keep waiting until they give up.
93     try:
94         os.kill(pid, signal.SIGKILL)
95     except OSError, oserr:
96         if oserr.errno == 3:
97             print oserr.strerror
98             # the process didn't exist, so wipe the pid file
99             os.remove(pidfile)
100             return 2
101         else:
102             raise
103     try:
104         os.remove(pidfile)
105     except EnvironmentError:
106         pass
107     start = time.time()
108     time.sleep(0.1)
109     wait = 40
110     first_time = True
111     while True:
112         # poll once per second until we see the process is no longer running
113         try:
114             os.kill(pid, 0)
115         except OSError:
116             print >>out, "process %d is dead" % pid
117             return
118         wait -= 1
119         if wait < 0:
120             if first_time:
121                 print >>err, ("It looks like pid %d is still running "
122                               "after %d seconds" % (pid,
123                                                     (time.time() - start)))
124                 print >>err, "I will keep watching it until you interrupt me."
125                 wait = 10
126                 first_time = False
127             else:
128                 print >>err, "pid %d still running after %d seconds" % \
129                       (pid, (time.time() - start))
130                 wait = 10
131         time.sleep(1)
132     # we define rc=1 to mean "I think something is still running, sorry"
133     return 1
134
135 def restart(config, stdout, stderr):
136     rc = stop(config, stdout, stderr)
137     if rc == 2:
138         print >>stderr, "ignoring couldn't-stop"
139         rc = 0
140     if rc:
141         print >>stderr, "not restarting"
142         return rc
143     return start(config, stdout, stderr)
144
145 def run(config, stdout, stderr):
146     from twisted.internet import reactor
147     from twisted.python import log, logfile
148     from allmydata import client
149
150     basedir = config['basedir']
151     precondition(isinstance(basedir, unicode), basedir)
152
153     if not os.path.isdir(basedir):
154         print >>stderr, "%s does not look like a directory at all" % quote_output(basedir)
155         return 1
156     for fn in listdir_unicode(basedir):
157         if fn.endswith(u".tac"):
158             tac = str(fn)
159             break
160     else:
161         print >>stderr, "%s does not look like a node directory (no .tac file)" % quote_output(basedir)
162         return 1
163     if "client" not in tac:
164         print >>stderr, ("%s looks like it contains a non-client node (%s).\n"
165                          "Use 'tahoe start' instead of 'tahoe run'."
166                          % (quote_output(basedir), tac))
167         return 1
168
169     os.chdir(basedir)
170
171     # set up twisted logging. this will become part of the node rsn.
172     logdir = os.path.join(basedir, 'logs')
173     if not os.path.exists(logdir):
174         os.makedirs(logdir)
175     lf = logfile.LogFile('tahoesvc.log', logdir)
176     log.startLogging(lf)
177
178     # run the node itself
179     c = client.Client(basedir)
180     reactor.callLater(0, c.startService) # after reactor startup
181     reactor.run()
182
183     return 0
184
185
186 subCommands = [
187     ["start", None, StartOptions, "Start a node (of any type)."],
188     ["stop", None, StopOptions, "Stop a node."],
189     ["restart", None, RestartOptions, "Restart a node."],
190     ["run", None, RunOptions, "Run a node synchronously."],
191 ]
192
193 dispatch = {
194     "start": start,
195     "stop": stop,
196     "restart": restart,
197     "run": run,
198     }