#! /bin/bash # Thor - Linux Admin tools/loader # Copyright 2014-2017 Rackspace US, Inc. # NOT FOR PUBLIC USE # 20140818, 20150804, 20150925, 20170220, 20171116 Chris Novoa - 3linc_dev@rackspace.com #USAGE: # . <(curl -Ls thor.sh) [script] [args] # bash <(curl -Ls thor.sh) [script] [args] #wget -qO- ip.thor.sh #curl -ks ip.thor.sh #GET ip.thor.sh #bash <(curl -Ls https://rax.io/serverinf) serverinf #bash <(curl -s rax.mirror.rackspace.com/3linc/) serverinf export THOR_VERSION=1.0.19-20170220 export THOR_BASEDOM="rax.mirror.rackspace.com/3linc" #"rax.mirror.rackspace.com/3linc" # export THOR_BASEURL=$THOR_BASEDOM #/thor export THOR_COMMAND="bash <(curl -s $THOR_BASEDOM)" export THOR_SCRIPTS=$THOR_BASEURL/thor export THOR_TMP="$(mktemp -p /tmp -d THOR-${USER}.XXXXXXX)" export THOR_REQ_CMD=$1 export COLUMNS=$(tput cols) THOR_DEBUG=false #ST_REQ=$1; shift; ST_ARGS=$@; ############################################################################################ # UTILS ############################################################################################ ST_ofst() { OFST=$(history | tail -1 | awk '{print $1}') && history -d $OFST; } thorinit () { #Initialize, create tmp in memory [ "THOR_DEBUG" = true ] && echo -e "-= THOR INITIALIZED =-"; if [ -e /dev/shm ] ; then rm -rf $THOR_TMP THOR_TMP="$(mktemp -p /dev/shm -d THOR-${USER}.XXXXXXX)" export WARNINGS="$THOR_TMP/warnings.txt" export NOTICES="$THOR_TMP/notices.txt" fi echo -e "$(date)\nTHOR v.$THOR_VERSION\n$THOR_TMP" > $THOR_TMP/thor.log } thorcleanup () { [ -n "$THOR_TMP" ] && rm -rf $THOR_TMP #ST_ofst } trap 'thorcleanup' 0 1 2 3 15 gzrun () { BINURL=$1 shift umask=$(umask) umask 77 GZTMPDIR="$THOR_TMP" GZTMP="$GZTMPDIR/$(basename $BINURL)" wget -q -O $GZTMP $BINURL if gunzip "$GZTMP"; then GZTMP="${GZTMP%.*}" umask $umask chmod 700 $GZTMP ( (sleep 5; rm -rf "$GZTMPDIR") 2>/dev/null & ) $GZTMP ${1+"$@"}; res=$? else echo >&2 "Cannot decompress $BINURL" (return 127); res=127 fi } ############################################################################################ # UTILS ############################################################################################ OKS='' hr () { echo -ne "\n ┈╼";eval printf %.0s═ '{1..'$((COLUMNS-8))'}'; echo -ne "━╾┈\n"; } hrhead () { [ -n "$1" ] && lndr="╒" || lndr=" ┈╼═"; echo -ne "\n$lndr";eval printf %.0s═ '{1..'$((COLUMNS-6))'}'; echo -ne "━╾┈"; [ -n "$1" ] && echo -e "\n│ $1\n╘══════════════━╾┈";echo -e ""; } ansi () { #local B='0;';local U='0;'; l local C=0 local B='';local U=''; local BK=''; local I=''; local D=''; local C=39; local N=''; #pipe (0)/stdin (88) OR function call (concatenated) [ "$(stat -Lc %t /dev/fd/0)" == 0 ] && local STR="$(cat)" || local STR="${!#}" while ((${#: -1})); do case "$1" in tb|1|bb|bld|bold) B='1;' ;; td|2|dd|dim) D='2;' ;; ti|3|ii|ita|italic) I='3;' ;; tu|4|uu|und|underline) U='4;' ;; blk|30) C=30 ;; # Black - Regular r|31|red) C=31 ;; g|32|grn) C=32 ;; y|33|ylw) C=33 ;; b|34|blu) C=34 ;; p|35|pur) C=35 ;; c|36|cyn) C=36 ;; w|37|wht) C=37 ;; tx256|38) [ ! -z "${2##*[!0-9]*}" ] && shift; C="38;5;$1" ;; #if opt after t256 is a number, get the next opt for txt color def|39) C=39 ;; dg|90|darkgray) C=90 ;; lr|91|lightred) C=91 ;; lg|92|lightgrn) C=92 ;; ly|93|lightylw) C=93 ;; lb|94|lightblu) C=94 ;; lp|95|lightpur) C=95 ;; lc|96|lightcyn) C=96 ;; lw|97|lightwht) C=97 ;; bkblk|40) BK='40;' ;; bkred|41) BK='41;' ;; bkgrn|42) BK='42;' ;; bkylw|43) BK='43;' ;; bkblu|44) BK='44;' ;; bkpur|45) BK='45;' ;; bkcyn|46) BK='46;' ;; bkwht|47) BK='47;' ;; #light gray background bk256|48) [ ! -z "${2##*[!0-9]*}" ] && shift; C="48;5;$1" ;; #if opt after t256 is a number, get the next opt for bk color bklblk|100) BK='100;' ;; #dark gray background bklred|101) BK='101;' ;; bklgrn|102) BK='102;' ;; bklylw|103) BK='103;' ;; bklblu|104) BK='104;' ;; bklpur|105) BK='105;' ;; bklcyn|106) BK='106;' ;; bklwht|107) BK='107;' ;; n) N="-n" ;; 0|r|reset) C=0; B='21;';BK='0;';U='24;';I='0;';D='22;' ;; help|h) "This is a colorize function." ;; #*) esac shift done echo -e $N "\033[${B}${U}${D}${I}${BK}${C}m${STR}\033[00m" } bbcode () { #TODO copy the same logic from the ansi function #if [ ! -z "$BB" ] ; then BBOLD0="[b]" && BBOLD1="[/b]" BITLC0="[i]" && BITLC1="[/i]" BUNDR0="[u]" && BUNDR1="[/u]" BSTRK0="[s]" && BSTRK1="[/s]" BBURL0="[url]" && BBURL1="[/url]" BCOLRED0="[color=#993333]" && BCOLRED1="[/color]" BCOLGRN0="[color=#669900]" && BCOLGRN1="[/color]" BCOLBLU0="[color=#006699]" && BCOLBLU1="[/color]" BCOLBRN0="[color=#996633]" && BCOLBRN1="[/color]" #fi } c () { ansi "$@"; } print () { ansi "$@"; } h1 () { print tb c "$BBOLD0===== $1 ======================$BBOLD1"; } h2 () { print tb c "$BBOLD0=== $1 ======================$BBOLD1"; } h3 () { print tb c "$BBOLD0== $1 ======================$BBOLD1"; } HWARN () { [ "$1" = "q" ] && quiet=1 && shift CURWARN="$(print r bb "$BCOLRED0[WARNING] $1$BCOLRED1")"; [ -n $quiet ] && (echo -e "$CURWARN" >> "$WARNINGS" && quiet='') || echo -e "$CURWARN" | tee -a "$WARNINGS"; } HNOTE () { [ "$1" = "q" ] && quiet=1 && shift CURNOTE="$(print y bb "$BCOLBRN0[NOTICE]$BCOLBRN1 $1")"; [ -n $quiet ] && (echo -e "$CURNOTE" >> "$NOTICES" && quiet='') || echo -e "$CURNOTE" | tee -a "$NOTICES"; } HOK () { CUROK="$(print g "$BCOLGRN0[OK]$BCOLGRN1 $1")"; OKS="$OKS\n$CUROK"; echo -ne "$CUROK"; } URL () { print b "${BBURL0}$1${BBURL1}"; } spinner() { #Usage Example: command -to run & spinner $! local pid=$1; local delay=0.1; local i=1; case $2 in 0) sp='=-' ;; 3) sp='▖▘▝▗' ;; 4) sp='◴◷◶◵' ;; 6) sp='⠁⠂⠄⡀⢀⠠⠐⠈' ;; 10) sp='|/-\' ;; *) sp='-+-x' ;; esac tput sc while ps o pid | grep -q $1; do printf "\b${sp:i++%${#sp}:1}"; sleep $delay; tput rc; tput el done tput rc; tput ed } timeout () { #usage: timeout 10 "Self-destructing in %s" tput sc time=$1; while [ $time -ge 0 ]; do tput rc; tput el printf "$2" $time ((time--)) sleep 1 done tput rc; tput ed; } piduptime () { ps -eo "%p %t" | grep -E "$(tr ' ' '|' <<< $1)" | awk '{print $NF}' | sed 's/-/d /' | sort -r | head -1 } TODO () { #this is just a placeholder for use in empty functions since bash hates that echo "TODO: $1" } ############################################################################################ # INFORMATION ABOUT ENVIRONMENT isroot () { [ $(id -u) = 0 ] && return 0 || return 1 #[ $EUID = 0 ] && return 0 || return 1 } ischroot () { [ "$(awk '$5=="/" {print $1}' /dev/null)" RUSERSTR="$(sed -nr 's/.*rack\[([0-9]+)\].*racker\s([A-Za-z0-9]+)\s.*(\/dev[\/a-z0-9]+)/\1|\2|\3/p' <<< "$LOGSTR" | tail -1)" RUSER=$(cut -d'|' -f2 <<< "$RUSERSTR") RTTY=$(cut -d'|' -f3 <<< "$RUSERSTR") RPID=$(ps ax | grep "$TTY.*[b]ash" | awk 'END{print $1; exit}') if [ "$RUSERSTR" == "" ]; then echo -e "'rack'\t\t[$RPID] /dev/$TTY"; else echo -e "$RUSER\t[$RPID] $RTTY"; fi done | column -t fi } wai () { echo -e "You are $(whoami) in $(pwd)\n\n" } ############################################################################################ # GATHER SERVER VITALS #OPERATING SYSTEM info_os () { #currently only works for fedora and deban based linuxes, TODO: arch, gentoo if (which rpm && [ -f /etc/redhat-release ]) &> /dev/null ; then #Fedora upstream family - releaseinf=$(rpm -q --queryformat '%{name}\n%{version}\n%{release}' --whatprovides /etc/redhat-release) releasepkg=$(sed -n '1{p;q}' <<< "$releaseinf") releasever=$(sed -n '2{p;q}' <<< "$releaseinf") releaserel=$(sed -n '3{p;q}' <<< "$releaseinf") export THOR_DISTRO_FAMILY="fedora" #if 2nd line ($releasever) contains "server" (i.e. 5Server or 6Server), use the 3rd line for DISTRO_VERS #otherwise concat the 2nd and 3rd line for DISTRO_VERS if grep -iq "server" <<< "$releasever"; then export THOR_DISTRO_VERS="$releaserel" else export THOR_DISTRO_VERS="$releasever.$releaserel" fi if grep -iq "sles" <<< "$releasepkg"; then #When dealing with SLES, there is CentOS SLES (managed CentOS) and Red Hat SLES (RHEL on SUSE patching) #RHEL SLES has only redhat-release, CENTOS SLES has redhat-release and centos-release #This may change in the future. # o./display/Linux/CentOS # o./pages/viewpage.action?pageId=127109574 # o./display/enterprisesupport/SUSE+Manager+Patching+for+RHEL+and+CentOS#SUSEManagerPatchingforRHELandCentOS-CurrentlyunsupportedversionsofRHELorCentOSonSUSE [ -e /etc/centos-release ] && export THOR_DISTRO="CentOS" || export THOR_DISTRO="Red Hat" export THOR_DISTRO_RELEASE="SLES Managed" elif grep -iqE "(xenserver)" <<< "$releasepkg"; then [ -e /etc/centos-release ] && export THOR_DISTRO="CentOS" || export THOR_DISTRO="Red Hat" export THOR_DISTRO_RELEASE="Citrix XenServer" elif grep -iqE "(centos|redhat|fedora)" <<< "$releasepkg"; then case $(grep -ioE "(centos|redhat|fedora)" <<< "$releasepkg") in centos) export THOR_DISTRO="CentOS" #Check for Spacewalk rhn plugin if grep -qi spacewalk /usr/share/yum-plugins/rhnplugin.py 2> /dev/null; then export THOR_DISTRO_RELEASE="(Spacewalk)" else export THOR_DISTRO_RELEASE="(Community)" fi ;; redhat) export THOR_DISTRO="Red Hat" export THOR_DISTRO_RELEASE="Enterprise Linux" export THOR_DISTRO_SHORTNAME="RHEL" ;; fedora) export THOR_DISTRO="Fedora" export THOR_DISTRO_RELEASE="(Core)" ;; esac fi elif (which dpkg && which apt-get) &> /dev/null ; then #Debian upstream family releaseinf="$(lsb_release -a 2>/dev/null | sed -nr 's/Description:\s+([A-Za-z \/]+)\s+([0-9.]+)\s+([A-Za-z]*)/\1\n\2\n\3/p')" export THOR_DISTRO_FAMILY="debian" export THOR_DISTRO_FAMILY_RELEASE="$(cat /etc/debian_version 2> /dev/null)" export THOR_DISTRO=$(sed -n '1{p;q}' <<< "$releaseinf") export THOR_DISTRO_VERS=$(sed -n '2{p;q}' <<< "$releaseinf") export THOR_DISTRO_RELEASE=$(sed -n '3{p;q}' <<< "$releaseinf") fi export THOR_DISTRO_VERS_MAJOR=$(cut -d. -f1 <<< $THOR_DISTRO_VERS) export THOR_DISTRO_VERS_MINOR=$(cut -d. -f2 <<< $THOR_DISTRO_VERS) [ -n "$THOR_DISTRO" ] && export THOR_DISTRO_FULLNAME="$THOR_DISTRO" [ -z "$THOR_DISTRO_SHORTNAME" ] && export THOR_DISTRO_SHORTNAME="$THOR_DISTRO" [ -n "$THOR_DISTRO_RELEASE" ] && export THOR_DISTRO_FULLNAME="$THOR_DISTRO_FULLNAME $THOR_DISTRO_RELEASE" [ -n "$THOR_DISTRO_VERS_MAJOR" ] && export THOR_DISTRO_FULLNAME="$THOR_DISTRO_FULLNAME $THOR_DISTRO_VERS_MAJOR" [ -n "$THOR_DISTRO_VERS_MINOR" ] && export THOR_DISTRO_FULLNAME="$THOR_DISTRO_FULLNAME.$THOR_DISTRO_VERS_MINOR" # || export THOR_DISTRO_FULLNAME=" $THOR_DISTRO_FULLNAME " #export THOR_DISTRO_FULLNAME="$THOR_DISTRO $THOR_DISTRO_RELEASE $THOR_DISTRO_VERS_MAJOR.$THOR_DISTRO_VERS_MINOR" [ -n "$THOR_DISTRO_VERS_MAJOR" ] && export THOR_DISTRO_SHORTNAME="$THOR_DISTRO_SHORTNAME$THOR_DISTRO_VERS_MAJOR" [ -n "$THOR_DISTRO_VERS_MINOR" ] && export THOR_DISTRO_SHORTNAME="$THOR_DISTRO_SHORTNAME.$THOR_DISTRO_VERS_MINOR" #env | grep THOR } info_rhcluster () { hash clustat 2>/dev/null && clustat -fl } #KIR Details (RS dedicated servers) info_rskir () { export THOR_CORE_DC=$(cat /root/.rackspace/datacenter 2> /dev/null) export THOR_CORE_ID=$(cat /root/.rackspace/server_number 2> /dev/null) export THOR_CORE_AN=$(cat /root/.rackspace/customer_number 2> /dev/null) export THOR_CORE_KD=$(cat /root/.rackspace/kick_date 2> /dev/null) } #SYSTEM ARCH/PROC INFO/RAM/ info_hardware () { TODO $FUNCNAME } #NETWORK -> IPs/HOSTNAME/ info_ip () { #works on most rhel/debian systems IPADDRS="$(/sbin/ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1}')" #RHEL 7, bsd, darwin [ -z "$IPADDRS" ] && IPADDRS="$(/sbin/ifconfig | grep -E 'inet.[0-9]' | grep -v '127.0.0.1' | awk '{ print $2}')" #Sun Solaris [ -z "$IPADDRS" ] && IPADDRS="$(/sbin/ifconfig -a | grep inet | grep -v '127.0.0.1' | awk '{ print $2}')" echo -e "$IPADDRS" } info_nsplnt () { NSPLNT="$(netstat -plnt)" } info_net () { ips="$(info_ip)" ipseco="$(tail -1 <<< "$ips")" export THOR_NET_PRIMIP_INT="$(head -1 <<< "$ips")" export THOR_NET_PRIMIP_EXT="$(curl -s racker.io | sed 's/\s//g')" [ "$ipseco" == "$THOR_NET_PRIMIP_INT" ] && export THOR_NET_SECIP_INT="" || export THOR_NET_SECIP_INT="$ipseco" #export THOR_NET_SECIP_INT="$([ "$ipseco" == "$THOR_NET_PRIMIP_INT" ] && echo "" || echo "$ipseco" )" export THOR_NET_HOSTNAME="$(hostname)" } info_sysinstalldate () { #get the system install date passwd -S root|tail -1|awk '{print $3}' } ############################################################################################ # THOR FEATURES ############################################################################################ #scriptmanifest () { # #} thorhalp () { #OPTIONS=$(whiptail --title 'THOR Utility Selection' --menu 'Choose your weapon...' 15 60 7 '1' 'Serverinf' '2' 'Plesk Triage' '3' 'Sophos Tool' '4' 'Shellshock Check' '5' 'Heartbleed Check' '10' 'Help' 'q' 'quit' 3>&1 1>&2 2>&3 ) echo "Welcome to Bash $BASH_VERSION" } thorerror() { echo -e "\n\nTHOR SCRIPT LOADER SAYS:\n $@\n\n" logger -t "rackspace_support" -p error "[$(whoami)][rax.mirror.rackspace.com/3linc/$cmdscript] Rackspace Linux admin tools error: $@" return 1 } thorlog() { #logger -t "rackspace_support" -p info "[$SUDO_USER:$USER:$(ps hotty $$):thor.sh:$THOR_REQ_CMD] $@" return 0 } ############################################################################################ # MAIN ############################################################################################ preload () { info_net info_os info_rskir } thorcli () { cmdargs="$@" cmdargct="$#" #echo $cmdargct if [ $cmdargct -gt 0 ]; then cmdscript="$1" && shift cmdargs="$@" REQUEST="$THOR_SCRIPTS/$cmdscript" REQUESTMD5="$THOR_SCRIPTS/$cmdscript.md5" RESPONSE="$THOR_TMP/$cmdscript.response" HEADERS="$THOR_TMP/$cmdscript.headers" STATUS="$THOR_TMP/$cmdscript.status" OUTPUT="$THOR_TMP/$cmdscript.output" #echo -e "SCRIPT: $cmdscript\nARGS: $cmdargs\nREMOTE: $REQUEST\n\n" # http://stackoverflow.com/questions/18258804/how-to-catch-timeout-errors-in-a-curl-shell-script httpstat=$(curl -Lks -w %{http_code} -D $HEADERS -o $RESPONSE "$REQUEST") case $httpstat in 200) if [ -f $RESPONSE ];then thorlog "Rackspace Linux admin tools started. $THOR_COMMAND -> $0" preload ( (source $RESPONSE) ) thorlog "Rackspace Linux admin tools completed. $REQUEST -> $RESPONSE(DEL)" else ( thorerror "ERROR LOADING: $cmdscript\n File to source not found after download ($RESPONSE)." ) fi if [ "$THOR_DEBUG" = true ]; then #( (bash <(curl -kSs $THOR_SCRIPTS/$cmdscript) $cmdargs ) ) echo -e "Dumped: $REQUEST\nInto: $RESPONSE" fi ;; *) ( thorerror "ERROR LOADING: $cmdscript\n cURL returned: HTTP/1.1 $httpstat" ) ;; esac fi } thor () { if [ "$0" = "$BASH_SOURCE" ]; then v=subshell else v=sourced fi thorcli "$@" #ST_ofst } thorinit thor "$@" if [ "$THOR_DEBUG" = true ]; then echo "$THOR_TMP" echo "process $$ is $v ($0, $BASH_SOURCE)" fi #ST_ofst