mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-07-21 07:28:10 -07:00
Updated rnsh config args to work similarly to other RNS utils
This commit is contained in:
@@ -44,7 +44,8 @@ def setup_argument_parser():
|
|||||||
parser = argparse.ArgumentParser(description="Reticulum Remote Shell Utility", epilog="When specifying a command to execute, separate rnsh\noptions from the command and its arguments with --\n\nFor example:\n rnsh -l -- /bin/bash --login\n rnsh <destination> -- ls -la /tmp", formatter_class=argparse.RawDescriptionHelpFormatter)
|
parser = argparse.ArgumentParser(description="Reticulum Remote Shell Utility", epilog="When specifying a command to execute, separate rnsh\noptions from the command and its arguments with --\n\nFor example:\n rnsh -l -- /bin/bash --login\n rnsh <destination> -- ls -la /tmp", formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
|
||||||
# Common options
|
# Common options
|
||||||
parser.add_argument("--config", "-c", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
parser.add_argument("--config", "-c", action="store", default=None, help="path to config directory", type=str)
|
||||||
|
parser.add_argument("--rnsconfig", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
|
||||||
parser.add_argument("--identity", "-i", action="store", default=None, help="path to identity file to use", type=str)
|
parser.add_argument("--identity", "-i", action="store", default=None, help="path to identity file to use", type=str)
|
||||||
parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
|
parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
|
||||||
parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
|
parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ class RemoteExecutionError(Exception):
|
|||||||
def __init__(self, msg): self.msg = msg
|
def __init__(self, msg): self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
async def _initiate_link(configdir, rnsconfigdir, identitypath=None, verbosity=0, quietness=0, noid=False, destination=None,
|
async def _initiate_link(configdir, rnsconfigdir, identitypath=None, logfile=None, verbosity=0, quietness=0, noid=False, destination=None,
|
||||||
timeout=RNS.Transport.PATH_REQUEST_TIMEOUT):
|
timeout=RNS.Transport.PATH_REQUEST_TIMEOUT):
|
||||||
global _identity, _reticulum, _link, _destination, _remote_exec_grace
|
global _identity, _reticulum, _link, _destination, _remote_exec_grace
|
||||||
|
|
||||||
@@ -160,11 +160,11 @@ async def _initiate_link(configdir, rnsconfigdir, identitypath=None, verbosity=0
|
|||||||
|
|
||||||
if _reticulum is None:
|
if _reticulum is None:
|
||||||
targetloglevel = compute_target_rns_loglevel(verbosity, quietness, RNS.LOG_ERROR)
|
targetloglevel = compute_target_rns_loglevel(verbosity, quietness, RNS.LOG_ERROR)
|
||||||
RNS.logfile = os.path.join(configdir, "logfile")
|
RNS.logfile = logfile
|
||||||
_reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=targetloglevel, logdest=RNS.LOG_FILE)
|
_reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=targetloglevel, logdest=RNS.LOG_FILE)
|
||||||
|
|
||||||
if _identity is None:
|
if _identity is None:
|
||||||
_identity = rnsh.prepare_identity(identitypath)
|
_identity = rnsh.prepare_identity(identity_path=identitypath, service_name=None, configdir=configdir)
|
||||||
|
|
||||||
if not RNS.Transport.has_path(destination_hash):
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
RNS.Transport.request_path(destination_hash)
|
RNS.Transport.request_path(destination_hash)
|
||||||
@@ -211,8 +211,9 @@ async def _handle_error(errmsg: RNS.MessageBase):
|
|||||||
raise RemoteExecutionError(f"Remote error: {errmsg.msg}")
|
raise RemoteExecutionError(f"Remote error: {errmsg.msg}")
|
||||||
|
|
||||||
|
|
||||||
async def initiate(configdir: str, rnsconfigdir:str, identitypath: str, verbosity: int, quietness: int, noid: bool, destination: str,
|
async def initiate(configdir: str, rnsconfigdir:str, identitypath: str, logfile:str, verbosity: int, quietness: int, noid: bool, destination: str,
|
||||||
timeout: float, command: [str] | None = None):
|
timeout: float, command: [str] | None = None):
|
||||||
|
|
||||||
global _finished, _link
|
global _finished, _link
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = RNS.Transport.PATH_REQUEST_TIMEOUT
|
timeout = RNS.Transport.PATH_REQUEST_TIMEOUT
|
||||||
|
|||||||
@@ -118,25 +118,32 @@ def compute_target_rns_loglevel(verbosity: int, quietness: int, base_level: int
|
|||||||
|
|
||||||
except Exception: return base_level
|
except Exception: return base_level
|
||||||
|
|
||||||
async def listen(configdir, rnsconfigdir, command, identitypath=None, service_name=None, verbosity=0, quietness=0, allowed=None,
|
async def listen(configdir, rnsconfigdir, command, identitypath=None, logfile=None, service_name=None, verbosity=0, quietness=0,
|
||||||
allowed_file=None, disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False,
|
allowed=None, allowed_file=None, disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False,
|
||||||
loop: asyncio.AbstractEventLoop = None):
|
loop: asyncio.AbstractEventLoop = None):
|
||||||
global _identity, _allow_all, _allowed_identity_hashes, _allowed_file, _allowed_file_identity_hashes
|
global _identity, _allow_all, _allowed_identity_hashes, _allowed_file, _allowed_file_identity_hashes
|
||||||
global _reticulum, _cmd, _destination, _no_remote_command, _remote_cmd_as_args, _finished
|
global _reticulum, _cmd, _destination, _no_remote_command, _remote_cmd_as_args, _finished
|
||||||
|
|
||||||
if not loop: loop = asyncio.get_running_loop()
|
if not loop: loop = asyncio.get_running_loop()
|
||||||
if service_name is None or len(service_name) == 0:
|
if service_name is None or len(service_name) == 0: service_name = "default"
|
||||||
service_name = "default"
|
|
||||||
|
|
||||||
RNS.log(f"Using service name {service_name}", RNS.LOG_INFO)
|
RNS.log(f"Using service name {service_name}", RNS.LOG_INFO)
|
||||||
|
if logfile:
|
||||||
|
RNS.log(f"Logging to {logfile}", RNS.LOG_NOTICE)
|
||||||
|
logdest = RNS.LOG_FILE
|
||||||
|
RNS.logfile = logfile
|
||||||
|
else:
|
||||||
|
RNS.log(f"Logging to console", RNS.LOG_NOTICE)
|
||||||
|
logdest = RNS.LOG_STDOUT
|
||||||
|
|
||||||
# More -v should increase verbosity (higher RNS.loglevel); -q should decrease it
|
# More -v should increase verbosity (higher RNS.loglevel); -q should decrease it
|
||||||
targetloglevel = compute_target_rns_loglevel(verbosity, quietness, RNS.LOG_INFO)
|
targetloglevel = compute_target_rns_loglevel(verbosity, quietness, RNS.LOG_INFO)
|
||||||
_reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=targetloglevel)
|
_reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=targetloglevel)
|
||||||
_identity = rnsh.prepare_identity(identitypath, service_name)
|
_identity = rnsh.prepare_identity(identity_path=identitypath, service_name=service_name, configdir=configdir)
|
||||||
_destination = RNS.Destination(_identity, RNS.Destination.IN, RNS.Destination.SINGLE, rnsh.APP_NAME)
|
_destination = RNS.Destination(_identity, RNS.Destination.IN, RNS.Destination.SINGLE, rnsh.APP_NAME)
|
||||||
|
|
||||||
RNS.log(f"rnsh listening for commands on {RNS.prettyhexrep(_destination.hash)}", RNS.LOG_NOTICE)
|
RNS.log(f"rnsh listening for commands on {RNS.prettyhexrep(_destination.hash)}", RNS.LOG_NOTICE)
|
||||||
|
RNS.logdest = logdest
|
||||||
|
|
||||||
_cmd = command
|
_cmd = command
|
||||||
if _cmd is None or len(_cmd) == 0:
|
if _cmd is None or len(_cmd) == 0:
|
||||||
|
|||||||
+40
-23
@@ -57,43 +57,54 @@ loop: asyncio.AbstractEventLoop | None = None
|
|||||||
|
|
||||||
def _sanitize_service_name(service_name:str) -> str: return re.sub(r'\W+', '', service_name)
|
def _sanitize_service_name(service_name:str) -> str: return re.sub(r'\W+', '', service_name)
|
||||||
|
|
||||||
def prepare_identity(identity_path, service_name: str = None) -> tuple[RNS.Identity]:
|
def prepare_identity(identity_path=None, service_name=None, configdir=None):
|
||||||
service_name = _sanitize_service_name(service_name or "")
|
service_name = _sanitize_service_name(service_name or "")
|
||||||
if identity_path is None:
|
if not identity_path:
|
||||||
identity_path = RNS.Reticulum.identitypath + "/" + APP_NAME + \
|
identity_path = f"{configdir}/identity" + (f".{service_name}" if service_name and len(service_name) > 0 else "")
|
||||||
(f".{service_name}" if service_name and len(service_name) > 0 else "")
|
|
||||||
|
|
||||||
identity = None
|
identity = None
|
||||||
if os.path.isfile(identity_path):
|
if os.path.isfile(identity_path):
|
||||||
|
RNS.log(f"Loading identity from {identity_path}", RNS.LOG_VERBOSE)
|
||||||
identity = RNS.Identity.from_file(identity_path)
|
identity = RNS.Identity.from_file(identity_path)
|
||||||
|
|
||||||
if identity is None:
|
if identity is None:
|
||||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
RNS.log(f"Creating identity {identity_path}", RNS.LOG_NOTICE)
|
||||||
identity = RNS.Identity()
|
identity = RNS.Identity()
|
||||||
identity.to_file(identity_path)
|
identity.to_file(identity_path)
|
||||||
|
|
||||||
return identity
|
return identity
|
||||||
|
|
||||||
|
|
||||||
def print_identity(configdir, identitypath, service_name, include_destination: bool):
|
def print_identity(configdir, rnsconfigdir, identitypath, service_name, include_destination: bool):
|
||||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=RNS.LOG_INFO)
|
reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=RNS.LOG_INFO)
|
||||||
if service_name and len(service_name) > 0:
|
if service_name and len(service_name) > 0: print(f"Using service name \"{service_name}\"")
|
||||||
print(f"Using service name \"{service_name}\"")
|
identity = prepare_identity(identity_path=identitypath, service_name=service_name, configdir=configdir)
|
||||||
identity = prepare_identity(identitypath, service_name)
|
|
||||||
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME)
|
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME)
|
||||||
print("Identity : " + str(identity))
|
print("Identity : " + str(identity))
|
||||||
if include_destination:
|
if include_destination: print("Listening on : " + RNS.prettyhexrep(destination.hash))
|
||||||
print("Listening on : " + RNS.prettyhexrep(destination.hash))
|
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
verbose_set = False
|
verbose_set = False
|
||||||
|
|
||||||
def ensure_config_directory():
|
def ensure_config_directory(configdir=None):
|
||||||
if os.path.isdir(os.path.expanduser("~/.config/rnsh")): return os.path.expanduser("~/.config/rnsh")
|
if configdir:
|
||||||
elif os.path.isdir(os.path.expanduser("~/.rnsh")): return os.path.expanduser("~/.rnsh")
|
if os.path.isdir(os.path.expanduser(configdir)): return os.path.expanduser(configdir)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
RNS.log(f"Creating configuration directory: {os.path.expanduser(configdir)}")
|
||||||
|
os.makedirs(os.path.expanduser(configdir))
|
||||||
|
return os.path.expanduser(configdir)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log(f"Could not get or create rnsh configuration directory, aborting", RNS.LOG_CRITICAL)
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
elif os.path.isdir(os.path.expanduser("~/.config/rnsh")): return os.path.expanduser("~/.config/rnsh")
|
||||||
|
elif os.path.isdir(os.path.expanduser("~/.rnsh")): return os.path.expanduser("~/.rnsh")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
RNS.log(f"Creating configuration directory: {os.path.expanduser('~/.rnsh')}")
|
||||||
os.makedirs(os.path.expanduser("~/.rnsh"))
|
os.makedirs(os.path.expanduser("~/.rnsh"))
|
||||||
return os.path.expanduser("~/.rnsh")
|
return os.path.expanduser("~/.rnsh")
|
||||||
|
|
||||||
@@ -106,24 +117,29 @@ async def _rnsh_cli_main():
|
|||||||
global verbose_set
|
global verbose_set
|
||||||
args, parser = parse_arguments()
|
args, parser = parse_arguments()
|
||||||
verbose_set = args.verbose > 0
|
verbose_set = args.verbose > 0
|
||||||
configdir = ensure_config_directory()
|
configdir = ensure_config_directory(args.config)
|
||||||
|
|
||||||
|
if not configdir:
|
||||||
|
RNS.log(f"Could not resolve rnsh configuration directory", RNS.LOG_CRITICAL)
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
if args.print_identity:
|
if args.print_identity:
|
||||||
print_identity(args.config, args.identity, args.service, args.listen)
|
print_identity(configdir, args.rnsconfig, args.identity, args.service, args.listen)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
logfile = f"{configdir}/logfile"
|
||||||
|
|
||||||
if args.listen:
|
if args.listen:
|
||||||
allowed_file = None
|
allowed_file = None
|
||||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
if os.path.isfile(os.path.expanduser("~/.config/rnsh/allowed_identities")):
|
if os.path.isfile(os.path.expanduser(f"{configdir}/allowed_identities")):
|
||||||
allowed_file = os.path.expanduser("~/.config/rnsh/allowed_identities")
|
allowed_file = os.path.expanduser(f"{configdir}/allowed_identities")
|
||||||
elif os.path.isfile(os.path.expanduser("~/.rnsh/allowed_identities")):
|
|
||||||
allowed_file = os.path.expanduser("~/.rnsh/allowed_identities")
|
|
||||||
|
|
||||||
await listener.listen(configdir=configdir,
|
await listener.listen(configdir=configdir,
|
||||||
rnsconfigdir=args.config,
|
rnsconfigdir=args.rnsconfig,
|
||||||
command=args.command,
|
command=args.command,
|
||||||
identitypath=args.identity,
|
identitypath=args.identity,
|
||||||
|
logfile=logfile,
|
||||||
service_name=args.service,
|
service_name=args.service,
|
||||||
verbosity=args.verbose,
|
verbosity=args.verbose,
|
||||||
quietness=args.quiet,
|
quietness=args.quiet,
|
||||||
@@ -137,8 +153,9 @@ async def _rnsh_cli_main():
|
|||||||
|
|
||||||
if args.destination is not None:
|
if args.destination is not None:
|
||||||
return_code = await initiator.initiate(configdir=configdir,
|
return_code = await initiator.initiate(configdir=configdir,
|
||||||
rnsconfigdir=args.config,
|
rnsconfigdir=args.rnsconfig,
|
||||||
identitypath=args.identity,
|
identitypath=args.identity,
|
||||||
|
logfile=logfile,
|
||||||
verbosity=args.verbose,
|
verbosity=args.verbose,
|
||||||
quietness=args.quiet,
|
quietness=args.quiet,
|
||||||
noid=args.no_id,
|
noid=args.no_id,
|
||||||
|
|||||||
+28
-32
@@ -1002,39 +1002,35 @@ available. These are only recognised immediately after a newline character:
|
|||||||
destination hexadecimal hash of the destination to connect to
|
destination hexadecimal hash of the destination to connect to
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--config, -c CONFIG path to alternative Reticulum config directory
|
--config, -c PATH path to config directory
|
||||||
--identity, -i IDENTITY
|
--rnsconfig, -c PATH path to alternative Reticulum config directory
|
||||||
path to identity file to use
|
--identity, -i IDENTITY path to identity file to use
|
||||||
-v, --verbose increase verbosity
|
-v, --verbose increase verbosity
|
||||||
-q, --quiet decrease verbosity
|
-q, --quiet decrease verbosity
|
||||||
-p, --print-identity print identity and destination info and exit
|
-p, --print-identity print identity and destination info and exit
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-l, --listen listen (server) mode; any command specified after --
|
-l, --listen listen (server) mode; any command specified after --
|
||||||
will be used as the default command when the initiator
|
will be used as the default command when the initiator
|
||||||
does not provide one or when remote command execution
|
does not provide one or when remote command execution
|
||||||
is disabled; if no command is specified, the default
|
is disabled; if no command is specified, the default
|
||||||
shell of the user running rnsh will be used
|
shell of the user running rnsh will be used
|
||||||
-s, --service SERVICE
|
-s, --service SERVICE service name for identity file if not the default
|
||||||
service name for identity file if not the default
|
-b, --announce PERIOD announce on startup and every PERIOD seconds; specify
|
||||||
-b, --announce PERIOD
|
0 to announce on startup only
|
||||||
announce on startup and every PERIOD seconds; specify
|
-a, --allowed HASH allow this identity to connect (may be specified
|
||||||
0 to announce on startup only
|
multiple times); allowed identities can also be
|
||||||
-a, --allowed HASH allow this identity to connect (may be specified
|
specified in ~/.rnsh/allowed_identities or
|
||||||
multiple times); allowed identities can also be
|
~/.config/rnsh/allowed_identities, one hash per line
|
||||||
specified in ~/.rnsh/allowed_identities or
|
-n, --no-auth disable authentication (allow any identity to connect)
|
||||||
~/.config/rnsh/allowed_identities, one hash per line
|
|
||||||
-n, --no-auth disable authentication (allow any identity to connect)
|
|
||||||
-A, --remote-command-as-args
|
-A, --remote-command-as-args
|
||||||
concatenate remote command to the argument list of the
|
concatenate remote command to the argument list of the
|
||||||
default program or shell
|
default program or shell
|
||||||
-C, --no-remote-command
|
-C, --no-remote-command disable executing command lines received from the
|
||||||
disable executing command lines received from the
|
remote initiator
|
||||||
remote initiator
|
-N, --no-id disable identity announcement on connect
|
||||||
-N, --no-id disable identity announcement on connect
|
-m, --mirror return with the exit code of the remote process
|
||||||
-m, --mirror return with the exit code of the remote process
|
-w, --timeout SECONDS connect and request timeout in seconds
|
||||||
-w, --timeout SECONDS
|
|
||||||
connect and request timeout in seconds
|
|
||||||
|
|
||||||
When specifying a command to execute, separate rnsh options from the command
|
When specifying a command to execute, separate rnsh options from the command
|
||||||
and its arguments with --. For example:
|
and its arguments with --. For example:
|
||||||
|
|||||||
Reference in New Issue
Block a user