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