shell dot on steroids https://pagure.io/shellfu

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #!/bin/bash
  2. ffoo import misc
  3. ffoo import pretty
  4. age_of_leaf() {
  5. #
  6. # Print age of each filename on STDIN
  7. #
  8. local now=$1
  9. local ctime
  10. sed 's/.*/"&"/' \
  11. | xargs stat -c %Z \
  12. | while read ctime;
  13. do echo $(($now - $ctime));
  14. done
  15. }
  16. age_of_tree() {
  17. #
  18. # Age of the youngest leaf in the tree
  19. #
  20. local now=$(date +%s)
  21. find -L "$1" \
  22. | age_of_leaf $now \
  23. | sort -n \
  24. | head -1
  25. }
  26. cat_uri() {
  27. #
  28. # Cat for URIs
  29. #
  30. wget -q $1 -O - || echo "ERROR: $?" >&2
  31. }
  32. has_files() {
  33. #
  34. # Directory exists and has at least one file
  35. #
  36. test -d "$1" || return 1
  37. test -e "$1"/* &>/dev/null
  38. case $? in
  39. 0) return 0 ;;
  40. 1) return 1 ;;
  41. *) return 0 ;;
  42. esac
  43. }
  44. pids_matching() {
  45. #
  46. # Print list of PIDs where command matches regex(es)
  47. #
  48. # Note that *comm* field of *ps* is used, which may be
  49. # truncated, so don't rely on name ending
  50. #
  51. local all_pids=""
  52. local pids=""
  53. local pfx=""
  54. local pattern
  55. for pattern in "$@";
  56. do
  57. local pids=$(
  58. ps -e -o pid= -o comm= \
  59. | perl -ne "
  60. chomp;
  61. s/^ +//;
  62. my (\$pid, \$comm) = split m/ +/;
  63. next unless \$comm =~ m/$pattern/;
  64. print qq/\$pid /;
  65. "
  66. )
  67. debug -v pattern pids
  68. all_pids="$all_pids$pfx$pids"
  69. pfx=" "
  70. done
  71. debug -v all_pids
  72. test -n "$all_pids" && echo $all_pids
  73. }
  74. listening_on() {
  75. #
  76. # Check if something is listening on local TCP port $1
  77. #
  78. local host="localhost"
  79. local port="$1"
  80. debug -v host port
  81. test -n "$port" || usage_is "PORT"
  82. netstat -ntlp \
  83. | sed -e 's/ */ /g' \
  84. | cut -d\ -f 4 \
  85. | grep -qs :$port\$
  86. }
  87. reachable_by_ping() {
  88. #
  89. # Check if host $1 is reachable by ICMP ping
  90. #
  91. local host=$1
  92. debug -v host
  93. test -n "$host" || usage_is "HOST"
  94. ping -w 1 $host >& /dev/null
  95. }
  96. reachable_by_ssh() {
  97. #
  98. # Check if host $1 is reachable by SSH
  99. #
  100. local host=$1
  101. debug -v host
  102. test -n "$host" || usage_is "HOST"
  103. ssh -n -o ConnectTimeout=3 $host -- true >/dev/null 2>&1;
  104. }
  105. reachable_by_tcp() {
  106. #
  107. # Check if TCP port $1 on host $2 is reachable (open)
  108. #
  109. local port=$1
  110. local host=$2
  111. debug -v port host
  112. test -n "$host" -a -n "$port" || usage_is "PORT HOST"
  113. nc -w 1 $host $port >/dev/null 2>&1;
  114. }
  115. filter_hosts() {
  116. #
  117. # Filter hosts on STDIN, leaving those reachable
  118. #
  119. # Default check is by ICMP only, but you can use arguments
  120. # to enable/disable checks:
  121. #
  122. # -t|--tcp PORT enable TCP check on PORT
  123. # -p|--ping enable ICMP ping (default)
  124. # -P|--no-ping disable ICMP ping
  125. # -s|--ssh enable SSH (check by calling `true`)
  126. # -S|--no-ssh disable SSH
  127. #
  128. local ping_fun="reachable_by_ping"
  129. local ssh_fun=true
  130. local tcp_fun=true
  131. # note: here the ^ true builtin acts as placeholder (i.e. no check)
  132. local tcp_port=0
  133. while true; do case $1 in
  134. -t|--tcp) tcp_fun="reachable_by_tcp $2"; shift 2 ;;
  135. -p|--ping) ping_fun="reachable_by_ping"; shift ;;
  136. -P|--no-ping) ping_fun=true; shift ;;
  137. -s|--ssh) ssh_fun="reachable_by_ssh"; shift ;;
  138. -S|--no-ssh) ssh_fun=true; shift ;;
  139. "") break ;;
  140. *) usage_is "[-p|P] [-s|-S] [-t port]"; return 2 ;;
  141. esac done
  142. filter $ping_fun | filter $tcp_fun | filter $ssh_fun
  143. }
  144. sort_paths_by_age() {
  145. #
  146. # Read paths on STDIN and sort them by age of youngest leaf
  147. #
  148. while read path;
  149. do echo "$(age_of_tree $path) $path"
  150. done \
  151. | sort -n \
  152. | cut -d\ -f 2-
  153. }
  154. wait_until() {
  155. #
  156. # Wait until command succeeds (or fails)
  157. #
  158. # Repeat calling a command until it succeeds or exits with
  159. # acceptable status. For example:
  160. #
  161. # wait_until -t 90 my_command
  162. # wait_until -t 90 -d 30 my_expensive_command
  163. # wait_until -x 7 command_that_should_exit_with_7
  164. # wait_until -X "-lt 7" command_that_should_exit_with_less_than_7
  165. # wait_until -e some_bash_builtin
  166. #
  167. # Note that polling happens synchronously so if your
  168. # command blocks for more than specified delay, freequency
  169. # and timeout cannot be guaranteed. If you need to assure
  170. # more predictable behavior, limit your command with
  171. # timeout(1)
  172. #
  173. local timeout=10
  174. local delay=1
  175. local use_eval=false
  176. local es_test="-eq 0"
  177. while true; do case "$1" in
  178. -x) es_test="-eq $2"; shift; ;;
  179. -X) es_test="$2"; shift; ;;
  180. -e) use_eval=true; shift; break ;;
  181. -d) delay="$2"; shift 2; ;;
  182. -t) timeout="$2"; shift 2; ;;
  183. --) shift 1; break; ;;
  184. *) break; ;;
  185. esac done
  186. think "waiting for: $@"
  187. local elapsed=0
  188. local start=$(date +%s)
  189. while true;
  190. do
  191. sleep $delay
  192. local now=$(date +%s)
  193. local elapsed=$(($now - $start))
  194. local es=0
  195. debug "\$@='$@'"
  196. if $use_eval;
  197. then
  198. eval $@;
  199. es=$?
  200. else
  201. $@;
  202. es=$?
  203. fi
  204. debug -v es
  205. if [ $es $es_test ];
  206. then
  207. return 0;
  208. fi
  209. if test $elapsed -gt $timeout;
  210. then
  211. warn "timeout reached ($elapsed>$timeout) when waiting for ($es $es_test): $@"
  212. return 1
  213. fi
  214. think "waiting for another $delay s"
  215. done
  216. }