2019-03-24 13:09:10 +00:00
|
|
|
from __future__ import print_function
|
|
|
|
|
2017-04-12 20:52:09 +00:00
|
|
|
import os
|
|
|
|
import io
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
import subprocess
|
|
|
|
from os.path import join, exists
|
|
|
|
|
|
|
|
from allmydata.scripts.common import BasedirOptions
|
|
|
|
from allmydata.scripts.default_nodedir import _default_nodedir
|
|
|
|
from allmydata.util.encodingutil import quote_local_unicode_path
|
|
|
|
|
|
|
|
from .tahoe_daemonize import MyTwistdConfig, identify_node_type
|
|
|
|
|
|
|
|
|
|
|
|
class StartOptions(BasedirOptions):
|
|
|
|
subcommand_name = "start"
|
|
|
|
optParameters = [
|
|
|
|
("basedir", "C", None,
|
|
|
|
"Specify which Tahoe base directory should be used."
|
|
|
|
" This has the same effect as the global --node-directory option."
|
|
|
|
" [default: %s]" % quote_local_unicode_path(_default_nodedir)),
|
|
|
|
]
|
|
|
|
|
|
|
|
def parseArgs(self, basedir=None, *twistd_args):
|
|
|
|
# This can't handle e.g. 'tahoe start --nodaemon', since '--nodaemon'
|
|
|
|
# looks like an option to the tahoe subcommand, not to twistd. So you
|
|
|
|
# can either use 'tahoe start' or 'tahoe start NODEDIR
|
|
|
|
# --TWISTD-OPTIONS'. Note that 'tahoe --node-directory=NODEDIR start
|
|
|
|
# --TWISTD-OPTIONS' also isn't allowed, unfortunately.
|
|
|
|
|
|
|
|
BasedirOptions.parseArgs(self, basedir)
|
|
|
|
self.twistd_args = twistd_args
|
|
|
|
|
|
|
|
def getSynopsis(self):
|
|
|
|
return ("Usage: %s [global-options] %s [options]"
|
|
|
|
" [NODEDIR [twistd-options]]"
|
|
|
|
% (self.command_name, self.subcommand_name))
|
|
|
|
|
|
|
|
def getUsage(self, width=None):
|
|
|
|
t = BasedirOptions.getUsage(self, width) + "\n"
|
|
|
|
twistd_options = str(MyTwistdConfig()).partition("\n")[2].partition("\n\n")[0]
|
|
|
|
t += twistd_options.replace("Options:", "twistd-options:", 1)
|
|
|
|
t += """
|
|
|
|
|
|
|
|
Note that if any twistd-options are used, NODEDIR must be specified explicitly
|
|
|
|
(not by default or using -C/--basedir or -d/--node-directory), and followed by
|
|
|
|
the twistd-options.
|
|
|
|
"""
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
|
|
def start(config):
|
|
|
|
"""
|
|
|
|
Start a tahoe node (daemonize it and confirm startup)
|
|
|
|
|
|
|
|
We run 'tahoe daemonize' with all the options given to 'tahoe
|
|
|
|
start' and then watch the log files for the correct text to appear
|
|
|
|
(e.g. "introducer started"). If that doesn't happen within a few
|
|
|
|
seconds, an error is printed along with all collected logs.
|
|
|
|
"""
|
|
|
|
|
|
|
|
out = config.stdout
|
|
|
|
err = config.stderr
|
|
|
|
basedir = config['basedir']
|
|
|
|
quoted_basedir = quote_local_unicode_path(basedir)
|
2019-03-24 13:09:10 +00:00
|
|
|
print("STARTING", quoted_basedir, file=out)
|
2017-04-12 20:52:09 +00:00
|
|
|
if not os.path.isdir(basedir):
|
2019-03-24 13:09:10 +00:00
|
|
|
print("%s does not look like a directory at all" % quoted_basedir, file=err)
|
2017-04-12 20:52:09 +00:00
|
|
|
return 1
|
|
|
|
nodetype = identify_node_type(basedir)
|
|
|
|
if not nodetype:
|
2019-03-24 13:09:10 +00:00
|
|
|
print("%s is not a recognizable node directory" % quoted_basedir, file=err)
|
2017-04-12 20:52:09 +00:00
|
|
|
return 1
|
|
|
|
|
|
|
|
# "tahoe start" attempts to monitor the logs for successful
|
|
|
|
# startup -- but we can't always do that.
|
|
|
|
|
|
|
|
can_monitor_logs = False
|
|
|
|
if (nodetype in (u"client", u"introducer")
|
|
|
|
and "--nodaemon" not in config.twistd_args
|
|
|
|
and "--syslog" not in config.twistd_args
|
|
|
|
and "--logfile" not in config.twistd_args):
|
|
|
|
can_monitor_logs = True
|
|
|
|
|
|
|
|
if "--help" in config.twistd_args:
|
|
|
|
return 0
|
|
|
|
|
|
|
|
if not can_monitor_logs:
|
2019-03-24 13:09:10 +00:00
|
|
|
print("Custom logging options; can't monitor logs for proper startup messages", file=out)
|
2017-04-12 20:52:09 +00:00
|
|
|
return 1
|
|
|
|
|
|
|
|
# before we spawn tahoe, we check if "the log file" exists or not,
|
|
|
|
# and if so remember how big it is -- essentially, we're doing
|
|
|
|
# "tail -f" to see what "this" incarnation of "tahoe daemonize"
|
|
|
|
# spews forth.
|
|
|
|
starting_offset = 0
|
|
|
|
log_fname = join(basedir, 'logs', 'twistd.log')
|
|
|
|
if exists(log_fname):
|
|
|
|
with open(log_fname, 'r') as f:
|
|
|
|
f.seek(0, 2)
|
|
|
|
starting_offset = f.tell()
|
|
|
|
|
|
|
|
# spawn tahoe. Note that since this daemonizes, it should return
|
|
|
|
# "pretty fast" and with a zero return-code, or else something
|
|
|
|
# Very Bad has happened.
|
|
|
|
try:
|
2018-03-29 18:11:15 +00:00
|
|
|
args = [sys.executable] if not getattr(sys, 'frozen', False) else []
|
2017-04-12 20:52:09 +00:00
|
|
|
for i, arg in enumerate(sys.argv):
|
|
|
|
if arg in ['start', 'restart']:
|
|
|
|
args.append('daemonize')
|
|
|
|
else:
|
|
|
|
args.append(arg)
|
|
|
|
subprocess.check_call(args)
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
return e.returncode
|
|
|
|
|
|
|
|
# now, we have to determine if tahoe has actually started up
|
|
|
|
# successfully or not. so, we start sucking up log files and
|
|
|
|
# looking for "the magic string", which depends on the node type.
|
|
|
|
|
|
|
|
magic_string = u'{} running'.format(nodetype)
|
|
|
|
with io.open(log_fname, 'r') as f:
|
|
|
|
f.seek(starting_offset)
|
|
|
|
|
|
|
|
collected = u''
|
2017-09-20 06:55:19 +00:00
|
|
|
overall_start = time.time()
|
|
|
|
while time.time() - overall_start < 60:
|
|
|
|
this_start = time.time()
|
|
|
|
while time.time() - this_start < 5:
|
|
|
|
collected += f.read()
|
|
|
|
if magic_string in collected:
|
|
|
|
if not config.parent['quiet']:
|
2019-03-24 13:09:10 +00:00
|
|
|
print("Node has started successfully", file=out)
|
2017-09-20 06:55:19 +00:00
|
|
|
return 0
|
|
|
|
if 'Traceback ' in collected:
|
2019-03-24 13:09:10 +00:00
|
|
|
print("Error starting node; see '{}' for more:\n\n{}".format(
|
2017-09-20 06:55:19 +00:00
|
|
|
log_fname,
|
|
|
|
collected,
|
2019-03-24 13:09:10 +00:00
|
|
|
), file=err)
|
2017-09-20 06:55:19 +00:00
|
|
|
return 1
|
|
|
|
time.sleep(0.1)
|
2019-03-24 13:09:10 +00:00
|
|
|
print("Still waiting up to {}s for node startup".format(
|
2017-09-20 06:55:19 +00:00
|
|
|
60 - int(time.time() - overall_start)
|
2019-03-24 13:09:10 +00:00
|
|
|
), file=out)
|
2017-04-12 20:52:09 +00:00
|
|
|
|
2019-03-24 13:09:10 +00:00
|
|
|
print("Something has gone wrong starting the node.", file=out)
|
|
|
|
print("Logs are available in '{}'".format(log_fname), file=out)
|
|
|
|
print("Collected for this run:", file=out)
|
|
|
|
print(collected, file=out)
|
2017-04-12 20:52:09 +00:00
|
|
|
return 1
|