mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-04-27 14:20:35 +00:00
Refactored rnsh to use argparse
This commit is contained in:
parent
6abb31e469
commit
f924086198
8 changed files with 119 additions and 47 deletions
59
RNS/Utilities/rnsh/args.py
Normal file
59
RNS/Utilities/rnsh/args.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import argparse
|
||||
import sys
|
||||
|
||||
from RNS.Utilities.rnsh._version import __version__ as __rnsh_version__
|
||||
from RNS._version import __version__
|
||||
|
||||
DEFAULT_SERVICE_NAME = "default"
|
||||
|
||||
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("--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")
|
||||
parser.add_argument("-p", "--print-identity", action="store_true", default=False, help="print identity and destination info and exit")
|
||||
parser.add_argument("--version", action="version", version="rnsh {rv} (protocol {pv})".format(rv=__version__, pv=__rnsh_version__))
|
||||
|
||||
# Listener options
|
||||
parser.add_argument("-l", "--listen", action="store_true", default=False, help="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")
|
||||
parser.add_argument("-s", "--service", action="store", default=None, help="service name for identity file if not the default", type=str)
|
||||
parser.add_argument("-b", "--announce",action="store", default=None,help="announce on startup and every PERIOD seconds; specify 0 to announce on startup only",metavar="PERIOD", type=int)
|
||||
parser.add_argument("-a", "--allowed", action="append", default=None, metavar="HASH", type=str, help="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")
|
||||
parser.add_argument("-n", "--no-auth", action="store_true", default=False, help="disable authentication (allow any identity to connect)")
|
||||
parser.add_argument("-A", "--remote-command-as-args", action="store_true", default=False, help="concatenate remote command to the argument list of the default program or shell")
|
||||
parser.add_argument("-C", "--no-remote-command", action="store_true", default=False, help="disable executing command lines received from the remote initiator")
|
||||
|
||||
# Initiator options
|
||||
parser.add_argument("-N", "--no-id", action="store_true", default=False, help="disable identity announcement on connect")
|
||||
parser.add_argument("-m", "--mirror", action="store_true", default=False, help="return with the exit code of the remote process")
|
||||
parser.add_argument("-w", "--timeout", action="store", default=None, help="connect and request timeout in seconds", metavar="SECONDS", type=float)
|
||||
|
||||
parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the destination to connect to", type=str)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def parse_arguments(argv=None):
|
||||
if argv is None: argv = sys.argv[1:]
|
||||
|
||||
# Split at -- to separate rnsh options from the command to execute.
|
||||
# Everything before -- (or the entire argv if no --) goes to argparse.
|
||||
# Everything after -- becomes the command list.
|
||||
try:
|
||||
split_idx = argv.index("--")
|
||||
rnsh_argv = argv[:split_idx]
|
||||
command = argv[split_idx + 1:]
|
||||
except ValueError:
|
||||
rnsh_argv = argv
|
||||
command = []
|
||||
|
||||
parser = setup_argument_parser()
|
||||
args = parser.parse_args(rnsh_argv)
|
||||
args.command = command
|
||||
|
||||
if args.listen and not args.service: args.service = DEFAULT_SERVICE_NAME
|
||||
|
||||
return args, parser
|
||||
|
|
@ -39,18 +39,18 @@ import time
|
|||
import tty
|
||||
from typing import Callable, TypeVar
|
||||
import RNS
|
||||
import rnsh.exception as exception
|
||||
import rnsh.process as process
|
||||
import rnsh.retry as retry
|
||||
import rnsh.session as session
|
||||
import RNS.Utilities.rnsh.exception as exception
|
||||
import RNS.Utilities.rnsh.process as process
|
||||
import RNS.Utilities.rnsh.retry as retry
|
||||
import RNS.Utilities.rnsh.session as session
|
||||
import re
|
||||
import contextlib
|
||||
import rnsh.args
|
||||
|
||||
import pwd
|
||||
import bz2
|
||||
import rnsh.protocol as protocol
|
||||
import rnsh.helpers as helpers
|
||||
import rnsh.rnsh
|
||||
import RNS.Utilities.rnsh.protocol as protocol
|
||||
import RNS.Utilities.rnsh.helpers as helpers
|
||||
import RNS.Utilities.rnsh.rnsh as rnsh
|
||||
|
||||
_identity = None
|
||||
_reticulum = None
|
||||
|
|
@ -154,7 +154,7 @@ async def _initiate_link(configdir, rnsconfigdir, identitypath=None, verbosity=0
|
|||
_reticulum = RNS.Reticulum(configdir=rnsconfigdir, loglevel=targetloglevel, logdest=RNS.LOG_FILE)
|
||||
|
||||
if _identity is None:
|
||||
_identity = rnsh.rnsh.prepare_identity(identitypath)
|
||||
_identity = rnsh.prepare_identity(identitypath)
|
||||
|
||||
if not RNS.Transport.has_path(destination_hash):
|
||||
RNS.Transport.request_path(destination_hash)
|
||||
|
|
@ -169,7 +169,7 @@ async def _initiate_link(configdir, rnsconfigdir, identitypath=None, verbosity=0
|
|||
listener_identity,
|
||||
RNS.Destination.OUT,
|
||||
RNS.Destination.SINGLE,
|
||||
rnsh.rnsh.APP_NAME
|
||||
rnsh.APP_NAME
|
||||
)
|
||||
|
||||
if _link is None or _link.status == RNS.Link.PENDING:
|
||||
|
|
|
|||
|
|
@ -36,17 +36,17 @@ import time
|
|||
import tty
|
||||
from typing import Callable, TypeVar
|
||||
import RNS
|
||||
import rnsh.exception as exception
|
||||
import rnsh.process as process
|
||||
import rnsh.retry as retry
|
||||
import rnsh.session as session
|
||||
import RNS.Utilities.rnsh.exception as exception
|
||||
import RNS.Utilities.rnsh.process as process
|
||||
import RNS.Utilities.rnsh.retry as retry
|
||||
import RNS.Utilities.rnsh.session as session
|
||||
import re
|
||||
import contextlib
|
||||
import rnsh.args
|
||||
|
||||
import pwd
|
||||
import rnsh.protocol as protocol
|
||||
import rnsh.helpers as helpers
|
||||
import rnsh.rnsh
|
||||
import RNS.Utilities.rnsh.protocol as protocol
|
||||
import RNS.Utilities.rnsh.helpers as helpers
|
||||
import RNS.Utilities.rnsh.rnsh as rnsh
|
||||
|
||||
|
||||
_identity = None
|
||||
|
|
@ -123,8 +123,8 @@ async def listen(configdir, rnsconfigdir, command, identitypath=None, service_na
|
|||
# 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.rnsh.prepare_identity(identitypath, service_name)
|
||||
_destination = RNS.Destination(_identity, RNS.Destination.IN, RNS.Destination.SINGLE, rnsh.rnsh.APP_NAME)
|
||||
_identity = rnsh.prepare_identity(identitypath, service_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)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import types
|
|||
import typing
|
||||
import RNS
|
||||
|
||||
import rnsh.exception as exception
|
||||
import RNS.Utilities.rnsh.exception as exception
|
||||
|
||||
CTRL_C = "\x03".encode("utf-8")
|
||||
CTRL_D = "\x04".encode("utf-8")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
import RNS
|
||||
from RNS.vendor import umsgpack
|
||||
from RNS.Buffer import StreamDataMessage as RNSStreamDataMessage
|
||||
import rnsh.retry
|
||||
import RNS.Utilities.rnsh.retry
|
||||
import abc
|
||||
import contextlib
|
||||
import struct
|
||||
|
|
@ -77,7 +77,7 @@ class VersionInfoMessage(RNS.MessageBase):
|
|||
|
||||
def __init__(self, sw_version: str = None):
|
||||
super().__init__()
|
||||
self.sw_version = sw_version or rnsh.__version__
|
||||
self.sw_version = sw_version or RNS.Utilities.rnsh.__version__
|
||||
self.protocol_version = PROTOCOL_VERSION
|
||||
|
||||
def pack(self) -> bytes: return umsgpack.packb((self.sw_version, self.protocol_version))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
# MIT License
|
||||
# Based on the original rnsh program by Aaron Heise (@acehoss)
|
||||
# https://github.com/acehoss/rnsh - MIT License - Copyright (c) 2023 Aaron Heise
|
||||
# This version of rnsh is included in RNS under the Reticulum License
|
||||
#
|
||||
# Copyright (c) 2023 Aaron Heise
|
||||
# Reticulum License
|
||||
#
|
||||
# Copyright (c) 2016-2025 Mark Qvist
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -9,8 +13,16 @@
|
|||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
# - The Software shall not be used in any kind of system which includes amongst
|
||||
# its functions the ability to purposefully do harm to human beings.
|
||||
#
|
||||
# - The Software shall not be used, directly or indirectly, in the creation of
|
||||
# an artificial intelligence, machine learning or language model training
|
||||
# dataset, including but not limited to any use that contributes to the
|
||||
# training or development of such a model or algorithm.
|
||||
#
|
||||
# - The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
|
|
@ -23,7 +35,7 @@
|
|||
import asyncio
|
||||
import threading
|
||||
import time
|
||||
import rnsh.exception as exception
|
||||
import RNS.Utilities.rnsh.exception as exception
|
||||
from typing import Callable
|
||||
from contextlib import AbstractContextManager
|
||||
import types
|
||||
|
|
|
|||
|
|
@ -32,12 +32,13 @@ import os
|
|||
import sys
|
||||
|
||||
import RNS
|
||||
import rnsh.process as process
|
||||
import rnsh.session as session
|
||||
import rnsh.args
|
||||
import rnsh.loop
|
||||
import rnsh.listener as listener
|
||||
import rnsh.initiator as initiator
|
||||
import RNS.Utilities.rnsh.process as process
|
||||
import RNS.Utilities.rnsh.session as session
|
||||
import RNS.Utilities.rnsh.args
|
||||
import RNS.Utilities.rnsh.loop
|
||||
import RNS.Utilities.rnsh.listener as listener
|
||||
import RNS.Utilities.rnsh.initiator as initiator
|
||||
from RNS.Utilities.rnsh.args import parse_arguments
|
||||
|
||||
APP_NAME = "rnsh"
|
||||
loop: asyncio.AbstractEventLoop | None = None
|
||||
|
|
@ -91,13 +92,13 @@ def ensure_config_directory():
|
|||
|
||||
async def _rnsh_cli_main():
|
||||
global verbose_set
|
||||
args = rnsh.args.Args(sys.argv)
|
||||
args, parser = parse_arguments()
|
||||
verbose_set = args.verbose > 0
|
||||
|
||||
configdir = ensure_config_directory()
|
||||
|
||||
if args.print_identity:
|
||||
print_identity(args.config, args.identity, args.service_name, args.listen)
|
||||
print_identity(args.config, args.identity, args.service, args.listen)
|
||||
return 0
|
||||
|
||||
if args.listen:
|
||||
|
|
@ -110,17 +111,17 @@ async def _rnsh_cli_main():
|
|||
|
||||
await listener.listen(configdir=configdir,
|
||||
rnsconfigdir=args.config,
|
||||
command=args.command_line,
|
||||
command=args.command,
|
||||
identitypath=args.identity,
|
||||
service_name=args.service_name,
|
||||
service_name=args.service,
|
||||
verbosity=args.verbose,
|
||||
quietness=args.quiet,
|
||||
allowed=args.allowed,
|
||||
allowed=args.allowed or [],
|
||||
allowed_file=allowed_file,
|
||||
disable_auth=args.no_auth,
|
||||
announce_period=args.announce,
|
||||
no_remote_command=args.no_remote_cmd,
|
||||
remote_cmd_as_args=args.remote_cmd_as_args)
|
||||
no_remote_command=args.no_remote_command,
|
||||
remote_cmd_as_args=args.remote_command_as_args)
|
||||
return 0
|
||||
|
||||
if args.destination is not None:
|
||||
|
|
@ -132,12 +133,12 @@ async def _rnsh_cli_main():
|
|||
noid=args.no_id,
|
||||
destination=args.destination,
|
||||
timeout=args.timeout,
|
||||
command=args.command_line
|
||||
command=args.command
|
||||
)
|
||||
return return_code if args.mirror else 0
|
||||
else:
|
||||
print("")
|
||||
print(rnsh.args.usage)
|
||||
parser.print_help()
|
||||
print("")
|
||||
return 1
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from __future__ import annotations
|
||||
import contextlib
|
||||
import functools
|
||||
import rnsh.exception as exception
|
||||
import asyncio
|
||||
import rnsh.process as process
|
||||
import rnsh.helpers as helpers
|
||||
import rnsh.protocol as protocol
|
||||
import RNS.Utilities.rnsh.exception as exception
|
||||
import RNS.Utilities.rnsh.process as process
|
||||
import RNS.Utilities.rnsh.helpers as helpers
|
||||
import RNS.Utilities.rnsh.protocol as protocol
|
||||
import enum
|
||||
from typing import TypeVar, Generic, Callable, List
|
||||
from abc import abstractmethod, ABC
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue