sssh.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. #!/usr/bin/env bash
  2. # ########################################################################
  3. #
  4. # ______________________________________
  5. # __________________________/\_\________
  6. # __________________________\_\_\_______
  7. # _____/',__\__/',__\__/',__\\_\____`\__
  8. # ____/\__,_`\/\__,_`\/\__,_`\\_\_\_\_\_
  9. # ____\/\____/\/\____/\/\____/_\_\_\_\_\
  10. # _____\/___/__\/___/__\/___/___\/_/\/_/
  11. # ______________________________________
  12. #
  13. # Super SSH Script
  14. #
  15. # Version 2.0.4 [04/2022]
  16. #
  17. # ########################################################################
  18. ## some infos for developer
  19. ##
  20. ## There are 3 types of arrays:
  21. ##
  22. ## - array_projects, contain the projectnames
  23. ## - array_${project_name}, dynamic generates from file name
  24. ## - array_${host_name}, dynamic generates from project-configuration
  25. ##
  26. #### config ####
  27. ## if true every host get "ping" one time
  28. PINGHOST="true" # gueltige Werte [true,false]
  29. DEBUG="false"
  30. ## print full fqdn in project menu [default: false]
  31. ## fqdn is always used to connect even is deactivated!!
  32. FQDN="false"
  33. ## print just first group in project menu [default: true]
  34. FIRST_GROUP_ONLY="true"
  35. #### do not change ####
  36. ## used to kill from subshell
  37. MAINPID=$$
  38. ## locationOfScript
  39. locationOfScript=$(dirname "$(readlink -e "$0")")
  40. ## declare project-array
  41. declare -a array_projects
  42. #=== FUNCTION ================================================================
  43. # NAME: get_projects
  44. # DESCRIPTION: read project-files (*.lst) to arrays
  45. # PARAMETERS:
  46. # RETURNS:
  47. #===============================================================================
  48. function get_projects() {
  49. ## empty array
  50. array_projects=()
  51. array_projects+=('defaults')
  52. while read file
  53. do
  54. PROJECTNAME=`basename "${file}" .lst`
  55. ## Allow projectfiles with [0-9][0-9]_projectname oder [0-9][0-9]projectname for sorting
  56. [[ ${PROJECTNAME} =~ ^[0-9]* ]] && PROJECTNAME=`echo ${PROJECTNAME} | sed -E 's/^[0-9]*(_|)//'`
  57. ## write projectname to project array
  58. array_projects+=("${PROJECTNAME}")
  59. declare -ga ${PROJECTNAME}
  60. eval "${PROJECTNAME}+=('defaults')"
  61. ## parse project file
  62. while read VARNAME VALUE
  63. do
  64. if [ "${VARNAME}" == "HOST" ]
  65. then
  66. ## reset vars
  67. unset HOSTNAME_VAR
  68. ## define readable varname
  69. HOSTNAME="${VALUE}"
  70. ## save orig hostname
  71. HOSTNAME_VAR="${HOSTNAME}"
  72. ## add x to ip addresses because declare doesn't like numbers
  73. ## 10.10.10.10 => x10_10_10_10
  74. [[ ${HOSTNAME::1} == [0-9] ]] && { HOSTNAME_PRINT=${HOSTNAME}; HOSTNAME=x${HOSTNAME}; } || HOSTNAME_PRINT=`echo ${HOSTNAME} | cut -d'.' -f1` ;
  75. ## cleanup varname for array
  76. ARRAY_HOSTNAME="`echo ${HOSTNAME} | sed 's/\./_/g'| sed 's/-/_/g'`"
  77. ## Array by hostname
  78. declare -Ag "${ARRAY_HOSTNAME}"
  79. ## write default variables to host array
  80. eval "${ARRAY_HOSTNAME}+=(['fqdn']=\"${HOSTNAME_VAR}\")"
  81. eval "${ARRAY_HOSTNAME}+=(['name']=\"${HOSTNAME_PRINT}\")"
  82. eval "${PROJECTNAME}+=(\"${ARRAY_HOSTNAME}\")"
  83. continue
  84. else
  85. ## add configuration to host-array
  86. ## varnames getting lower case (https://stackoverflow.com/questions/2264428/how-to-convert-a-string-to-lower-case-in-bash/2265268#2265268)
  87. ## eval "nas+=([port]="22")"
  88. eval "${ARRAY_HOSTNAME}+=([\"${VARNAME,,}\"]=\"${VALUE}\")"
  89. fi
  90. done <<<$( egrep -v "(^\s*$|^#)" "${file}" )
  91. done <<<$( find ${locationOfScript} ~/sssh -iname "*.lst" | sort )
  92. }
  93. #=== FUNCTION ================================================================
  94. # NAME: get_groups
  95. # DESCRIPTION: find all groups of given project
  96. # PARAMETERS: project_name
  97. # RETURNS:
  98. #===============================================================================
  99. function get_groups() {
  100. ## reset groups/groupscount (neccessary for project reload)
  101. groups=""
  102. groups_count=0
  103. counter=0
  104. ## be carfully: $GROUPS is an BASH internal variable
  105. declare -n array_project="$project"
  106. declare -Ag array_groups
  107. # x y z sind mit sonderfunktionen belegt!
  108. arr_character=( a b c d e f g h i j k l m n o p q r s t u v w )
  109. for host in "${array_project[@]}"; do
  110. [ "${host}" == "defaults" ] && continue
  111. declare -n vars="${host}"
  112. if [ -n "${vars['group']}" ];
  113. then
  114. for group_name in ${vars['group']}; do
  115. if [[ ! " ${array_groups[@]} " =~ " ${group_name} " ]]
  116. then
  117. eval array_groups+=([\"${arr_character[${groups_count}]}\"]=\"${group_name}\")
  118. groups_count=$[groups_count+1]
  119. fi
  120. done
  121. fi
  122. done
  123. ## print groups
  124. ## loop allowed characters
  125. for i in "${arr_character[@]}"; do
  126. ## if character is key of array_groups
  127. if [[ "${!array_groups[@]}" =~ "${i}" ]]; then
  128. printf "[\033[1;33m%s\033[0m]\033[1;32m%s\033[0m " "${i}" "${array_groups[$i]}"
  129. fi
  130. done
  131. }
  132. #=== FUNCTION ================================================================
  133. # NAME: get_projectname
  134. # DESCRIPTION: detect the project name
  135. # PARAMETERS: project nr or name
  136. # RETURNS:
  137. #===============================================================================
  138. function get_projectname() {
  139. declare -g project
  140. ## check if given projectname or projectnumber
  141. if [[ $1 =~ ^[0-9]+$ ]] ; then
  142. ## projectnumber, read projectname from "projects" string
  143. project="${array_projects["$1"]}"
  144. else
  145. ## projectname ...
  146. project=$1
  147. fi
  148. }
  149. #=== FUNCTION ================================================================
  150. # NAME: print_mainmenu
  151. # DESCRIPTION: print out main menu of all projects
  152. # PARAMETERS:
  153. # RETURNS:
  154. #===============================================================================
  155. function print_mainmenu() {
  156. declare -g INPUT_PROJECT
  157. while ! check_input "${INPUT_PROJECT}"; do
  158. if ! ${DEBUG}; then clear; fi
  159. echo " == Super SSH Sprungmenu =="
  160. echo
  161. for i in "${!array_projects[@]}"; do
  162. [ $i -ne 0 ] && printf "%6s %s\n" "$i" "${array_projects[$i]}"
  163. done
  164. echo
  165. echo " x Abbruch"
  166. echo
  167. echo " ===================="
  168. echo -ne "Auswahl: "
  169. read INPUT_PROJECT
  170. done
  171. }
  172. #=== FUNCTION ================================================================
  173. # NAME: print_projectmenu
  174. # DESCRIPTION: print out projectmenu for given project
  175. # PARAMETERS: project,group
  176. # RETURNS:
  177. #===============================================================================
  178. function print_projectmenu() {
  179. ## reset variables
  180. GROUP_FILTER=
  181. LINENUMBER=
  182. if ! ${DEBUG}; then clear; fi
  183. echo -e " == SSH Sprungmenu ==\n\n Projekt: $project\n"
  184. ## just read out all groups
  185. get_groups ${project}
  186. ## group filter given
  187. if [ -n "$2" ]; then
  188. for i in "${!arr_character[@]}"; do
  189. if [ "${arr_character[$i]}" == "$2" ]; then
  190. GROUP_FILTER=${array_groups["$2"]}
  191. fi
  192. done
  193. fi
  194. # Print header
  195. if [ "$PINGHOST" == "true" ]; then
  196. printf "\n\n\033[1;33m%3s %11s %-25s %-12s %-13s %s\033[0m\n" "ID" "<USER> @" "Host:<port>" "[Status]" "Gruppe" "Beschreibung"
  197. else
  198. echo -e "\033[1;33mID USER\t Host\tGruppe\t\t\tBeschreibung \033[0m "
  199. fi
  200. declare -n array_project="$project"
  201. for host in "${array_project[@]}"; do
  202. unset SSHPORT_PRINT GROUP_PRINT
  203. [ "${host}" == "defaults" ] && continue
  204. declare -n vars="$host"
  205. LINENUMBER=$[LINENUMBER+1]
  206. SSHHOST="${vars['name']}"
  207. DESCRIBTION="${vars['desc']}"
  208. USERNAME="${vars['user']}"
  209. SSHPORT="${vars['port']}"
  210. ALIAS="${vars['alias']}"
  211. GROUP="${vars['group']} "
  212. if ( [ -n "${GROUP_FILTER}" ] && [[ ! "${GROUP}" =~ "${GROUP_FILTER} " ]]); then continue; fi
  213. ## print just first group
  214. [ ${FIRST_GROUP_ONLY} ] && GROUP_PRINT=`echo "${GROUP}" | cut -d' ' -f1` || GROUP_PRINT=${GROUP}
  215. ## print full fqdn if defined and not deactivated by conf
  216. if ( ${FQDN} && [ ${vars['fqdn']} ] ); then SSHHOST=${vars['fqdn']}; fi
  217. ## FQDN allways used to ping host
  218. if ( [ ${vars['fqdn']} ] ); then SSHPINGHOST="${vars['fqdn']}"; fi
  219. ## check some variables
  220. [[ -z ${USERNAME} ]] && USERNAME="" || USERNAME="${USERNAME} @"
  221. [[ -z ${SSHPORT} ]] && SSHPORT="22"
  222. [[ "${SSHPORT}" != "22" ]] && SSHPORT_PRINT=":${SSHPORT}"
  223. [[ "${PINGHOST}" == "true" ]] && ping_host ${SSHPINGHOST} ${SSHPORT} || STATUS="\t"
  224. [[ ! -z ${ALIAS} ]] && SSHHOST="${ALIAS}"
  225. printf "%2s %12s %-25s ${STATUSCOLOR}%-12s\033[0m %-13s %s\n" "${LINENUMBER}" "${USERNAME}" "${SSHHOST}${SSHPORT_PRINT}" "${STATUS}" "${GROUP_PRINT}" "${DESCRIBTION}"
  226. done
  227. # Ausgabe der Standarteintraege
  228. echo -en "\n\n x Abbruch\n z Zurueck ins Basemenu\n ============================\nAuswahl: "
  229. }
  230. #=== FUNCTION ================================================================
  231. # NAME: ping_host
  232. # DESCRIPTION: check if given ssh port is open
  233. # PARAMETERS: host, portnumber
  234. # RETURNS:
  235. #===============================================================================
  236. function ping_host() {
  237. ## returncode doesn't work in subshell ...
  238. PING=$(netcat -w 1 -z $1 $2 >> /dev/null 2>&1 ; echo $? )
  239. if [ "$PING" -eq 0 ]; then
  240. STATUS="[UP]"
  241. STATUSCOLOR="\033[1;32m"
  242. else
  243. STATUS="[DOWN]"
  244. STATUSCOLOR="\033[1;31m"
  245. fi
  246. }
  247. #=== FUNCTION ================================================================
  248. # NAME: check_input
  249. # DESCRIPTION: check if given input is correct and digit is in list range
  250. # PARAMETERS: PROJECT, HOST (optional)
  251. # RETURNS: true/false
  252. #===============================================================================
  253. function check_input() {
  254. ## if host is given project must known!
  255. [ -z $2 ] && SEARCHSTRING=$1 || SEARCHSTRING=$2
  256. case "${SEARCHSTRING}" in
  257. dummy)
  258. return 1
  259. ;;
  260. [[:digit:]]*)
  261. ## projectnumber is given
  262. ## 0-9
  263. if [ -z "$2" ]; then
  264. [[ "${!array_projects[@]}" =~ "${1}" ]] && return 0 || return 1
  265. else
  266. ## array_hosts => dynamic array "{projectname}"
  267. declare -n array_hosts="${array_projects[${1}]}"
  268. ## check if given digit is an host (index number of array)
  269. [[ "${!array_hosts[@]}" =~ "${1}" ]] && return 0 || return 1
  270. fi
  271. ;;
  272. z)
  273. ## back to mainmenu
  274. bash $0
  275. ;;
  276. x)
  277. ## exit
  278. echo "Du weißt auch nicht was du willst ..."
  279. kill $MAINPID
  280. ;;
  281. [[:alpha:]])
  282. ## group filter
  283. return 1
  284. ;;
  285. [[:alpha:]]*)
  286. ## check for project-names
  287. ## a-z or A-Z
  288. if [ -z "$2" ]; then
  289. [[ "${array_projects[@]}" =~ "${1}" ]] && return 0 || return 1
  290. else
  291. ## array_hosts => dynamic array "{projectname}"
  292. declare -n array_hosts="${array_projects[${1}]}"
  293. ## check if given project name is in dynamic project array
  294. [[ "${array_hosts[@]}" =~ "${1}" ]] && return 0 || return 1
  295. fi
  296. ;;
  297. *)
  298. return 1
  299. ;;
  300. esac
  301. }
  302. #=== FUNCTION ================================================================
  303. # NAME: ssh_to_host
  304. # DESCRIPTION: connect to host
  305. # PARAMETERS: project, host
  306. # RETURNS: true/false
  307. #===============================================================================
  308. function ssh_to_host() {
  309. PROJECT=$1
  310. HOST=$2
  311. ## check if number or hostname given
  312. if [ `echo ${HOST} | grep -E ^[[:digit:]]+$` ]; then
  313. ## linenumber
  314. declare -n ARR_PROJECT="${PROJECT}"
  315. SSHHOST=${ARR_PROJECT[${HOST}]}
  316. else
  317. ## hostname
  318. SSHHOST=${HOST}
  319. fi
  320. declare -n ARRAY_SSHHOST="${SSHHOST}"
  321. USERNAME=${ARRAY_SSHHOST["user"]}
  322. SSHPORT=${ARRAY_SSHHOST["port"]}
  323. SSHFQDN=${ARRAY_SSHHOST["fqdn"]}
  324. ## if fqdn is defined we allways connect to this
  325. # [ -n ${ARRAY_SSHHOST['fqdn']} ] && SSHHOST="${ARRAY_SSHHOST['fqdn']}"
  326. # TMP_FQDN="${ARRAY_SSHHOST['fqdn']}"
  327. # [ -n ${SSHFQDN} ] && SSHHOST="${SSHFQDN}"
  328. # if ( ${ARRAY_SSHHOST['fqdn']} && [ -n ${ARRAY_SSHHOST['fqdn']} ] ); then SSHHOST="${ARRAY_SSHHOST['fqdn']}"; fi
  329. ## add ssh-parameter if optional params defined
  330. [[ -n ${USERNAME} ]] && USERNAME="-l ${USERNAME} "
  331. [[ -n ${SSHPORT} ]] && SSHPORT="-p ${SSHPORT} "
  332. [[ -n ${SSHFQDN} ]] && SSHHOST="${SSHFQDN}"
  333. echo "Executing: ssh ${SSHPORT}${USERNAME}${SSHHOST}"
  334. ssh ${SSHPORT} ${USERNAME} ${SSHHOST}
  335. }
  336. #=== FUNCTION ================================================================
  337. # NAME: debug
  338. # DESCRIPTION: print the arrays
  339. # PARAMETERS:
  340. # RETURNS:
  341. #===============================================================================
  342. function debug() {
  343. echo -e '\n### DEBUG START ##########################\n'
  344. echo "Debug: array_projects"
  345. for project in "${array_projects[@]}"; do
  346. echo -e "\nproject: $project"
  347. declare -n hosts="${project}"
  348. for host in "${hosts[@]}"; do
  349. declare -n vars="${host}"
  350. printf " - %s\n" "server: ${host}"
  351. for i in "${!vars[@]}"; do
  352. printf " - %s: %s\n" "$i" "${vars[$i]}"
  353. done
  354. done
  355. done
  356. echo -e '\n### DEBUG END ############################\n'
  357. }
  358. #=== Main Script =============================================================
  359. #
  360. #
  361. #===============================================================================
  362. INPUT_PROJECT=$1
  363. [ -n "$2" ] && INPUT_HOST="$2" || INPUT_HOST="dummy"
  364. get_projects
  365. if ${DEBUG}; then debug; fi
  366. print_mainmenu
  367. get_projectname ${INPUT_PROJECT}
  368. ## check if group OR host given
  369. while ! check_input "${INPUT_PROJECT}" "${INPUT_HOST}" ; do
  370. print_projectmenu "${INPUT_PROJECT}" "${INPUT_HOST}"
  371. read INPUT_HOST
  372. INPUT_HOST="${INPUT_HOST:-dummy}"
  373. done
  374. ssh_to_host ${project} ${INPUT_HOST}