distribute.sh 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/bin/sh
  2. readonly DEFAULT_OUT_FMT="%s.out"
  3. readonly CMD_FEED="$(mktemp -u /tmp/distributer-XXX.fifo)"
  4. readonly PROC_BUFFER=10
  5. readonly MAX_PROCS=$(expr $(ulimit -u) / 3 - $PROC_BUFFER)
  6. readonly MAX_CMD_SIZE=350
  7. readonly READER="$(dirname $(realpath $0))/fixed-read"
  8. readonly TIMEOUT=600 # 10 minutes
  9. readonly CONF_LIST="$1"
  10. readonly SERVER_LIST="$2"
  11. readonly OUT_FMT="${3:-$DEFAULT_OUT_FMT}"
  12. readonly MK_DIR_CMD="if [ ! -d \"$(dirname "$OUT_FMT")\" ]; then
  13. mkdir -p \"$(dirname "$OUT_FMT")\" > /dev/null
  14. fi"
  15. readonly S_SPACE="s/[[:space:].\/]/_/g"
  16. readonly R_DASH="s/-//g"
  17. readonly S_QUOTE="s/'/\\\\'/g"
  18. help() {
  19. printf "Usage: $(basename $0) cmd_list server_list [out_file_fmt]\n" >&2
  20. printf " cmd_list: A text file containing a list of commands to run.\n" >&2
  21. printf " server_list: A text file containing a list of servers to\n" >&2
  22. printf " connect to and run commands on.\n" >&2
  23. printf " out_file_fmt: File name format to write the output of each\n" >&2
  24. printf " command to (default: %s).\n\n" "$DEFAULT_OUT_FMT" >&2
  25. printf "All commands will be allocated to the first available server.\n" >&2
  26. printf "Each command must be valid on every server.\n" >&2
  27. printf "The output will be saved to a text file on the remote systems.\n" >&2
  28. exit 1
  29. }
  30. clean_server() {
  31. cmd_feed="$1"
  32. rm "$cmd_feed"
  33. exit 2
  34. }
  35. run_server() {
  36. server="$1"
  37. loop="$(mktemp -u /tmp/$(basename $CMD_FEED .fifo)-$server-XXX.fifo)"
  38. out_file=$(printf "$OUT_FMT" "$(basename "$loop" .fifo)")
  39. mkfifo "$loop"
  40. trap "clean_server $loop" 2 15
  41. {
  42. printf "echo > %s\necho\n" "$out_file"
  43. # Block until command completes
  44. while read -t $TIMEOUT line < "$loop" > /dev/null && \
  45. cmd=$("$READER" $MAX_CMD_SIZE)
  46. do
  47. cmd=$(printf "%s" "$cmd" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
  48. #cmd_sanitized="$(echo $cmd | sed "$S_SPACE;$R_DASH;$S_QUOTE")"
  49. #out_file="$(printf "$OUT_FMT" $cmd_sanitized)"
  50. printf "$server: %s\n" "$cmd" >&2
  51. #printf "%s > %s\necho\n" "$cmd" "$out_file"
  52. printf "printf \"%s: \" >> %s\n" "$cmd" "$out_file"
  53. printf "%s >> %s\n" "$cmd" "$out_file"
  54. printf "echo\n"
  55. done
  56. } | ssh -oBatchMode=yes -oStrictHostKeyChecking=no "$server" "sh" > "$loop"
  57. clean_server "$loop"
  58. echo "Server '$server' finished!" >&2
  59. }
  60. clean_up() {
  61. for pid in $(pgrep -P $$); do
  62. pkill -P $pid
  63. done
  64. rm "$CMD_FEED"
  65. exit 2
  66. }
  67. main() {
  68. mkfifo "$CMD_FEED"
  69. trap clean_up 2 15
  70. cat "$CONF_LIST" | sed '/^[[:space:]]*$/d' | \
  71. xargs -d'\n' printf "%-$MAX_CMD_SIZE.${MAX_CMD_SIZE}s" > "$CMD_FEED" &
  72. for server in $(head -n$MAX_PROCS "$SERVER_LIST"); do
  73. run_server "$server" < "$CMD_FEED" > /dev/null &
  74. done
  75. for pid in $(pgrep -P $$); do
  76. wait $pid
  77. done
  78. clean_up
  79. echo "All jobs finished!"
  80. }
  81. if [ "$#" -eq 2 -o "$#" -eq 3 ]; then
  82. main
  83. else
  84. help
  85. fi