multiSSH3 5.75__py3-none-any.whl → 5.76__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of multiSSH3 might be problematic. Click here for more details.
- multiSSH3.py +38 -15
- {multissh3-5.75.dist-info → multissh3-5.76.dist-info}/METADATA +1 -1
- multissh3-5.76.dist-info/RECORD +6 -0
- multissh3-5.75.dist-info/RECORD +0 -6
- {multissh3-5.75.dist-info → multissh3-5.76.dist-info}/WHEEL +0 -0
- {multissh3-5.75.dist-info → multissh3-5.76.dist-info}/entry_points.txt +0 -0
- {multissh3-5.75.dist-info → multissh3-5.76.dist-info}/top_level.txt +0 -0
multiSSH3.py
CHANGED
|
@@ -55,10 +55,10 @@ except AttributeError:
|
|
|
55
55
|
# If neither is available, use a dummy decorator
|
|
56
56
|
def cache_decorator(func):
|
|
57
57
|
return func
|
|
58
|
-
version = '5.
|
|
58
|
+
version = '5.76'
|
|
59
59
|
VERSION = version
|
|
60
60
|
__version__ = version
|
|
61
|
-
COMMIT_DATE = '2025-06-
|
|
61
|
+
COMMIT_DATE = '2025-06-25'
|
|
62
62
|
|
|
63
63
|
CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
|
|
64
64
|
'~/multiSSH3.config.json',
|
|
@@ -78,6 +78,24 @@ def eprint(*args, **kwargs):
|
|
|
78
78
|
print(f"Error: Cannot print to stderr: {e}")
|
|
79
79
|
print(*args, **kwargs)
|
|
80
80
|
|
|
81
|
+
def _exit_with_code(code, message=None):
|
|
82
|
+
'''
|
|
83
|
+
Exit the program with a specific code and print a message
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
code (int): The exit code
|
|
87
|
+
message (str, optional): The message to print. Defaults to None.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
None
|
|
91
|
+
'''
|
|
92
|
+
global __returnZero
|
|
93
|
+
if message:
|
|
94
|
+
eprint('Exiting: '+ message)
|
|
95
|
+
if __returnZero:
|
|
96
|
+
code = 0
|
|
97
|
+
sys.exit(code)
|
|
98
|
+
|
|
81
99
|
def signal_handler(sig, frame):
|
|
82
100
|
'''
|
|
83
101
|
Handle the Ctrl C signal
|
|
@@ -98,7 +116,7 @@ def signal_handler(sig, frame):
|
|
|
98
116
|
# wait for 0.1 seconds to allow the threads to exit
|
|
99
117
|
time.sleep(0.1)
|
|
100
118
|
os.system(f'pkill -ef {os.path.basename(__file__)}')
|
|
101
|
-
|
|
119
|
+
_exit_with_code(1, 'Exiting immediately due to Ctrl C')
|
|
102
120
|
|
|
103
121
|
# def input_with_timeout_and_countdown(timeout, prompt='Please enter your selection'):
|
|
104
122
|
# """
|
|
@@ -303,6 +321,7 @@ DEFAULT_CURSES_MINIMUM_LINE_LEN = 1
|
|
|
303
321
|
DEFAULT_SINGLE_WINDOW = False
|
|
304
322
|
DEFAULT_ERROR_ONLY = False
|
|
305
323
|
DEFAULT_NO_OUTPUT = False
|
|
324
|
+
DEFAULT_RETURN_ZERO = False
|
|
306
325
|
DEFAULT_NO_ENV = False
|
|
307
326
|
DEFAULT_ENV_FILE = '/etc/profile.d/hosts.sh'
|
|
308
327
|
DEFAULT_NO_HISTORY = False
|
|
@@ -362,6 +381,7 @@ __curses_current_color_index = 10
|
|
|
362
381
|
__max_connections_nofile_limit_supported = 0
|
|
363
382
|
__thread_start_delay = 0
|
|
364
383
|
_encoding = DEFAULT_ENCODING
|
|
384
|
+
__returnZero = DEFAULT_RETURN_ZERO
|
|
365
385
|
if __resource_lib_available:
|
|
366
386
|
# Get the current limits
|
|
367
387
|
_, __system_nofile_limit = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
@@ -2761,7 +2781,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2761
2781
|
else:
|
|
2762
2782
|
eprint(f"Warning: ssh-copy-id not found in {_binPaths} , skipping copy id to the hosts")
|
|
2763
2783
|
if not commands:
|
|
2764
|
-
|
|
2784
|
+
_exit_with_code(0, "Copy id finished, no commands to run")
|
|
2765
2785
|
if files and not commands:
|
|
2766
2786
|
# if files are specified but not target dir, we default to file sync mode
|
|
2767
2787
|
file_sync = True
|
|
@@ -2778,8 +2798,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2778
2798
|
except:
|
|
2779
2799
|
pathSet.update(glob.glob(file,recursive=True))
|
|
2780
2800
|
if not pathSet:
|
|
2781
|
-
|
|
2782
|
-
sys.exit(66)
|
|
2801
|
+
_exit_with_code(66, f'No source files at {files!r} are found after resolving globs!')
|
|
2783
2802
|
else:
|
|
2784
2803
|
pathSet = set(files)
|
|
2785
2804
|
if file_sync:
|
|
@@ -2796,7 +2815,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2796
2815
|
eprint("Error: the number of commands must be the same as the number of hosts")
|
|
2797
2816
|
eprint(f"Number of commands: {len(commands)}")
|
|
2798
2817
|
eprint(f"Number of hosts: {len(set(targetHostDic) - set(skipHostSet))}")
|
|
2799
|
-
|
|
2818
|
+
_exit_with_code(255, "Number of commands and hosts do not match")
|
|
2800
2819
|
if not __global_suppress_printout:
|
|
2801
2820
|
eprint('-'*80)
|
|
2802
2821
|
eprint("Running in one on one mode")
|
|
@@ -2911,6 +2930,7 @@ def generate_default_config(args):
|
|
|
2911
2930
|
'DEFAULT_SINGLE_WINDOW': args.single_window,
|
|
2912
2931
|
'DEFAULT_ERROR_ONLY': args.error_only,
|
|
2913
2932
|
'DEFAULT_NO_OUTPUT': args.no_output,
|
|
2933
|
+
'DEFAULT_RETURN_ZERO': args.return_zero,
|
|
2914
2934
|
'DEFAULT_NO_ENV': args.no_env,
|
|
2915
2935
|
'DEFAULT_ENV_FILE': args.env_file,
|
|
2916
2936
|
'DEFAULT_NO_HISTORY': args.no_history,
|
|
@@ -2945,8 +2965,7 @@ def write_default_config(args,CONFIG_FILE = None):
|
|
|
2945
2965
|
elif inStr.lower().strip().startswith('o'):
|
|
2946
2966
|
backup = False
|
|
2947
2967
|
else:
|
|
2948
|
-
|
|
2949
|
-
sys.exit(1)
|
|
2968
|
+
_exit_with_code(0, "Aborted by user, no config file written")
|
|
2950
2969
|
try:
|
|
2951
2970
|
if backup and os.path.exists(CONFIG_FILE):
|
|
2952
2971
|
os.rename(CONFIG_FILE,CONFIG_FILE+'.bak')
|
|
@@ -2955,8 +2974,7 @@ def write_default_config(args,CONFIG_FILE = None):
|
|
|
2955
2974
|
eprint(f"Do you want to continue writing the new config file to {CONFIG_FILE!r}? (y/n)")
|
|
2956
2975
|
inStr = input_with_timeout_and_countdown(10)
|
|
2957
2976
|
if not inStr or not inStr.lower().strip().startswith('y'):
|
|
2958
|
-
|
|
2959
|
-
sys.exit(1)
|
|
2977
|
+
_exit_with_code(0, "Aborted by user, no config file written")
|
|
2960
2978
|
try:
|
|
2961
2979
|
with open(CONFIG_FILE,'w') as f:
|
|
2962
2980
|
json.dump(__configs_from_file,f,indent=4)
|
|
@@ -2978,6 +2996,7 @@ def main():
|
|
|
2978
2996
|
global __DEBUG_MODE
|
|
2979
2997
|
global __configs_from_file
|
|
2980
2998
|
global _encoding
|
|
2999
|
+
global __returnZero
|
|
2981
3000
|
_emo = False
|
|
2982
3001
|
# We handle the signal
|
|
2983
3002
|
signal.signal(signal.SIGINT, signal_handler)
|
|
@@ -3010,6 +3029,7 @@ def main():
|
|
|
3010
3029
|
parser.add_argument('-B','-sw','--single_window', action='store_true', help=f'Use a single window for all hosts. (default: {DEFAULT_SINGLE_WINDOW})', default=DEFAULT_SINGLE_WINDOW)
|
|
3011
3030
|
parser.add_argument('-R','-eo','--error_only', action='store_true', help=f'Only print the error output. (default: {DEFAULT_ERROR_ONLY})', default=DEFAULT_ERROR_ONLY)
|
|
3012
3031
|
parser.add_argument('-Q',"-no","--no_output", action='store_true', help=f"Do not print the output. (default: {DEFAULT_NO_OUTPUT})", default=DEFAULT_NO_OUTPUT)
|
|
3032
|
+
parser.add_argument('-Z','-rz','--return_zero', action='store_true', help=f"Return 0 even if there are errors. (default: {DEFAULT_RETURN_ZERO})", default=DEFAULT_RETURN_ZERO)
|
|
3013
3033
|
parser.add_argument('-C','--no_env', action='store_true', help=f'Do not load the command line environment variables. (default: {DEFAULT_NO_ENV})', default=DEFAULT_NO_ENV)
|
|
3014
3034
|
parser.add_argument("--env_file", type=str, help=f"The file to load the mssh file based environment variables from. ( Still work with --no_env ) (default: {DEFAULT_ENV_FILE})", default=DEFAULT_ENV_FILE)
|
|
3015
3035
|
parser.add_argument("-m","--max_connections", type=int, help=f"Max number of connections to use (default: 4 * cpu_count)", default=DEFAULT_MAX_CONNECTIONS)
|
|
@@ -3055,7 +3075,10 @@ def main():
|
|
|
3055
3075
|
args.no_history = True
|
|
3056
3076
|
args.greppable = True
|
|
3057
3077
|
args.error_only = True
|
|
3058
|
-
|
|
3078
|
+
|
|
3079
|
+
if args.return_zero:
|
|
3080
|
+
__returnZero = True
|
|
3081
|
+
|
|
3059
3082
|
if args.generate_config_file or args.store_config_file:
|
|
3060
3083
|
if args.store_config_file:
|
|
3061
3084
|
configFileToWriteTo = args.store_config_file
|
|
@@ -3071,7 +3094,7 @@ def main():
|
|
|
3071
3094
|
if configFileToWriteTo:
|
|
3072
3095
|
with open(configFileToWriteTo,'r') as f:
|
|
3073
3096
|
eprint(f"Config file content: \n{f.read()}")
|
|
3074
|
-
|
|
3097
|
+
_exit_with_code(0)
|
|
3075
3098
|
if args.config_file:
|
|
3076
3099
|
if os.path.exists(args.config_file):
|
|
3077
3100
|
__configs_from_file.update(load_config_file(os.path.expanduser(args.config_file)))
|
|
@@ -3094,7 +3117,7 @@ def main():
|
|
|
3094
3117
|
elif inStr.lower().strip().startswith('m'):
|
|
3095
3118
|
eprint(f"\nRunning multiple commands: {', '.join(args.commands)!r} on all hosts")
|
|
3096
3119
|
else:
|
|
3097
|
-
|
|
3120
|
+
_exit_with_code(0, "Aborted by user, no commands to run")
|
|
3098
3121
|
|
|
3099
3122
|
if args.key or args.use_key:
|
|
3100
3123
|
if not args.key:
|
|
@@ -3179,7 +3202,7 @@ def main():
|
|
|
3179
3202
|
# os.system(f'pkill -ef {os.path.basename(__file__)}')
|
|
3180
3203
|
# os._exit(mainReturnCode)
|
|
3181
3204
|
|
|
3182
|
-
|
|
3205
|
+
_exit_with_code(__mainReturnCode)
|
|
3183
3206
|
|
|
3184
3207
|
if __name__ == "__main__":
|
|
3185
3208
|
main()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
multiSSH3.py,sha256=ioUj2vZW1RiOyXRm_yJizPp751_ggMZeVtDrM6Lvvjk,150851
|
|
2
|
+
multissh3-5.76.dist-info/METADATA,sha256=VYwjn_-6fQHyubKyY6M0cuh6DNJaI4hIkDh4RV0qE5k,18093
|
|
3
|
+
multissh3-5.76.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
+
multissh3-5.76.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
+
multissh3-5.76.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
+
multissh3-5.76.dist-info/RECORD,,
|
multissh3-5.75.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=5pDQ7zSeNmfzu-o081Z7VOfW_8Ke48WPHi0BxnBnHAw,149915
|
|
2
|
-
multissh3-5.75.dist-info/METADATA,sha256=1_pBC-nNbRg_aR3LFNNInE5Whrn9hBaQMhnxEkO2IOg,18093
|
|
3
|
-
multissh3-5.75.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
-
multissh3-5.75.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
-
multissh3-5.75.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
-
multissh3-5.75.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|