mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-07-20 06:58: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)
|
||||
|
||||
# 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("-v", "--verbose", action="count", default=0, help="increase 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
|
||||
|
||||
|
||||
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):
|
||||
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:
|
||||
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)
|
||||
|
||||
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):
|
||||
RNS.Transport.request_path(destination_hash)
|
||||
@@ -211,8 +211,9 @@ async def _handle_error(errmsg: RNS.MessageBase):
|
||||
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):
|
||||
|
||||
global _finished, _link
|
||||
if timeout is None:
|
||||
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
|
||||
|
||||
async def listen(configdir, rnsconfigdir, command, identitypath=None, service_name=None, verbosity=0, quietness=0, allowed=None,
|
||||
allowed_file=None, disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False,
|
||||
async def listen(configdir, rnsconfigdir, command, identitypath=None, logfile=None, service_name=None, verbosity=0, quietness=0,
|
||||
allowed=None, allowed_file=None, disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False,
|
||||
loop: asyncio.AbstractEventLoop = None):
|
||||
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
|
||||
|
||||
if not loop: loop = asyncio.get_running_loop()
|
||||
if service_name is None or len(service_name) == 0:
|
||||
service_name = "default"
|
||||
if service_name is None or len(service_name) == 0: service_name = "default"
|
||||
|
||||
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
|
||||
targetloglevel = compute_target_rns_loglevel(verbosity, quietness, RNS.LOG_INFO)
|
||||
_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)
|
||||
|
||||
RNS.log(f"rnsh listening for commands on {RNS.prettyhexrep(_destination.hash)}", RNS.LOG_NOTICE)
|
||||
RNS.logdest = logdest
|
||||
|
||||
_cmd = command
|
||||
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 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 "")
|
||||
if identity_path is None:
|
||||
identity_path = RNS.Reticulum.identitypath + "/" + APP_NAME + \
|
||||
(f".{service_name}" if service_name and len(service_name) > 0 else "")
|
||||
if not identity_path:
|
||||
identity_path = f"{configdir}/identity" + (f".{service_name}" if service_name and len(service_name) > 0 else "")
|
||||
|
||||
identity = None
|
||||
if os.path.isfile(identity_path):
|
||||
RNS.log(f"Loading identity from {identity_path}", RNS.LOG_VERBOSE)
|
||||
identity = RNS.Identity.from_file(identity_path)
|
||||
|
||||
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.to_file(identity_path)
|
||||
|
||||
return identity
|
||||
|
||||
|
||||
def print_identity(configdir, identitypath, service_name, include_destination: bool):
|
||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=RNS.LOG_INFO)
|
||||
if service_name and len(service_name) > 0:
|
||||
print(f"Using service name \"{service_name}\"")
|
||||
identity = prepare_identity(identitypath, service_name)
|
||||
def print_identity(configdir, rnsconfigdir, identitypath, service_name, include_destination: bool):
|
||||
reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=RNS.LOG_INFO)
|
||||
if service_name and len(service_name) > 0: print(f"Using service name \"{service_name}\"")
|
||||
identity = prepare_identity(identity_path=identitypath, service_name=service_name, configdir=configdir)
|
||||
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME)
|
||||
print("Identity : " + str(identity))
|
||||
if include_destination:
|
||||
print("Listening on : " + RNS.prettyhexrep(destination.hash))
|
||||
if include_destination: print("Listening on : " + RNS.prettyhexrep(destination.hash))
|
||||
|
||||
exit(0)
|
||||
|
||||
verbose_set = False
|
||||
|
||||
def ensure_config_directory():
|
||||
if 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")
|
||||
def ensure_config_directory(configdir=None):
|
||||
if configdir:
|
||||
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:
|
||||
try:
|
||||
RNS.log(f"Creating configuration directory: {os.path.expanduser('~/.rnsh')}")
|
||||
os.makedirs(os.path.expanduser("~/.rnsh"))
|
||||
return os.path.expanduser("~/.rnsh")
|
||||
|
||||
@@ -106,24 +117,29 @@ async def _rnsh_cli_main():
|
||||
global verbose_set
|
||||
args, parser = parse_arguments()
|
||||
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:
|
||||
print_identity(args.config, args.identity, args.service, args.listen)
|
||||
print_identity(configdir, args.rnsconfig, args.identity, args.service, args.listen)
|
||||
return 0
|
||||
|
||||
logfile = f"{configdir}/logfile"
|
||||
|
||||
if args.listen:
|
||||
allowed_file = None
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if os.path.isfile(os.path.expanduser("~/.config/rnsh/allowed_identities")):
|
||||
allowed_file = os.path.expanduser("~/.config/rnsh/allowed_identities")
|
||||
elif os.path.isfile(os.path.expanduser("~/.rnsh/allowed_identities")):
|
||||
allowed_file = os.path.expanduser("~/.rnsh/allowed_identities")
|
||||
if os.path.isfile(os.path.expanduser(f"{configdir}/allowed_identities")):
|
||||
allowed_file = os.path.expanduser(f"{configdir}/allowed_identities")
|
||||
|
||||
await listener.listen(configdir=configdir,
|
||||
rnsconfigdir=args.config,
|
||||
rnsconfigdir=args.rnsconfig,
|
||||
command=args.command,
|
||||
identitypath=args.identity,
|
||||
logfile=logfile,
|
||||
service_name=args.service,
|
||||
verbosity=args.verbose,
|
||||
quietness=args.quiet,
|
||||
@@ -137,8 +153,9 @@ async def _rnsh_cli_main():
|
||||
|
||||
if args.destination is not None:
|
||||
return_code = await initiator.initiate(configdir=configdir,
|
||||
rnsconfigdir=args.config,
|
||||
rnsconfigdir=args.rnsconfig,
|
||||
identitypath=args.identity,
|
||||
logfile=logfile,
|
||||
verbosity=args.verbose,
|
||||
quietness=args.quiet,
|
||||
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
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--config, -c CONFIG path to alternative Reticulum config directory
|
||||
--identity, -i IDENTITY
|
||||
path to identity file to use
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
--version show program's version number and exit
|
||||
-l, --listen listen (server) mode; any command specified after --
|
||||
will be used as the default command when the initiator
|
||||
does not provide one or when remote command execution
|
||||
is disabled; if no command is specified, the default
|
||||
shell of the user running rnsh will be used
|
||||
-s, --service SERVICE
|
||||
service name for identity file if not the default
|
||||
-b, --announce PERIOD
|
||||
announce on startup and every PERIOD seconds; specify
|
||||
0 to announce on startup only
|
||||
-a, --allowed HASH allow this identity to connect (may be specified
|
||||
multiple times); allowed identities can also be
|
||||
specified in ~/.rnsh/allowed_identities or
|
||||
~/.config/rnsh/allowed_identities, one hash per line
|
||||
-n, --no-auth disable authentication (allow any identity to connect)
|
||||
-h, --help show this help message and exit
|
||||
--config, -c PATH path to config directory
|
||||
--rnsconfig, -c PATH path to alternative Reticulum config directory
|
||||
--identity, -i IDENTITY path to identity file to use
|
||||
-v, --verbose increase verbosity
|
||||
-q, --quiet decrease verbosity
|
||||
-p, --print-identity print identity and destination info and exit
|
||||
--version show program's version number and exit
|
||||
-l, --listen listen (server) mode; any command specified after --
|
||||
will be used as the default command when the initiator
|
||||
does not provide one or when remote command execution
|
||||
is disabled; if no command is specified, the default
|
||||
shell of the user running rnsh will be used
|
||||
-s, --service SERVICE service name for identity file if not the default
|
||||
-b, --announce PERIOD announce on startup and every PERIOD seconds; specify
|
||||
0 to announce on startup only
|
||||
-a, --allowed HASH allow this identity to connect (may be specified
|
||||
multiple times); allowed identities can also be
|
||||
specified in ~/.rnsh/allowed_identities or
|
||||
~/.config/rnsh/allowed_identities, one hash per line
|
||||
-n, --no-auth disable authentication (allow any identity to connect)
|
||||
-A, --remote-command-as-args
|
||||
concatenate remote command to the argument list of the
|
||||
default program or shell
|
||||
-C, --no-remote-command
|
||||
disable executing command lines received from the
|
||||
remote initiator
|
||||
-N, --no-id disable identity announcement on connect
|
||||
-m, --mirror return with the exit code of the remote process
|
||||
-w, --timeout SECONDS
|
||||
connect and request timeout in seconds
|
||||
concatenate remote command to the argument list of the
|
||||
default program or shell
|
||||
-C, --no-remote-command disable executing command lines received from the
|
||||
remote initiator
|
||||
-N, --no-id disable identity announcement on connect
|
||||
-m, --mirror return with the exit code of the remote process
|
||||
-w, --timeout SECONDS connect and request timeout in seconds
|
||||
|
||||
When specifying a command to execute, separate rnsh options from the command
|
||||
and its arguments with --. For example:
|
||||
|
||||
Reference in New Issue
Block a user