Compare commits
10 Commits
Difrex/mas
...
v0.9
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c8f072499 | |||
|
|
68bb493c44 | ||
| e75a733d0b | |||
| c057e1e8ed | |||
| 0266d29308 | |||
|
|
de2f544178 | ||
| 083144d656 | |||
| afa565104c | |||
| d5844ab267 | |||
| 48eea214fa |
@@ -23,3 +23,9 @@ To run command line help:
|
|||||||
sshch -h
|
sshch -h
|
||||||
```
|
```
|
||||||
**If you want to use unsafe 'password' feature you must install 'sshpass' first.**
|
**If you want to use unsafe 'password' feature you must install 'sshpass' first.**
|
||||||
|
|
||||||
|
If you want to use bash autocompletion function with sshch, copy autocompletion script to /etc/bash_completion.d/:
|
||||||
|
```
|
||||||
|
sudo cp sshch_bash_completion.sh /etc/bash_completion.d/sshch
|
||||||
|
```
|
||||||
|
(changes will come into effect with new bash session)
|
||||||
2
setup.py
2
setup.py
@@ -10,7 +10,7 @@ def main():
|
|||||||
url='https://github.com/zlaxy/sshch/',
|
url='https://github.com/zlaxy/sshch/',
|
||||||
description='Ssh connection manager',
|
description='Ssh connection manager',
|
||||||
license='DWTWL 2.5',
|
license='DWTWL 2.5',
|
||||||
version='0.55',
|
version='0.8',
|
||||||
py_modules=['sshch'],
|
py_modules=['sshch'],
|
||||||
scripts=['sshch/sshch'],
|
scripts=['sshch/sshch'],
|
||||||
|
|
||||||
|
|||||||
102
sshch/sshch
102
sshch/sshch
@@ -1,20 +1,26 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
try:
|
||||||
|
import configparser
|
||||||
|
except ImportError:
|
||||||
|
import ConfigParser as configparser # Python 2.x import
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
from sys import argv
|
from sys import argv
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from curses import textpad
|
|
||||||
import ConfigParser
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import base64
|
import base64
|
||||||
|
import time
|
||||||
import curses
|
import curses
|
||||||
|
from curses import textpad, panel
|
||||||
|
|
||||||
# https://github.com/zlaxy/sshch
|
# https://github.com/zlaxy/sshch
|
||||||
version = "0.55"
|
version = "0.9"
|
||||||
# path to conf file, default: ~/.config/sshch.conf
|
# path to conf file, default: ~/.config/sshch.conf
|
||||||
conf_file = path.expanduser("~") + '/.config/sshch.conf'
|
conf_file = path.expanduser("~") + '/.config/sshch.conf'
|
||||||
|
|
||||||
@@ -47,19 +53,25 @@ def RemoveAliases(aliases):
|
|||||||
|
|
||||||
|
|
||||||
def ConnectAlias(alias, command=False):
|
def ConnectAlias(alias, command=False):
|
||||||
print "Connecting to " + alias + "..."
|
|
||||||
exec_string = ""
|
exec_string = ""
|
||||||
if conf.has_option(alias, "password"):
|
if conf.has_option(alias, "password"):
|
||||||
password = base64.b32decode(base64.b16decode(
|
password = base64.b32decode(base64.b16decode(
|
||||||
base64.b64decode(conf.get(alias, "password"))))
|
base64.b64decode(conf.get(alias, "password"))))
|
||||||
exec_string = "sshpass -p " + password + " "
|
exec_string = 'sshpass -p "' + password + '" '
|
||||||
|
if conf.has_option(alias, "exec_string"):
|
||||||
exec_string = exec_string + conf.get(alias, "exec_string")
|
exec_string = exec_string + conf.get(alias, "exec_string")
|
||||||
if command:
|
if command:
|
||||||
exec_string = exec_string + " " + command
|
exec_string = exec_string + " " + command
|
||||||
# Variables bellow is newer used
|
# Variables bellow is newer used
|
||||||
subprocess.Popen(exec_string, shell=True)
|
subprocess.Popen(exec_string, shell=True).communicate()[0]
|
||||||
# p.communicate()[0]
|
|
||||||
print "... " + alias + " session finished."
|
|
||||||
|
def HoldConnection(alias):
|
||||||
|
print("Connecting to " + alias + ". Press CTRL+C to cancel.")
|
||||||
|
time.sleep(1)
|
||||||
|
while True:
|
||||||
|
ConnectAlias(alias)
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
def CMDAdd(alias):
|
def CMDAdd(alias):
|
||||||
@@ -69,10 +81,10 @@ def CMDAdd(alias):
|
|||||||
"(example: ssh user@somehost.com):\n"]))
|
"(example: ssh user@somehost.com):\n"]))
|
||||||
string = ""
|
string = ""
|
||||||
while string == "":
|
while string == "":
|
||||||
string = raw_input(prompt_add)
|
string = input(prompt_add)
|
||||||
SetAliasString(alias, string)
|
SetAliasString(alias, string)
|
||||||
else:
|
else:
|
||||||
print result
|
print(result)
|
||||||
|
|
||||||
|
|
||||||
def CMDEdit(alias):
|
def CMDEdit(alias):
|
||||||
@@ -81,10 +93,10 @@ def CMDEdit(alias):
|
|||||||
"(example: ssh user@somehost.com):\n"]))
|
"(example: ssh user@somehost.com):\n"]))
|
||||||
string = ""
|
string = ""
|
||||||
while string == "":
|
while string == "":
|
||||||
string = raw_input(prompt_edit)
|
string = input(prompt_edit)
|
||||||
SetAliasString(alias, string)
|
SetAliasString(alias, string)
|
||||||
else:
|
else:
|
||||||
print "error: '" + alias + "' alias is not exists"
|
print("error: '" + alias + "' alias is not exists")
|
||||||
|
|
||||||
|
|
||||||
def CMDPassword(alias):
|
def CMDPassword(alias):
|
||||||
@@ -95,52 +107,57 @@ def CMDPassword(alias):
|
|||||||
if not string == "":
|
if not string == "":
|
||||||
SetPassword(alias, string)
|
SetPassword(alias, string)
|
||||||
else:
|
else:
|
||||||
print "error: '" + alias + "' alias is not exists"
|
print("error: '" + alias + "' alias is not exists")
|
||||||
|
|
||||||
|
|
||||||
def CMDRemove(alias):
|
def CMDRemove(alias):
|
||||||
if conf.has_section(alias):
|
if conf.has_section(alias):
|
||||||
prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias: ")
|
prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias: ")
|
||||||
string = raw_input(prompt_remove)
|
string = input(prompt_remove)
|
||||||
if string == "yes":
|
if string == "yes":
|
||||||
RemoveAliases([alias])
|
RemoveAliases([alias])
|
||||||
else:
|
else:
|
||||||
print "'" + alias + "' alias was not deleted."
|
print("'" + alias + "' alias was not deleted.")
|
||||||
else:
|
else:
|
||||||
print "error: '" + alias + "' alias is not exists."
|
print("error: '" + alias + "' alias is not exists.")
|
||||||
|
|
||||||
|
|
||||||
def CMDConnect(aliases, command=False):
|
def CMDConnect(aliases, command=False):
|
||||||
for alias in aliases:
|
for alias in aliases:
|
||||||
if conf.has_section(alias):
|
if conf.has_section(alias):
|
||||||
|
print("Connecting to " + alias + "...")
|
||||||
ConnectAlias(alias, command)
|
ConnectAlias(alias, command)
|
||||||
|
print("... " + alias + " session finished.")
|
||||||
else:
|
else:
|
||||||
print "error: '" + alias + "' alias is not exists"
|
print("error: '" + alias + "' alias is not exists")
|
||||||
|
|
||||||
|
|
||||||
def CMDList(option, opt, value, parser):
|
def CMDList(option, opt, value, parser):
|
||||||
print ', '.join(str(p) for p in conf.sections())
|
print(', '.join(str(p) for p in conf.sections()))
|
||||||
|
|
||||||
|
|
||||||
def CMDFullList(option, opt, value, parser):
|
def CMDFullList(option, opt, value, parser):
|
||||||
for p in conf.sections():
|
for p in conf.sections():
|
||||||
to_print = "".join([str(p), " - ", conf.get(p, "exec_string"),
|
to_print = "".join([str(p), " - ", (conf.get(p, "exec_string") if
|
||||||
(" [password]" if conf.has_option(p, "password") else ""), "\n"])
|
conf.has_option(p, "exec_string") else ""),
|
||||||
|
(" [password]" if conf.has_option(p, "password") else "")])
|
||||||
print(to_print)
|
print(to_print)
|
||||||
|
|
||||||
|
|
||||||
def CursesConnect(screen, aliases, command=False):
|
def CursesConnect(screen, aliases, command=False):
|
||||||
curses.endwin()
|
curses.endwin()
|
||||||
for alias in aliases:
|
for alias in aliases:
|
||||||
|
print("Connecting to " + alias + "...")
|
||||||
ConnectAlias(alias, command)
|
ConnectAlias(alias, command)
|
||||||
print "Press 'enter' to continue."
|
print("... " + alias + " session finished.")
|
||||||
|
print("Press 'enter' to continue.")
|
||||||
screen.getch()
|
screen.getch()
|
||||||
|
|
||||||
|
|
||||||
def CursesExit(error=False):
|
def CursesExit(error=False):
|
||||||
curses.endwin()
|
curses.endwin()
|
||||||
if error:
|
if error:
|
||||||
print error
|
print(error)
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
@@ -176,7 +193,7 @@ def CursesPanel(screen, h, w, y, x, text,
|
|||||||
new_window.attroff(deco_colorpair)
|
new_window.attroff(deco_colorpair)
|
||||||
sub_window = new_window.subwin(h - 2, w - 2, y + 1, x + 1)
|
sub_window = new_window.subwin(h - 2, w - 2, y + 1, x + 1)
|
||||||
sub_window.insstr(0, 0, text)
|
sub_window.insstr(0, 0, text)
|
||||||
curses.panel.new_panel(new_window)
|
panel = curses.panel.new_panel(new_window)
|
||||||
curses.panel.update_panels()
|
curses.panel.update_panels()
|
||||||
screen.refresh()
|
screen.refresh()
|
||||||
if confirm == "password":
|
if confirm == "password":
|
||||||
@@ -245,6 +262,9 @@ def CMDOptions():
|
|||||||
opts.add_option('-c', '--command', action="store", type="string",
|
opts.add_option('-c', '--command', action="store", type="string",
|
||||||
dest="command", metavar="command", default=False,
|
dest="command", metavar="command", default=False,
|
||||||
help="add command for executing alias")
|
help="add command for executing alias")
|
||||||
|
opts.add_option('-k', '--keep', action="store", type="string",
|
||||||
|
dest="keep", metavar="alias", default=False,
|
||||||
|
help="hold connection with specified alias")
|
||||||
opts.add_option('-e', '--edit', action="store", type="string",
|
opts.add_option('-e', '--edit', action="store", type="string",
|
||||||
dest='edit', metavar="alias", default=False,
|
dest='edit', metavar="alias", default=False,
|
||||||
help="edit existing connection string")
|
help="edit existing connection string")
|
||||||
@@ -263,6 +283,8 @@ def CMDOptions():
|
|||||||
CMDPassword(options.password)
|
CMDPassword(options.password)
|
||||||
if options.remove:
|
if options.remove:
|
||||||
CMDRemove(options.remove)
|
CMDRemove(options.remove)
|
||||||
|
if options.keep:
|
||||||
|
HoldConnection(options.keep)
|
||||||
if alias:
|
if alias:
|
||||||
CMDConnect(alias, options.command)
|
CMDConnect(alias, options.command)
|
||||||
|
|
||||||
@@ -278,6 +300,7 @@ def CursesMain():
|
|||||||
" 'space'/'insert' - select\n",
|
" 'space'/'insert' - select\n",
|
||||||
" 'r'/'F8' - remove selected alias/aliases\n",
|
" 'r'/'F8' - remove selected alias/aliases\n",
|
||||||
" 'c'/'F3' - execute specific command with selected alias/aliases\n",
|
" 'c'/'F3' - execute specific command with selected alias/aliases\n",
|
||||||
|
" 'k'/'F7' - hold connection with selected alias\n",
|
||||||
" 'enter'/'F9' - connect to selected alias/aliases\n",
|
" 'enter'/'F9' - connect to selected alias/aliases\n",
|
||||||
" 'q'/'F10' - quit\n",
|
" 'q'/'F10' - quit\n",
|
||||||
" Run program with '--help' option to view command line help.\n",
|
" Run program with '--help' option to view command line help.\n",
|
||||||
@@ -315,8 +338,9 @@ def CursesMain():
|
|||||||
else:
|
else:
|
||||||
password = ""
|
password = ""
|
||||||
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
|
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
|
||||||
strings[i - 1], " (", conf.get(strings[i - 1], "exec_string"),
|
strings[i - 1], " (", (conf.get(strings[i - 1],
|
||||||
")", password]
|
"exec_string") if conf.has_option(strings[i - 1],
|
||||||
|
"exec_string") else ""), ")", password]
|
||||||
if (i == position):
|
if (i == position):
|
||||||
box.addnstr(i, 2, "".join(exec_string), width - 6, highlight_text)
|
box.addnstr(i, 2, "".join(exec_string), width - 6, highlight_text)
|
||||||
else:
|
else:
|
||||||
@@ -369,7 +393,9 @@ def CursesMain():
|
|||||||
while edit_string.rstrip() == "":
|
while edit_string.rstrip() == "":
|
||||||
string_textpad = CursesTextpad(screen, 3, width - 8,
|
string_textpad = CursesTextpad(screen, 3, width - 8,
|
||||||
(height // 2) - 1, 4, "Enter new execution string:",
|
(height // 2) - 1, 4, "Enter new execution string:",
|
||||||
conf.get(strings[position - 1], "exec_string"),
|
(conf.get(strings[position - 1],
|
||||||
|
"exec_string") if conf.has_option(strings[position - 1],
|
||||||
|
"exec_string") else ""),
|
||||||
normal_text, highlight_text)
|
normal_text, highlight_text)
|
||||||
edit_string = string_textpad.edit(CursesTextpadConfirm)
|
edit_string = string_textpad.edit(CursesTextpadConfirm)
|
||||||
SetAliasString(strings[position - 1],
|
SetAliasString(strings[position - 1],
|
||||||
@@ -428,6 +454,10 @@ def CursesMain():
|
|||||||
command_string = command_textpad.edit(CursesTextpadConfirm)
|
command_string = command_textpad.edit(CursesTextpadConfirm)
|
||||||
CursesConnect(screen, selected,
|
CursesConnect(screen, selected,
|
||||||
command_string.replace("\n", "").rstrip())
|
command_string.replace("\n", "").rstrip())
|
||||||
|
if (key_pressed == ord('k') or key_pressed == ord('K') or
|
||||||
|
key_pressed == (curses.KEY_F7)) and row_num != 0:
|
||||||
|
curses.endwin()
|
||||||
|
HoldConnection(strings[position - 1])
|
||||||
if (key_pressed == ord("\n") or key_pressed == (
|
if (key_pressed == ord("\n") or key_pressed == (
|
||||||
curses.KEY_F9)) and row_num != 0:
|
curses.KEY_F9)) and row_num != 0:
|
||||||
selected = []
|
selected = []
|
||||||
@@ -512,8 +542,9 @@ def CursesMain():
|
|||||||
else:
|
else:
|
||||||
password = ""
|
password = ""
|
||||||
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
|
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
|
||||||
strings[i - 1], " (", conf.get(strings[i - 1], "exec_string"),
|
strings[i - 1], " (", (conf.get(strings[i - 1],
|
||||||
")", password]
|
"exec_string") if conf.has_option(strings[i - 1],
|
||||||
|
"exec_string") else ""), ")", password]
|
||||||
if (i + (max_row * (page - 1)) == (position + (max_row * (page - 1)))):
|
if (i + (max_row * (page - 1)) == (position + (max_row * (page - 1)))):
|
||||||
box.addnstr(i - (max_row * (page - 1)), 2, "".join(
|
box.addnstr(i - (max_row * (page - 1)), 2, "".join(
|
||||||
exec_string), width - 6, highlight_text)
|
exec_string), width - 6, highlight_text)
|
||||||
@@ -529,7 +560,12 @@ def CursesMain():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
conf = ConfigParser.RawConfigParser()
|
try:
|
||||||
|
input = raw_input # Fix for Python 2.x
|
||||||
|
except NameError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
conf = configparser.RawConfigParser()
|
||||||
if not path.exists(conf_file):
|
if not path.exists(conf_file):
|
||||||
open(conf_file, 'w')
|
open(conf_file, 'w')
|
||||||
conf.read(conf_file)
|
conf.read(conf_file)
|
||||||
@@ -538,15 +574,15 @@ if __name__ == "__main__":
|
|||||||
CMDOptions()
|
CMDOptions()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
exit()
|
exit()
|
||||||
except ConfigParser.Error:
|
except configparser.Error:
|
||||||
print ("Error: can't parse your config file, please check it manually or make new one")
|
print("Error: can't parse your config file, please check it manually or make new one")
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
CursesMain()
|
CursesMain()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
CursesExit()
|
CursesExit()
|
||||||
except ConfigParser.NoOptionError:
|
except configparser.NoOptionError:
|
||||||
CursesExit("".join(["Error: can't parse your config file, please ",
|
CursesExit("".join(["Error: can't parse your config file, please ",
|
||||||
"check it manually or make new one"]))
|
"check it manually or make new one"]))
|
||||||
except curses.error:
|
except curses.error:
|
||||||
|
|||||||
11
sshch_bash_completion.sh
Normal file
11
sshch_bash_completion.sh
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
_sshch_complete()
|
||||||
|
{
|
||||||
|
local cur_word alias_list
|
||||||
|
cur_word="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
alias_list=`sshch -l | sed 's/,//g'`
|
||||||
|
COMPREPLY=($(compgen -W "$alias_list" -- $cur_word))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _sshch_complete sshch
|
||||||
|
|
||||||
Reference in New Issue
Block a user