charts.d.plugin.in 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: GPL-3.0-or-later
  3. # netdata
  4. # real-time performance and health monitoring, done right!
  5. # (C) 2017 Costa Tsaousis <costa@tsaousis.gr>
  6. # GPL v3+
  7. #
  8. # charts.d.plugin allows easy development of BASH plugins
  9. #
  10. # if you need to run parallel charts.d processes, link this file to a different name
  11. # in the same directory, with a .plugin suffix and netdata will start both of them,
  12. # each will have a different config file and modules configuration directory.
  13. #
  14. export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
  15. PROGRAM_FILE="$0"
  16. PROGRAM_NAME="$(basename $0)"
  17. PROGRAM_NAME="${PROGRAM_NAME/.plugin/}"
  18. MODULE_NAME="main"
  19. LOG_LEVEL_ERR=1
  20. LOG_LEVEL_WARN=2
  21. LOG_LEVEL_INFO=3
  22. LOG_LEVEL="$LOG_LEVEL_INFO"
  23. set_log_severity_level() {
  24. case ${NETDATA_LOG_SEVERITY_LEVEL,,} in
  25. "info") LOG_LEVEL="$LOG_LEVEL_INFO";;
  26. "warn" | "warning") LOG_LEVEL="$LOG_LEVEL_WARN";;
  27. "err" | "error") LOG_LEVEL="$LOG_LEVEL_ERR";;
  28. esac
  29. }
  30. set_log_severity_level
  31. # -----------------------------------------------------------------------------
  32. # create temp dir
  33. debug=0
  34. TMP_DIR=
  35. chartsd_cleanup() {
  36. trap '' EXIT QUIT HUP INT TERM
  37. if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]; then
  38. [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..."
  39. rm -rf "$TMP_DIR"
  40. fi
  41. echo "EXIT"
  42. exit 0
  43. }
  44. trap chartsd_cleanup EXIT QUIT HUP INT TERM
  45. if [ $UID = "0" ]; then
  46. TMP_DIR="$(mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX)"
  47. else
  48. TMP_DIR="$(mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX)"
  49. fi
  50. logdate() {
  51. date "+%Y-%m-%d %H:%M:%S"
  52. }
  53. log() {
  54. local status="${1}"
  55. shift
  56. echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: ${*}"
  57. }
  58. info() {
  59. [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_INFO" -gt "$LOG_LEVEL" ]] && return
  60. log INFO "${@}"
  61. }
  62. warning() {
  63. [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_WARN" -gt "$LOG_LEVEL" ]] && return
  64. log WARNING "${@}"
  65. }
  66. error() {
  67. [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_ERR" -gt "$LOG_LEVEL" ]] && return
  68. log ERROR "${@}"
  69. }
  70. fatal() {
  71. log FATAL "${@}"
  72. echo "DISABLE"
  73. exit 1
  74. }
  75. debug() {
  76. [ $debug -eq 1 ] && log DEBUG "${@}"
  77. }
  78. # -----------------------------------------------------------------------------
  79. # check a few commands
  80. require_cmd() {
  81. local x=$(which "${1}" 2>/dev/null || command -v "${1}" 2>/dev/null)
  82. if [ -z "${x}" -o ! -x "${x}" ]; then
  83. warning "command '${1}' is not found in ${PATH}."
  84. eval "${1^^}_CMD=\"\""
  85. return 1
  86. fi
  87. eval "${1^^}_CMD=\"${x}\""
  88. return 0
  89. }
  90. require_cmd date || exit 1
  91. require_cmd sed || exit 1
  92. require_cmd basename || exit 1
  93. require_cmd dirname || exit 1
  94. require_cmd cat || exit 1
  95. require_cmd grep || exit 1
  96. require_cmd egrep || exit 1
  97. require_cmd mktemp || exit 1
  98. require_cmd awk || exit 1
  99. require_cmd timeout || exit 1
  100. require_cmd curl || exit 1
  101. # -----------------------------------------------------------------------------
  102. [ $((BASH_VERSINFO[0])) -lt 4 ] && fatal "BASH version 4 or later is required, but found version: ${BASH_VERSION}. Please upgrade."
  103. info "started from '$PROGRAM_FILE' with options: $*"
  104. # -----------------------------------------------------------------------------
  105. # internal defaults
  106. # netdata exposes a few environment variables for us
  107. [ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")"
  108. [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
  109. [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
  110. pluginsd="${NETDATA_PLUGINS_DIR}"
  111. stockconfd="${NETDATA_STOCK_CONFIG_DIR}/${PROGRAM_NAME}"
  112. userconfd="${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}"
  113. olduserconfd="${NETDATA_USER_CONFIG_DIR}"
  114. chartsd="$pluginsd/../charts.d"
  115. minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}"
  116. update_every=${minimum_update_frequency} # this will be overwritten by the command line
  117. # work around for non BASH shells
  118. charts_create="_create"
  119. charts_update="_update"
  120. charts_check="_check"
  121. charts_underscore="_"
  122. # when making iterations, charts.d can loop more frequently
  123. # to prevent plugins missing iterations.
  124. # this is a percentage relative to update_every to align its
  125. # iterations.
  126. # The minimum is 10%, the maximum 100%.
  127. # So, if update_every is 1 second and time_divisor is 50,
  128. # charts.d will iterate every 500ms.
  129. # Charts will be called to collect data only if the time
  130. # passed since the last time the collected data is equal or
  131. # above their update_every.
  132. time_divisor=50
  133. # number of seconds to run without restart
  134. # after this time, charts.d.plugin will exit
  135. # netdata will restart it
  136. restart_timeout=$((3600 * 4))
  137. # check if the charts.d plugins are using global variables
  138. # they should not.
  139. # It does not currently support BASH v4 arrays, so it is
  140. # disabled
  141. dryrunner=0
  142. # check for timeout command
  143. check_for_timeout=1
  144. # the default enable/disable value for all charts
  145. enable_all_charts="yes"
  146. # -----------------------------------------------------------------------------
  147. # parse parameters
  148. check=0
  149. chart_only=
  150. while [ ! -z "$1" ]; do
  151. if [ "$1" = "check" ]; then
  152. check=1
  153. shift
  154. continue
  155. fi
  156. if [ "$1" = "debug" -o "$1" = "all" ]; then
  157. debug=1
  158. shift
  159. continue
  160. fi
  161. if [ -f "$chartsd/$1.chart.sh" ]; then
  162. debug=1
  163. chart_only="$(echo $1.chart.sh | sed "s/\.chart\.sh$//g")"
  164. shift
  165. continue
  166. fi
  167. if [ -f "$chartsd/$1" ]; then
  168. debug=1
  169. chart_only="$(echo $1 | sed "s/\.chart\.sh$//g")"
  170. shift
  171. continue
  172. fi
  173. # number check
  174. n="$1"
  175. x=$((n))
  176. if [ "$x" = "$n" ]; then
  177. shift
  178. update_every=$x
  179. [ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency
  180. continue
  181. fi
  182. fatal "Cannot understand parameter $1. Aborting."
  183. done
  184. # -----------------------------------------------------------------------------
  185. # loop control
  186. # default sleep function
  187. LOOPSLEEPMS_HIGHRES=0
  188. now_ms=
  189. current_time_ms_default() {
  190. now_ms="$(date +'%s')000"
  191. }
  192. current_time_ms="current_time_ms_default"
  193. current_time_ms_accuracy=1
  194. mysleep="sleep"
  195. # if found and included, this file overwrites loopsleepms()
  196. # and current_time_ms() with a high resolution timer function
  197. # for precise looping.
  198. source "$pluginsd/loopsleepms.sh.inc"
  199. [ $? -ne 0 ] && error "Failed to load '$pluginsd/loopsleepms.sh.inc'."
  200. # -----------------------------------------------------------------------------
  201. # load my configuration
  202. for myconfig in "${NETDATA_STOCK_CONFIG_DIR}/${PROGRAM_NAME}.conf" "${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf"; do
  203. if [ -f "$myconfig" ]; then
  204. source "$myconfig"
  205. if [ $? -ne 0 ]; then
  206. error "Config file '$myconfig' loaded with errors."
  207. else
  208. info "Configuration file '$myconfig' loaded."
  209. fi
  210. else
  211. warning "Configuration file '$myconfig' not found."
  212. fi
  213. done
  214. # make sure time_divisor is right
  215. time_divisor=$((time_divisor))
  216. [ $time_divisor -lt 10 ] && time_divisor=10
  217. [ $time_divisor -gt 100 ] && time_divisor=100
  218. # we check for the timeout command, after we load our
  219. # configuration, so that the user may overwrite the
  220. # timeout command we use, providing a function that
  221. # can emulate the timeout command we need:
  222. # > timeout SECONDS command ...
  223. if [ $check_for_timeout -eq 1 ]; then
  224. require_cmd timeout || exit 1
  225. fi
  226. # -----------------------------------------------------------------------------
  227. # internal checks
  228. # netdata passes the requested update frequency as the first argument
  229. update_every=$((update_every + 1 - 1)) # makes sure it is a number
  230. test $update_every -eq 0 && update_every=1 # if it is zero, make it 1
  231. # check the charts.d directory
  232. [ ! -d "$chartsd" ] && fatal "cannot find charts directory '$chartsd'"
  233. # -----------------------------------------------------------------------------
  234. # library functions
  235. fixid() {
  236. echo "$*" |
  237. tr -c "[A-Z][a-z][0-9]" "_" |
  238. sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" |
  239. tr "[A-Z]" "[a-z]"
  240. }
  241. isvarset() {
  242. [ -n "$1" ] && [ "$1" != "unknown" ] && [ "$1" != "none" ]
  243. return $?
  244. }
  245. getosid() {
  246. if isvarset "${NETDATA_CONTAINER_OS_ID}"; then
  247. echo "${NETDATA_CONTAINER_OS_ID}"
  248. else
  249. echo "${NETDATA_SYSTEM_OS_ID}"
  250. fi
  251. }
  252. run() {
  253. local ret pid="${BASHPID}" t
  254. if [ "z${1}" = "z-t" -a "${2}" != "0" ]; then
  255. t="${2}"
  256. shift 2
  257. timeout "${t}" "${@}" 2>"${TMP_DIR}/run.${pid}"
  258. ret=$?
  259. else
  260. "${@}" 2>"${TMP_DIR}/run.${pid}"
  261. ret=$?
  262. fi
  263. if [ ${ret} -ne 0 ]; then
  264. {
  265. printf "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: command '"
  266. printf "%q " "${@}"
  267. printf "' failed with code ${ret}:\n --- BEGIN TRACE ---\n"
  268. cat "${TMP_DIR}/run.${pid}"
  269. printf " --- END TRACE ---\n"
  270. } >&2
  271. fi
  272. rm -f "${TMP_DIR}/run.${pid}"
  273. return ${ret}
  274. }
  275. # convert any floating point number
  276. # to integer, give a multiplier
  277. # the result is stored in ${FLOAT2INT_RESULT}
  278. # so that no fork is necessary
  279. # the multiplier must be a power of 10
  280. float2int() {
  281. local f m="$2" a b l v=($1)
  282. f=${v[0]}
  283. # the length of the multiplier - 1
  284. l=$((${#m} - 1))
  285. # check if the number is in scientific notation
  286. if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]; then
  287. # convert it to decimal
  288. # unfortunately, this fork cannot be avoided
  289. # if you know of a way to avoid it, please let me know
  290. f=$(printf "%0.${l}f" ${f})
  291. fi
  292. # split the floating point number
  293. # in integer (a) and decimal (b)
  294. a=${f/.*/}
  295. b=${f/*./}
  296. # if the integer part is missing
  297. # set it to zero
  298. [ -z "${a}" ] && a="0"
  299. # strip leading zeros from the integer part
  300. # base 10 conversion
  301. a=$((10#$a))
  302. # check the length of the decimal part
  303. # against the length of the multiplier
  304. if [ ${#b} -gt ${l} ]; then
  305. # too many digits - take the most significant
  306. b=${b:0:l}
  307. elif [ ${#b} -lt ${l} ]; then
  308. # too few digits - pad with zero on the right
  309. local z="00000000000000000000000" r=$((l - ${#b}))
  310. b="${b}${z:0:r}"
  311. fi
  312. # strip leading zeros from the decimal part
  313. # base 10 conversion
  314. b=$((10#$b))
  315. # store the result
  316. FLOAT2INT_RESULT=$(((a * m) + b))
  317. }
  318. # -----------------------------------------------------------------------------
  319. # charts check functions
  320. all_charts() {
  321. cd "$chartsd"
  322. [ $? -ne 0 ] && error "cannot cd to $chartsd" && return 1
  323. ls *.chart.sh | sed "s/\.chart\.sh$//g"
  324. }
  325. declare -A charts_enable_keyword=(
  326. ['apache']="force"
  327. ['cpu_apps']="force"
  328. ['cpufreq']="force"
  329. ['example']="force"
  330. ['exim']="force"
  331. ['hddtemp']="force"
  332. ['load_average']="force"
  333. ['mem_apps']="force"
  334. ['mysql']="force"
  335. ['nginx']="force"
  336. ['phpfpm']="force"
  337. ['postfix']="force"
  338. ['sensors']="force"
  339. ['squid']="force"
  340. ['tomcat']="force"
  341. )
  342. declare -A obsolete_charts=(
  343. ['apache']="python.d.plugin module"
  344. ['cpu_apps']="apps.plugin"
  345. ['cpufreq']="proc plugin"
  346. ['exim']="python.d.plugin module"
  347. ['hddtemp']="python.d.plugin module"
  348. ['load_average']="proc plugin"
  349. ['mem_apps']="proc plugin"
  350. ['mysql']="python.d.plugin module"
  351. ['nginx']="python.d.plugin module"
  352. ['phpfpm']="python.d.plugin module"
  353. ['postfix']="python.d.plugin module"
  354. ['squid']="python.d.plugin module"
  355. ['tomcat']="python.d.plugin module"
  356. )
  357. all_enabled_charts() {
  358. local charts enabled required
  359. # find all enabled charts
  360. for chart in $(all_charts); do
  361. MODULE_NAME="${chart}"
  362. if [ -n "${obsolete_charts["$MODULE_NAME"]}" ]; then
  363. debug "is replaced by ${obsolete_charts["$MODULE_NAME"]}, skipping it."
  364. continue
  365. fi
  366. eval "enabled=\$$chart"
  367. if [ -z "${enabled}" ]; then
  368. enabled="${enable_all_charts}"
  369. fi
  370. required="${charts_enable_keyword[${chart}]}"
  371. [ -z "${required}" ] && required="yes"
  372. if [ ! "${enabled}" = "${required}" ]; then
  373. info "is disabled. Add a line with $chart=$required in '${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf' to enable it (or remove the line that disables it)."
  374. else
  375. debug "is enabled for auto-detection."
  376. local charts="$charts $chart"
  377. fi
  378. done
  379. MODULE_NAME="main"
  380. local charts2=
  381. for chart in $charts; do
  382. MODULE_NAME="${chart}"
  383. # check the enabled charts
  384. local check="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()")"
  385. if [ -z "$check" ]; then
  386. error "module '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
  387. continue
  388. fi
  389. local create="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()")"
  390. if [ -z "$create" ]; then
  391. error "module '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
  392. continue
  393. fi
  394. local update="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()")"
  395. if [ -z "$update" ]; then
  396. error "module '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
  397. continue
  398. fi
  399. # check its config
  400. #if [ -f "$userconfd/$chart.conf" ]
  401. #then
  402. # if [ ! -z "$( cat "$userconfd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_underscore" )" ]
  403. # then
  404. # error "module's $chart config $userconfd/$chart.conf should only have lines starting with $chart$charts_underscore . Disabling it."
  405. # continue
  406. # fi
  407. #fi
  408. #if [ $dryrunner -eq 1 ]
  409. # then
  410. # "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$userconfd/$chart.conf" >/dev/null
  411. # if [ $? -ne 0 ]
  412. # then
  413. # error "module's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
  414. # continue
  415. # fi
  416. #fi
  417. local charts2="$charts2 $chart"
  418. done
  419. MODULE_NAME="main"
  420. echo $charts2
  421. debug "enabled charts: $charts2"
  422. }
  423. # -----------------------------------------------------------------------------
  424. # load the charts
  425. suffix_retries="_retries"
  426. suffix_update_every="_update_every"
  427. active_charts=
  428. for chart in $(all_enabled_charts); do
  429. MODULE_NAME="${chart}"
  430. debug "loading module: '$chartsd/$chart.chart.sh'"
  431. source "$chartsd/$chart.chart.sh"
  432. [ $? -ne 0 ] && warning "Module '$chartsd/$chart.chart.sh' loaded with errors."
  433. # first load the stock config
  434. if [ -f "$stockconfd/$chart.conf" ]; then
  435. debug "loading module configuration: '$stockconfd/$chart.conf'"
  436. source "$stockconfd/$chart.conf"
  437. [ $? -ne 0 ] && warning "Config file '$stockconfd/$chart.conf' loaded with errors."
  438. else
  439. debug "not found module configuration: '$stockconfd/$chart.conf'"
  440. fi
  441. # then load the user config (it overwrites the stock)
  442. if [ -f "$userconfd/$chart.conf" ]; then
  443. debug "loading module configuration: '$userconfd/$chart.conf'"
  444. source "$userconfd/$chart.conf"
  445. [ $? -ne 0 ] && warning "Config file '$userconfd/$chart.conf' loaded with errors."
  446. else
  447. debug "not found module configuration: '$userconfd/$chart.conf'"
  448. if [ -f "$olduserconfd/$chart.conf" ]; then
  449. # support for very old netdata that had the charts.d module configs in /etc/netdata
  450. info "loading module configuration from obsolete location: '$olduserconfd/$chart.conf'"
  451. source "$olduserconfd/$chart.conf"
  452. [ $? -ne 0 ] && warning "Config file '$olduserconfd/$chart.conf' loaded with errors."
  453. fi
  454. fi
  455. eval "dt=\$$chart$suffix_update_every"
  456. dt=$((dt + 1 - 1)) # make sure it is a number
  457. if [ $dt -lt $update_every ]; then
  458. eval "$chart$suffix_update_every=$update_every"
  459. fi
  460. $chart$charts_check
  461. if [ $? -eq 0 ]; then
  462. debug "module '$chart' activated"
  463. active_charts="$active_charts $chart"
  464. else
  465. error "module's '$chart' check() function reports failure."
  466. fi
  467. done
  468. MODULE_NAME="main"
  469. debug "activated modules: $active_charts"
  470. # -----------------------------------------------------------------------------
  471. # check overwrites
  472. # enable work time reporting
  473. debug_time=
  474. test $debug -eq 1 && debug_time=tellwork
  475. # if we only need a specific chart, remove all the others
  476. if [ ! -z "${chart_only}" ]; then
  477. debug "requested to run only for: '${chart_only}'"
  478. check_charts=
  479. for chart in $active_charts; do
  480. if [ "$chart" = "$chart_only" ]; then
  481. check_charts="$chart"
  482. break
  483. fi
  484. done
  485. active_charts="$check_charts"
  486. fi
  487. debug "activated charts: $active_charts"
  488. # stop if we just need a pre-check
  489. if [ $check -eq 1 ]; then
  490. info "CHECK RESULT"
  491. info "Will run the charts: $active_charts"
  492. exit 0
  493. fi
  494. # -----------------------------------------------------------------------------
  495. cd "${TMP_DIR}" || exit 1
  496. # -----------------------------------------------------------------------------
  497. # create charts
  498. run_charts=
  499. for chart in $active_charts; do
  500. MODULE_NAME="${chart}"
  501. debug "calling '$chart$charts_create()'..."
  502. $chart$charts_create
  503. if [ $? -eq 0 ]; then
  504. run_charts="$run_charts $chart"
  505. debug "'$chart' initialized."
  506. else
  507. error "module's '$chart' function '$chart$charts_create()' reports failure."
  508. fi
  509. done
  510. MODULE_NAME="main"
  511. debug "run_charts='$run_charts'"
  512. # -----------------------------------------------------------------------------
  513. # update dimensions
  514. [ -z "$run_charts" ] && fatal "No charts to collect data from."
  515. keepalive() {
  516. if [ ! -t 1 ] && ! printf "\n"; then
  517. chartsd_cleanup
  518. fi
  519. }
  520. declare -A charts_last_update=() charts_update_every=() charts_retries=() charts_next_update=() charts_run_counter=() charts_serial_failures=()
  521. global_update() {
  522. local exit_at \
  523. c=0 dt ret last_ms exec_start_ms exec_end_ms \
  524. chart now_charts=() next_charts=($run_charts) \
  525. next_ms x seconds millis
  526. # return the current time in ms in $now_ms
  527. ${current_time_ms}
  528. exit_at=$((now_ms + (restart_timeout * 1000)))
  529. for chart in $run_charts; do
  530. eval "charts_update_every[$chart]=\$$chart$suffix_update_every"
  531. test -z "${charts_update_every[$chart]}" && charts_update_every[$chart]=$update_every
  532. eval "charts_retries[$chart]=\$$chart$suffix_retries"
  533. test -z "${charts_retries[$chart]}" && charts_retries[$chart]=10
  534. charts_last_update[$chart]=$((now_ms - (now_ms % (charts_update_every[$chart] * 1000))))
  535. charts_next_update[$chart]=$((charts_last_update[$chart] + (charts_update_every[$chart] * 1000)))
  536. charts_run_counter[$chart]=0
  537. charts_serial_failures[$chart]=0
  538. echo "CHART netdata.plugin_chartsd_$chart '' 'Execution time for $chart plugin' 'milliseconds / run' charts.d netdata.plugin_charts area 145000 ${charts_update_every[$chart]} '' '' '$chart'"
  539. echo "DIMENSION run_time 'run time' absolute 1 1"
  540. done
  541. # the main loop
  542. while [ "${#next_charts[@]}" -gt 0 ]; do
  543. keepalive
  544. c=$((c + 1))
  545. now_charts=("${next_charts[@]}")
  546. next_charts=()
  547. # return the current time in ms in $now_ms
  548. ${current_time_ms}
  549. for chart in "${now_charts[@]}"; do
  550. MODULE_NAME="${chart}"
  551. if [ ${now_ms} -ge ${charts_next_update[$chart]} ]; then
  552. last_ms=${charts_last_update[$chart]}
  553. dt=$((now_ms - last_ms))
  554. charts_last_update[$chart]=${now_ms}
  555. while [ ${charts_next_update[$chart]} -lt ${now_ms} ]; do
  556. charts_next_update[$chart]=$((charts_next_update[$chart] + (charts_update_every[$chart] * 1000)))
  557. done
  558. # the first call should not give a duration
  559. # so that netdata calibrates to current time
  560. dt=$((dt * 1000))
  561. charts_run_counter[$chart]=$((charts_run_counter[$chart] + 1))
  562. if [ ${charts_run_counter[$chart]} -eq 1 ]; then
  563. dt=
  564. fi
  565. exec_start_ms=$now_ms
  566. $chart$charts_update $dt
  567. ret=$?
  568. # return the current time in ms in $now_ms
  569. ${current_time_ms}
  570. exec_end_ms=$now_ms
  571. echo "BEGIN netdata.plugin_chartsd_$chart $dt"
  572. echo "SET run_time = $((exec_end_ms - exec_start_ms))"
  573. echo "END"
  574. if [ $ret -eq 0 ]; then
  575. charts_serial_failures[$chart]=0
  576. next_charts+=($chart)
  577. else
  578. charts_serial_failures[$chart]=$((charts_serial_failures[$chart] + 1))
  579. if [ ${charts_serial_failures[$chart]} -gt ${charts_retries[$chart]} ]; then
  580. error "module's '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
  581. else
  582. error "module's '$chart' update() function reports failure. Will keep trying for a while."
  583. next_charts+=($chart)
  584. fi
  585. fi
  586. else
  587. next_charts+=($chart)
  588. fi
  589. done
  590. MODULE_NAME="${chart}"
  591. # wait the time you are required to
  592. next_ms=$((now_ms + (update_every * 1000 * 100)))
  593. for x in "${charts_next_update[@]}"; do [ ${x} -lt ${next_ms} ] && next_ms=${x}; done
  594. next_ms=$((next_ms - now_ms))
  595. if [ ${LOOPSLEEPMS_HIGHRES} -eq 1 -a ${next_ms} -gt 0 ]; then
  596. next_ms=$((next_ms + current_time_ms_accuracy))
  597. seconds=$((next_ms / 1000))
  598. millis=$((next_ms % 1000))
  599. if [ ${millis} -lt 10 ]; then
  600. millis="00${millis}"
  601. elif [ ${millis} -lt 100 ]; then
  602. millis="0${millis}"
  603. fi
  604. debug "sleeping for ${seconds}.${millis} seconds."
  605. ${mysleep} ${seconds}.${millis}
  606. else
  607. debug "sleeping for ${update_every} seconds."
  608. ${mysleep} $update_every
  609. fi
  610. test ${now_ms} -ge ${exit_at} && exit 0
  611. done
  612. fatal "nothing left to do, exiting..."
  613. }
  614. global_update