2019-03-24 13:14:00 +00:00
|
|
|
from __future__ import print_function
|
2021-02-15 19:38:11 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import unicode_literals
|
2006-12-05 19:25:23 +00:00
|
|
|
|
2021-02-15 19:38:11 +00:00
|
|
|
from future.utils import PY2
|
|
|
|
if PY2:
|
|
|
|
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
|
2021-05-12 15:51:47 +00:00
|
|
|
|
2012-06-18 17:43:49 +00:00
|
|
|
import os, sys
|
2016-10-19 05:17:48 +00:00
|
|
|
from six.moves import StringIO
|
2020-08-21 20:27:21 +00:00
|
|
|
import six
|
2008-11-12 22:25:03 +00:00
|
|
|
|
2020-11-29 20:48:26 +00:00
|
|
|
try:
|
|
|
|
from allmydata.scripts.types_ import SubCommands
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
|
2006-12-05 19:25:23 +00:00
|
|
|
from twisted.python import usage
|
2016-09-09 21:25:10 +00:00
|
|
|
from twisted.internet import defer, task, threads
|
2006-12-05 19:25:23 +00:00
|
|
|
|
2012-06-18 17:43:49 +00:00
|
|
|
from allmydata.scripts.common import get_default_nodedir
|
2017-04-12 20:52:09 +00:00
|
|
|
from allmydata.scripts import debug, create_node, cli, \
|
2020-12-08 23:22:01 +00:00
|
|
|
admin, tahoe_run, tahoe_invite
|
2021-03-26 15:24:39 +00:00
|
|
|
from allmydata.util.encodingutil import quote_local_unicode_path, argv_to_unicode
|
2019-02-25 18:12:03 +00:00
|
|
|
from allmydata.util.eliotutil import (
|
|
|
|
opt_eliot_destination,
|
2019-02-25 18:34:02 +00:00
|
|
|
opt_help_eliot_destinations,
|
2019-03-04 14:44:00 +00:00
|
|
|
eliot_logging_service,
|
2019-02-25 18:12:03 +00:00
|
|
|
)
|
2006-12-05 19:25:23 +00:00
|
|
|
|
2020-11-23 20:10:18 +00:00
|
|
|
from .. import (
|
|
|
|
__full_version__,
|
|
|
|
)
|
|
|
|
|
2012-06-18 17:43:49 +00:00
|
|
|
_default_nodedir = get_default_nodedir()
|
|
|
|
|
|
|
|
NODEDIR_HELP = ("Specify which Tahoe node directory should be used. The "
|
|
|
|
"directory should either contain a full Tahoe node, or a "
|
|
|
|
"file named node.url that points to some other Tahoe node. "
|
|
|
|
"It should also contain a file named '"
|
|
|
|
+ os.path.join('private', 'aliases') +
|
|
|
|
"' which contains the mapping from alias name to root "
|
|
|
|
"dirnode URI.")
|
|
|
|
if _default_nodedir:
|
2015-02-04 00:10:21 +00:00
|
|
|
NODEDIR_HELP += " [default for most commands: " + quote_local_unicode_path(_default_nodedir) + "]"
|
2012-06-18 17:43:49 +00:00
|
|
|
|
2017-04-12 20:52:09 +00:00
|
|
|
|
|
|
|
# XXX all this 'dispatch' stuff needs to be unified + fixed up
|
|
|
|
_control_node_dispatch = {
|
|
|
|
"run": tahoe_run.run,
|
|
|
|
}
|
|
|
|
|
2019-02-25 18:59:35 +00:00
|
|
|
process_control_commands = [
|
2020-11-29 20:48:26 +00:00
|
|
|
("run", None, tahoe_run.RunOptions, "run a node without daemonizing"),
|
|
|
|
] # type: SubCommands
|
2019-02-25 18:59:35 +00:00
|
|
|
|
2017-04-12 20:52:09 +00:00
|
|
|
|
2012-06-18 17:43:49 +00:00
|
|
|
class Options(usage.Options):
|
|
|
|
# unit tests can override these to point at StringIO instances
|
|
|
|
stdin = sys.stdin
|
|
|
|
stdout = sys.stdout
|
|
|
|
stderr = sys.stderr
|
|
|
|
|
2019-02-25 18:59:35 +00:00
|
|
|
subCommands = ( create_node.subCommands
|
2012-03-12 22:02:58 +00:00
|
|
|
+ admin.subCommands
|
2019-02-25 18:59:35 +00:00
|
|
|
+ process_control_commands
|
2010-01-27 22:37:58 +00:00
|
|
|
+ debug.subCommands
|
|
|
|
+ cli.subCommands
|
2016-10-07 18:15:05 +00:00
|
|
|
+ tahoe_invite.subCommands
|
2010-01-27 22:37:58 +00:00
|
|
|
)
|
2006-12-05 19:25:23 +00:00
|
|
|
|
2012-06-18 17:43:49 +00:00
|
|
|
optFlags = [
|
|
|
|
["quiet", "q", "Operate silently."],
|
|
|
|
["version", "V", "Display version numbers."],
|
|
|
|
["version-and-path", None, "Display version numbers and paths to their locations."],
|
|
|
|
]
|
|
|
|
optParameters = [
|
|
|
|
["node-directory", "d", None, NODEDIR_HELP],
|
2020-08-21 20:27:21 +00:00
|
|
|
["wormhole-server", None, u"ws://wormhole.tahoe-lafs.org:4000/v1", "The magic wormhole server to use.", six.text_type],
|
|
|
|
["wormhole-invite-appid", None, u"tahoe-lafs.org/invite", "The appid to use on the wormhole server.", six.text_type],
|
2012-06-18 17:43:49 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
def opt_version(self):
|
2020-11-23 20:10:18 +00:00
|
|
|
print(__full_version__, file=self.stdout)
|
2012-06-18 17:43:49 +00:00
|
|
|
self.no_command_needed = True
|
|
|
|
|
2020-11-23 20:10:18 +00:00
|
|
|
opt_version_and_path = opt_version
|
2012-06-18 17:43:49 +00:00
|
|
|
|
2019-02-25 18:12:03 +00:00
|
|
|
opt_eliot_destination = opt_eliot_destination
|
2019-02-25 18:34:02 +00:00
|
|
|
opt_help_eliot_destinations = opt_help_eliot_destinations
|
2019-02-25 18:12:03 +00:00
|
|
|
|
2015-05-26 18:29:49 +00:00
|
|
|
def __str__(self):
|
|
|
|
return ("\nUsage: tahoe [global-options] <command> [command-options]\n"
|
|
|
|
+ self.getUsage())
|
|
|
|
|
2015-05-26 18:31:06 +00:00
|
|
|
synopsis = "\nUsage: tahoe [global-options]" # used only for subcommands
|
2012-06-18 17:43:49 +00:00
|
|
|
|
2010-01-26 04:45:59 +00:00
|
|
|
def getUsage(self, **kwargs):
|
2010-01-27 22:37:58 +00:00
|
|
|
t = usage.Options.getUsage(self, **kwargs)
|
2015-05-26 18:29:49 +00:00
|
|
|
t = t.replace("Options:", "\nGlobal options:", 1)
|
2010-01-26 04:45:59 +00:00
|
|
|
return t + "\nPlease run 'tahoe <command> --help' for more details on each command.\n"
|
2006-12-05 19:25:23 +00:00
|
|
|
|
|
|
|
def postOptions(self):
|
|
|
|
if not hasattr(self, 'subOptions'):
|
2011-01-21 07:59:13 +00:00
|
|
|
if not hasattr(self, 'no_command_needed'):
|
|
|
|
raise usage.UsageError("must specify a command")
|
|
|
|
sys.exit(0)
|
2006-12-05 19:25:23 +00:00
|
|
|
|
2010-08-03 08:54:16 +00:00
|
|
|
|
|
|
|
create_dispatch = {}
|
2020-12-09 15:34:16 +00:00
|
|
|
for module in (create_node,):
|
2020-11-29 20:52:04 +00:00
|
|
|
create_dispatch.update(module.dispatch) # type: ignore
|
2010-08-03 08:54:16 +00:00
|
|
|
|
2016-09-09 21:25:10 +00:00
|
|
|
def parse_options(argv, config=None):
|
|
|
|
if not config:
|
|
|
|
config = Options()
|
|
|
|
config.parseOptions(argv) # may raise usage.error
|
|
|
|
return config
|
2010-07-25 08:32:16 +00:00
|
|
|
|
2021-03-26 15:24:39 +00:00
|
|
|
|
2016-09-09 21:25:10 +00:00
|
|
|
def parse_or_exit_with_explanation(argv, stdout=sys.stdout):
|
2008-02-20 00:05:14 +00:00
|
|
|
config = Options()
|
2006-12-05 19:25:23 +00:00
|
|
|
try:
|
2016-09-09 21:25:10 +00:00
|
|
|
parse_options(argv, config=config)
|
2017-04-12 20:52:09 +00:00
|
|
|
except usage.error as e:
|
2008-08-12 20:37:32 +00:00
|
|
|
c = config
|
|
|
|
while hasattr(c, 'subOptions'):
|
|
|
|
c = c.subOptions
|
2019-03-24 13:14:00 +00:00
|
|
|
print(str(c), file=stdout)
|
2021-03-30 13:46:56 +00:00
|
|
|
# On Python 2 the string may turn into a unicode string, e.g. the error
|
|
|
|
# may be unicode, in which case it will print funny. Once we're on
|
|
|
|
# Python 3 we can just drop the ensure_str().
|
|
|
|
print(six.ensure_str("%s: %s\n" % (sys.argv[0], e)), file=stdout)
|
2016-09-09 21:25:10 +00:00
|
|
|
sys.exit(1)
|
|
|
|
return config
|
2006-12-05 19:25:23 +00:00
|
|
|
|
2016-09-09 21:25:10 +00:00
|
|
|
def dispatch(config,
|
|
|
|
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
|
2006-12-05 19:25:23 +00:00
|
|
|
command = config.subCommand
|
|
|
|
so = config.subOptions
|
2007-06-26 23:19:18 +00:00
|
|
|
if config['quiet']:
|
|
|
|
stdout = StringIO()
|
2008-08-01 18:46:24 +00:00
|
|
|
so.stdout = stdout
|
|
|
|
so.stderr = stderr
|
|
|
|
so.stdin = stdin
|
|
|
|
|
2010-08-03 08:54:16 +00:00
|
|
|
if command in create_dispatch:
|
2016-09-08 09:21:04 +00:00
|
|
|
f = create_dispatch[command]
|
2017-04-12 20:52:09 +00:00
|
|
|
elif command in _control_node_dispatch:
|
|
|
|
f = _control_node_dispatch[command]
|
2007-07-11 01:41:52 +00:00
|
|
|
elif command in debug.dispatch:
|
2016-09-08 09:21:04 +00:00
|
|
|
f = debug.dispatch[command]
|
2012-03-12 22:02:58 +00:00
|
|
|
elif command in admin.dispatch:
|
2016-09-08 09:21:04 +00:00
|
|
|
f = admin.dispatch[command]
|
2007-07-11 02:37:37 +00:00
|
|
|
elif command in cli.dispatch:
|
2016-09-09 21:25:10 +00:00
|
|
|
# these are blocking, and must be run in a thread
|
|
|
|
f0 = cli.dispatch[command]
|
|
|
|
f = lambda so: threads.deferToThread(f0, so)
|
2016-10-07 18:15:05 +00:00
|
|
|
elif command in tahoe_invite.dispatch:
|
|
|
|
f = tahoe_invite.dispatch[command]
|
2007-08-17 20:23:16 +00:00
|
|
|
else:
|
|
|
|
raise usage.UsageError()
|
2007-07-11 01:41:52 +00:00
|
|
|
|
2016-09-09 21:25:10 +00:00
|
|
|
d = defer.maybeDeferred(f, so)
|
|
|
|
# the calling convention for CLI dispatch functions is that they either:
|
|
|
|
# 1: succeed and return rc=0
|
|
|
|
# 2: print explanation to stderr and return rc!=0
|
|
|
|
# 3: raise an exception that should just be printed normally
|
|
|
|
# 4: return a Deferred that does 1 or 2 or 3
|
|
|
|
def _raise_sys_exit(rc):
|
|
|
|
sys.exit(rc)
|
|
|
|
d.addCallback(_raise_sys_exit)
|
|
|
|
return d
|
2010-07-25 08:32:16 +00:00
|
|
|
|
2019-03-04 14:44:00 +00:00
|
|
|
def _maybe_enable_eliot_logging(options, reactor):
|
2019-03-04 15:08:46 +00:00
|
|
|
if options.get("destinations"):
|
2019-03-04 14:44:00 +00:00
|
|
|
service = eliot_logging_service(reactor, options["destinations"])
|
|
|
|
# There is no Twisted "Application" around to hang this on so start
|
|
|
|
# and stop it ourselves.
|
|
|
|
service.startService()
|
|
|
|
reactor.addSystemEventTrigger("after", "shutdown", service.stopService)
|
|
|
|
# Pass on the options so we can dispatch the subcommand.
|
|
|
|
return options
|
|
|
|
|
2021-05-12 16:02:22 +00:00
|
|
|
PYTHON_3_WARNING = ("Support for Python 3 is an incomplete work-in-progress."
|
|
|
|
" Use at your own risk.")
|
|
|
|
|
2016-09-08 07:02:15 +00:00
|
|
|
def run():
|
2021-03-17 19:27:45 +00:00
|
|
|
if six.PY3:
|
2021-05-12 16:02:22 +00:00
|
|
|
print(PYTHON_3_WARNING, file=sys.stderr)
|
2010-07-25 08:32:16 +00:00
|
|
|
|
2016-09-09 21:25:10 +00:00
|
|
|
if sys.platform == "win32":
|
|
|
|
from allmydata.windows.fixups import initialize
|
|
|
|
initialize()
|
2019-03-04 14:44:00 +00:00
|
|
|
# doesn't return: calls sys.exit(rc)
|
|
|
|
task.react(_run_with_reactor)
|
|
|
|
|
2019-07-23 16:39:45 +00:00
|
|
|
|
|
|
|
def _setup_coverage(reactor):
|
|
|
|
"""
|
|
|
|
Arrange for coverage to be collected if the 'coverage' package is
|
|
|
|
installed
|
|
|
|
"""
|
|
|
|
# can we put this _setup_coverage call after we hit
|
|
|
|
# argument-parsing?
|
2019-08-07 19:59:40 +00:00
|
|
|
if '--coverage' not in sys.argv:
|
2019-07-23 16:39:45 +00:00
|
|
|
return
|
|
|
|
sys.argv.remove('--coverage')
|
|
|
|
|
|
|
|
try:
|
|
|
|
import coverage
|
|
|
|
except ImportError:
|
2019-08-07 20:08:23 +00:00
|
|
|
raise RuntimeError(
|
2019-08-07 19:59:40 +00:00
|
|
|
"The 'coveage' package must be installed to use --coverage"
|
|
|
|
)
|
2019-07-23 16:39:45 +00:00
|
|
|
|
2019-08-07 18:39:29 +00:00
|
|
|
# this doesn't change the shell's notion of the environment, but
|
|
|
|
# it makes the test in process_startup() succeed, which is the
|
|
|
|
# goal here.
|
2019-07-23 16:39:45 +00:00
|
|
|
os.environ["COVERAGE_PROCESS_START"] = '.coveragerc'
|
2019-08-07 18:39:29 +00:00
|
|
|
|
2019-07-23 16:39:45 +00:00
|
|
|
# maybe-start the global coverage, unless it already got started
|
|
|
|
cov = coverage.process_startup()
|
|
|
|
if cov is None:
|
|
|
|
cov = coverage.process_startup.coverage
|
|
|
|
|
2019-08-07 18:39:29 +00:00
|
|
|
def write_coverage_data():
|
2019-07-23 16:39:45 +00:00
|
|
|
"""
|
|
|
|
Make sure that coverage has stopped; internally, it depends on
|
|
|
|
ataxit handlers running which doesn't always happen (Twisted's
|
|
|
|
shutdown hook also won't run if os._exit() is called, but it
|
|
|
|
runs more-often than atexit handlers).
|
|
|
|
"""
|
|
|
|
cov.stop()
|
|
|
|
cov.save()
|
|
|
|
reactor.addSystemEventTrigger('after', 'shutdown', write_coverage_data)
|
|
|
|
|
|
|
|
|
2019-03-04 14:44:00 +00:00
|
|
|
def _run_with_reactor(reactor):
|
2019-07-23 16:39:45 +00:00
|
|
|
|
|
|
|
_setup_coverage(reactor)
|
|
|
|
|
2021-03-26 15:24:39 +00:00
|
|
|
argv = list(map(argv_to_unicode, sys.argv[1:]))
|
|
|
|
d = defer.maybeDeferred(parse_or_exit_with_explanation, argv)
|
2019-03-04 14:44:00 +00:00
|
|
|
d.addCallback(_maybe_enable_eliot_logging, reactor)
|
2016-09-09 21:25:10 +00:00
|
|
|
d.addCallback(dispatch)
|
|
|
|
def _show_exception(f):
|
|
|
|
# when task.react() notices a non-SystemExit exception, it does
|
|
|
|
# log.err() with the failure and then exits with rc=1. We want this
|
|
|
|
# to actually print the exception to stderr, like it would do if we
|
|
|
|
# weren't using react().
|
|
|
|
if f.check(SystemExit):
|
|
|
|
return f # dispatch function handled it
|
|
|
|
f.printTraceback(file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
d.addErrback(_show_exception)
|
2019-03-04 14:44:00 +00:00
|
|
|
return d
|
2016-04-19 16:22:34 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
run()
|