nc-exporting.sh 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: GPL-3.0-or-later
  3. # This is a simple exporting proxy, written in BASH, using the nc command.
  4. # Run the script without any parameters for help.
  5. MODE="${1}"
  6. MY_PORT="${2}"
  7. EXPORTING_HOST="${3}"
  8. EXPORTING_PORT="${4}"
  9. FILE="${NETDATA_NC_EXPORTING_DIR-/tmp}/netdata-nc-exporting-${MY_PORT}"
  10. log() {
  11. logger --stderr --id=$$ --tag "netdata-nc-exporting" "${*}"
  12. }
  13. mync() {
  14. local ret
  15. log "Running: nc ${*}"
  16. nc "${@}"
  17. ret=$?
  18. log "nc stopped with return code ${ret}."
  19. return ${ret}
  20. }
  21. listen_save_replay_forever() {
  22. local file="${1}" port="${2}" real_exporting_host="${3}" real_exporting_port="${4}" ret delay=1 started ended
  23. while true
  24. do
  25. log "Starting nc to listen on port ${port} and save metrics to ${file}"
  26. started=$(date +%s)
  27. mync -l -p "${port}" | tee -a -p --output-error=exit "${file}"
  28. ended=$(date +%s)
  29. if [ -s "${file}" ]
  30. then
  31. if [ -n "${real_exporting_host}" ] && [ -n "${real_exporting_port}" ]
  32. then
  33. log "Attempting to send the metrics to the real external database at ${real_exporting_host}:${real_exporting_port}"
  34. mync "${real_exporting_host}" "${real_exporting_port}" <"${file}"
  35. ret=$?
  36. if [ ${ret} -eq 0 ]
  37. then
  38. log "Successfully sent the metrics to ${real_exporting_host}:${real_exporting_port}"
  39. mv "${file}" "${file}.old"
  40. touch "${file}"
  41. else
  42. log "Failed to send the metrics to ${real_exporting_host}:${real_exporting_port} (nc returned ${ret}) - appending more data to ${file}"
  43. fi
  44. else
  45. log "No external database configured - appending more data to ${file}"
  46. fi
  47. fi
  48. # prevent a CPU hungry infinite loop
  49. # if nc cannot listen to port
  50. if [ $((ended - started)) -lt 5 ]
  51. then
  52. log "nc has been stopped too fast."
  53. delay=30
  54. else
  55. delay=1
  56. fi
  57. log "Waiting ${delay} seconds before listening again for data."
  58. sleep ${delay}
  59. done
  60. }
  61. if [ "${MODE}" = "start" ]
  62. then
  63. # start the listener, in exclusive mode
  64. # only one can use the same file/port at a time
  65. {
  66. flock -n 9
  67. # shellcheck disable=SC2181
  68. if [ $? -ne 0 ]
  69. then
  70. log "Cannot get exclusive lock on file ${FILE}.lock - Am I running multiple times?"
  71. exit 2
  72. fi
  73. # save our PID to the lock file
  74. echo "$$" >"${FILE}.lock"
  75. listen_save_replay_forever "${FILE}" "${MY_PORT}" "${EXPORTING_HOST}" "${EXPORTING_PORT}"
  76. ret=$?
  77. log "listener exited."
  78. exit ${ret}
  79. } 9>>"${FILE}.lock"
  80. # we can only get here if ${FILE}.lock cannot be created
  81. log "Cannot create file ${FILE}."
  82. exit 3
  83. elif [ "${MODE}" = "stop" ]
  84. then
  85. {
  86. flock -n 9
  87. # shellcheck disable=SC2181
  88. if [ $? -ne 0 ]
  89. then
  90. pid=$(<"${FILE}".lock)
  91. log "Killing process ${pid}..."
  92. kill -TERM "-${pid}"
  93. exit 0
  94. fi
  95. log "File ${FILE}.lock has been locked by me but it shouldn't. Is a collector running?"
  96. exit 4
  97. } 9<"${FILE}.lock"
  98. log "File ${FILE}.lock does not exist. Is a collector running?"
  99. exit 5
  100. else
  101. cat <<EOF
  102. Usage:
  103. "${0}" start|stop PORT [EXPORTING_HOST EXPORTING_PORT]
  104. PORT The port this script will listen
  105. (configure netdata to use this as an external database)
  106. EXPORTING_HOST The real host for the external database
  107. EXPORTING_PORT The real port for the external database
  108. This script can act as fallback database for netdata.
  109. It will receive metrics from netdata, save them to
  110. ${FILE}
  111. and once netdata reconnects to the real external database,
  112. this script will push all metrics collected to the real
  113. external database too and wait for a failure to happen again.
  114. Only one netdata can connect to this script at a time.
  115. If you need fallback for multiple netdata, run this script
  116. multiple times with different ports.
  117. You can run me in the background with this:
  118. screen -d -m "${0}" start PORT [EXPORTING_HOST EXPORTING_PORT]
  119. EOF
  120. exit 1
  121. fi