10 Commits

Author SHA1 Message Date
2c8f072499 Merge pull request #3 from GHPS/master
Added Python 3.x support
2017-11-20 09:57:30 +03:00
GHPS
68bb493c44 Added Python 3.x support 2017-11-19 23:34:25 +01:00
e75a733d0b README fix 2017-09-13 12:05:17 +03:00
c057e1e8ed Adding autocompetion instructions to README and fixing lack of 'exec_string' parameter crashing 2017-09-13 12:03:00 +03:00
0266d29308 Merge pull request #2 from jeff-99/master
added bash autocompletion script for sshch aliases
2017-09-12 20:48:32 +03:00
Jeffrey Slort
de2f544178 added bash autocompletion script for sshch aliases 2017-09-11 10:53:03 +02:00
083144d656 add quotes for sshpass (for & symbol in password) 2017-09-05 15:16:23 +03:00
afa565104c missprint 2017-07-25 17:39:18 +03:00
d5844ab267 add -k feature (hold connection) 2017-07-25 17:21:54 +03:00
48eea214fa Merge pull request #1 from Difrex/master
Code cleanup
2017-07-25 16:48:14 +03:00
4 changed files with 89 additions and 36 deletions

View File

@@ -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)

View File

@@ -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'],

View File

@@ -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
View 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