master
sh 724 lines 29.9 KB
Raw
1 #!/usr/bin/env sh
2
3 # -------------------------------------------------------------------------------------------------
4 # detect the kernel
5
6 KERNEL_NAME="$(uname -s)"
7 KERNEL_VERSION="$(uname -r)"
8 ARCHITECTURE="$(uname -m)"
9
10 # -------------------------------------------------------------------------------------------------
11 # detect the virtualization and possibly the container technology
12
13 # systemd-detect-virt: https://github.com/systemd/systemd/blob/df423851fcc05cf02281d11aab6aee7b476c1c3b/src/basic/virt.c#L999
14 # lscpu: https://github.com/util-linux/util-linux/blob/581b77da7aa4a5205902857184d555bed367e3e0/sys-utils/lscpu.c#L52
15 virtualization_normalize_name() {
16 vname="$1"
17 case "$vname" in
18 "User-mode Linux") vname="uml" ;;
19 "Windows Subsystem for Linux") vname="wsl" ;;
20 esac
21
22 echo "$vname" | tr '[:upper:]' '[:lower:]' | sed 's/ /-/g'
23 }
24
25 CONTAINER="unknown"
26 CONT_DETECTION="none"
27 CONTAINER_IS_OFFICIAL_IMAGE="${NETDATA_OFFICIAL_IMAGE:-false}"
28
29 if [ -z "${VIRTUALIZATION}" ]; then
30 VIRTUALIZATION="unknown"
31 VIRT_DETECTION="none"
32
33 if command -v systemd-detect-virt >/dev/null 2>&1; then
34 VIRTUALIZATION="$(systemd-detect-virt -v)"
35 VIRT_DETECTION="systemd-detect-virt"
36 CONTAINER_DETECT_TMP="$(systemd-detect-virt -c)"
37 [ -n "$CONTAINER_DETECT_TMP" ] && CONTAINER="$CONTAINER_DETECT_TMP"
38 CONT_DETECTION="systemd-detect-virt"
39 elif command -v lscpu >/dev/null 2>&1; then
40 VIRTUALIZATION=$(lscpu | grep "Hypervisor vendor:" | cut -d: -f 2 | awk '{$1=$1};1')
41 [ -n "$VIRTUALIZATION" ] && VIRT_DETECTION="lscpu"
42 [ -z "$VIRTUALIZATION" ] && lscpu | grep -q "Virtualization:" && VIRTUALIZATION="none"
43 elif command -v dmidecode >/dev/null 2>&1; then
44 VIRTUALIZATION=$(dmidecode -s system-product-name 2>/dev/null | grep "VMware\|Virtual\|KVM\|Bochs")
45 [ -n "$VIRTUALIZATION" ] && VIRT_DETECTION="dmidecode"
46 fi
47
48 if [ -z "${VIRTUALIZATION}" ] || [ "$VIRTUALIZATION" = "unknown" ]; then
49 if [ "${KERNEL_NAME}" = "FreeBSD" ]; then
50 VIRTUALIZATION=$(sysctl kern.vm_guest 2>/dev/null | cut -d: -f 2 | awk '{$1=$1};1')
51 [ -n "$VIRTUALIZATION" ] && VIRT_DETECTION="sysctl"
52 fi
53 fi
54
55 if [ -z "${VIRTUALIZATION}" ]; then
56 # Output from the command is outside of spec
57 VIRTUALIZATION="unknown"
58 VIRT_DETECTION="none"
59 elif [ "$VIRTUALIZATION" != "none" ] && [ "$VIRTUALIZATION" != "unknown" ]; then
60 VIRTUALIZATION=$(virtualization_normalize_name "$VIRTUALIZATION")
61 fi
62 else
63 # Passed from outside - probably in docker run
64 VIRT_DETECTION="provided"
65 fi
66
67 # -------------------------------------------------------------------------------------------------
68 # detect containers with heuristics
69
70 if [ "${CONTAINER}" = "unknown" ]; then
71 if [ -f /proc/1/sched ]; then
72 IFS='(, ' read -r process _ </proc/1/sched
73 if [ "${process}" = "netdata" ]; then
74 CONTAINER="container"
75 CONT_DETECTION="process"
76 fi
77 fi
78 # ubuntu and debian supply /bin/running-in-container
79 # https://www.apt-browse.org/browse/ubuntu/trusty/main/i386/upstart/1.12.1-0ubuntu4/file/bin/running-in-container
80 if /bin/running-in-container >/dev/null 2>&1; then
81 CONTAINER="container"
82 CONT_DETECTION="/bin/running-in-container"
83 fi
84
85 # lxc sets environment variable 'container'
86 #shellcheck disable=SC2154
87 if [ -n "${container}" ]; then
88 CONTAINER="lxc"
89 CONT_DETECTION="containerenv"
90 fi
91
92 # docker creates /.dockerenv
93 # http://stackoverflow.com/a/25518345
94 if [ -f "/.dockerenv" ]; then
95 CONTAINER="docker"
96 CONT_DETECTION="dockerenv"
97 fi
98
99 if [ -n "${KUBERNETES_SERVICE_HOST}" ]; then
100 CONTAINER="container"
101 CONT_DETECTION="kubernetes"
102 fi
103
104 if [ "${KERNEL_NAME}" = FreeBSD ] && command -v sysctl && sysctl security.jail.jailed 2>/dev/null | grep -q "1$"; then
105 CONTAINER="jail"
106 CONT_DETECTION="sysctl"
107 fi
108 fi
109
110 # -------------------------------------------------------------------------------------------------
111 # detect the operating system
112
113 # Initially assume all OS detection values are for a container, these are moved later if we are bare-metal
114
115 CONTAINER_OS_DETECTION="unknown"
116 CONTAINER_NAME="unknown"
117 CONTAINER_VERSION="unknown"
118 CONTAINER_VERSION_ID="unknown"
119 CONTAINER_ID="unknown"
120 CONTAINER_ID_LIKE="unknown"
121
122 if [ "${KERNEL_NAME}" = "Darwin" ]; then
123 CONTAINER_ID=$(sw_vers -productName)
124 CONTAINER_ID_LIKE="macOS"
125 CONTAINER_NAME="macOS"
126 CONTAINER_VERSION=$(sw_vers -productVersion)
127 CONTAINER_OS_DETECTION="sw_vers"
128 elif [ "${KERNEL_NAME}" = "FreeBSD" ]; then
129 CONTAINER_ID="FreeBSD"
130 CONTAINER_ID_LIKE="FreeBSD"
131 CONTAINER_NAME="FreeBSD"
132 CONTAINER_OS_DETECTION="uname"
133 CONTAINER_VERSION=$(uname -r)
134 KERNEL_VERSION=$(uname -K)
135 else
136 if [ -f "/etc/os-release" ]; then
137 eval "$(grep -E "^(NAME|ID|ID_LIKE|VERSION|VERSION_ID)=" </etc/os-release | sed 's/^/CONTAINER_/')"
138 CONTAINER_OS_DETECTION="/etc/os-release"
139 fi
140
141 # shellcheck disable=SC2153
142 if [ "${CONTAINER_NAME}" = "unknown" ] || [ "${CONTAINER_VERSION}" = "unknown" ] || [ "${CONTAINER_ID}" = "unknown" ]; then
143 if [ -f "/etc/lsb-release" ]; then
144 if [ "${CONTAINER_OS_DETECTION}" = "unknown" ]; then
145 CONTAINER_OS_DETECTION="/etc/lsb-release"
146 else
147 CONTAINER_OS_DETECTION="Mixed"
148 fi
149 DISTRIB_ID="unknown"
150 DISTRIB_RELEASE="unknown"
151 DISTRIB_CODENAME="unknown"
152 eval "$(grep -E "^(DISTRIB_ID|DISTRIB_RELEASE|DISTRIB_CODENAME)=" </etc/lsb-release)"
153 if [ "${CONTAINER_NAME}" = "unknown" ]; then CONTAINER_NAME="${DISTRIB_ID}"; fi
154 if [ "${CONTAINER_VERSION}" = "unknown" ]; then CONTAINER_VERSION="${DISTRIB_RELEASE}"; fi
155 if [ "${CONTAINER_ID}" = "unknown" ]; then CONTAINER_ID="${DISTRIB_CODENAME}"; fi
156 fi
157 if [ -n "$(command -v lsb_release 2>/dev/null)" ]; then
158 if [ "${CONTAINER_OS_DETECTION}" = "unknown" ]; then
159 CONTAINER_OS_DETECTION="lsb_release"
160 else
161 CONTAINER_OS_DETECTION="Mixed"
162 fi
163 if [ "${CONTAINER_NAME}" = "unknown" ]; then CONTAINER_NAME="$(lsb_release -is 2>/dev/null)"; fi
164 if [ "${CONTAINER_VERSION}" = "unknown" ]; then CONTAINER_VERSION="$(lsb_release -rs 2>/dev/null)"; fi
165 if [ "${CONTAINER_ID}" = "unknown" ]; then CONTAINER_ID="$(lsb_release -cs 2>/dev/null)"; fi
166 fi
167 fi
168 fi
169
170 # If Netdata is not running in a container then use the local detection as the host
171 HOST_OS_DETECTION="unknown"
172 HOST_NAME="unknown"
173 HOST_VERSION="unknown"
174 HOST_VERSION_ID="unknown"
175 HOST_ID="unknown"
176 HOST_ID_LIKE="unknown"
177
178 # 'systemd-detect-virt' returns 'none' if there is no hardware/container virtualization.
179 if [ "${CONTAINER}" = "unknown" ] || [ "${CONTAINER}" = "none" ]; then
180 for v in NAME ID ID_LIKE VERSION VERSION_ID OS_DETECTION; do
181 eval "HOST_$v=\$CONTAINER_$v; CONTAINER_$v=none"
182 done
183 else
184 # Otherwise try and use a user-supplied bind-mount into the container to resolve the host details
185 if [ -e "/host/etc/os-release" ]; then
186 eval "$(grep -E "^(NAME|ID|ID_LIKE|VERSION|VERSION_ID)=" </host/etc/os-release | sed 's/^/HOST_/')"
187 HOST_OS_DETECTION="/host/etc/os-release"
188 fi
189 if [ "${HOST_NAME}" = "unknown" ] || [ "${HOST_VERSION}" = "unknown" ] || [ "${HOST_ID}" = "unknown" ]; then
190 if [ -f "/host/etc/lsb-release" ]; then
191 if [ "${HOST_OS_DETECTION}" = "unknown" ]; then
192 HOST_OS_DETECTION="/etc/lsb-release"
193 else
194 HOST_OS_DETECTION="Mixed"
195 fi
196 DISTRIB_ID="unknown"
197 DISTRIB_RELEASE="unknown"
198 DISTRIB_CODENAME="unknown"
199 eval "$(grep -E "^(DISTRIB_ID|DISTRIB_RELEASE|DISTRIB_CODENAME)=" </etc/lsb-release)"
200 if [ "${HOST_NAME}" = "unknown" ]; then HOST_NAME="${DISTRIB_ID}"; fi
201 if [ "${HOST_VERSION}" = "unknown" ]; then HOST_VERSION="${DISTRIB_RELEASE}"; fi
202 if [ "${HOST_ID}" = "unknown" ]; then HOST_ID="${DISTRIB_CODENAME}"; fi
203 fi
204 fi
205 fi
206
207 if [ -d "/etc/pve" ] &&
208 echo "${KERNEL_VERSION}" | grep -q -- '-pve$' &&
209 command -v pveversion >/dev/null 2>&1; then
210 HOST_NAME="Proxmox VE"
211 HOST_ID="proxmox"
212 HOST_ID_LIKE="proxmox"
213 HOST_VERSION="$(pveversion | cut -f 2 -d '/')"
214 HOST_VERSION_ID="$(echo "${HOST_VERSION}" | cut -f 1 -d '.')"
215 HOST_OS_DETECTION="pveversion"
216 fi
217
218 # -------------------------------------------------------------------------------------------------
219 # Detect information about the CPU
220
221 LCPU_COUNT="unknown"
222 CPU_MODEL="unknown"
223 CPU_VENDOR="unknown"
224 CPU_FREQ="unknown"
225 CPU_INFO_SOURCE="none"
226
227 possible_cpu_freq=""
228 nproc="$(command -v nproc)"
229 lscpu="$(command -v lscpu)"
230 lscpu_output=""
231 dmidecode="$(command -v dmidecode)"
232 dmidecode_output=""
233
234 if [ -n "${lscpu}" ] && lscpu >/dev/null 2>&1; then
235 lscpu_output="$(LC_NUMERIC=C ${lscpu} 2>/dev/null)"
236 CPU_INFO_SOURCE="lscpu"
237 LCPU_COUNT="$(echo "${lscpu_output}" | grep "^CPU(s):" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
238 CPU_VENDOR="$(echo "${lscpu_output}" | grep "^Vendor ID:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
239 CPU_MODEL="$(echo "${lscpu_output}" | grep "^Model name:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
240 if grep -q "^lxcfs /proc" /proc/self/mounts 2>/dev/null && count=$(grep -c ^processor /proc/cpuinfo 2>/dev/null); then
241 LCPU_COUNT="$count"
242 fi
243 possible_cpu_freq="$(echo "${lscpu_output}" | grep -F "CPU max MHz:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep -o '^[0-9]*')"
244 if [ -z "$possible_cpu_freq" ]; then
245 possible_cpu_freq="$(echo "${lscpu_output}" | grep -F "CPU MHz:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep -o '^[0-9]*')"
246 fi
247 if [ -z "$possible_cpu_freq" ]; then
248 possible_cpu_freq="$(echo "${lscpu_output}" | grep "^Model name:" | grep -Eo "[0-9\.]+GHz" | grep -o "^[0-9\.]*" | awk '{print int($0*1000)}')"
249 fi
250 [ -n "$possible_cpu_freq" ] && possible_cpu_freq="${possible_cpu_freq} MHz"
251 elif [ -n "${dmidecode}" ] && dmidecode -t processor >/dev/null 2>&1; then
252 dmidecode_output="$(${dmidecode} -t processor 2>/dev/null)"
253 CPU_INFO_SOURCE="dmidecode"
254 LCPU_COUNT="$(echo "${dmidecode_output}" | grep -F "Thread Count:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
255 CPU_VENDOR="$(echo "${dmidecode_output}" | grep -F "Manufacturer:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
256 CPU_MODEL="$(echo "${dmidecode_output}" | grep -F "Version:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
257 possible_cpu_freq="$(echo "${dmidecode_output}" | grep -F "Current Speed:" | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
258 else
259 # Check OS-specific methods FIRST, then fall back to generic methods
260 if [ "${KERNEL_NAME}" = FreeBSD ]; then
261 CPU_INFO_SOURCE="sysctl"
262 LCPU_COUNT="$(sysctl -n kern.smp.cpus)"
263 # Try dev.cpu.0.freq first (what freebsd.plugin uses, returns MHz)
264 if possible_cpu_freq=$(sysctl -n dev.cpu.0.freq 2>/dev/null); then
265 possible_cpu_freq="${possible_cpu_freq} MHz"
266 # Fallback to machdep.tsc_freq (returns Hz, no unit suffix needed)
267 elif ! possible_cpu_freq=$(sysctl -n machdep.tsc_freq 2>/dev/null); then
268 # Last resort: parse hw.model for GHz values
269 possible_cpu_freq=$(sysctl -n hw.model 2>/dev/null | grep -Eo "[0-9\.]+GHz" | grep -o "^[0-9\.]*" | awk '{print int($0*1000)}')
270 [ -n "$possible_cpu_freq" ] && possible_cpu_freq="${possible_cpu_freq} MHz"
271 fi
272 elif [ "${KERNEL_NAME}" = Darwin ]; then
273 CPU_INFO_SOURCE="sysctl"
274 LCPU_COUNT="$(sysctl -n hw.logicalcpu)"
275 elif [ -n "${nproc}" ]; then
276 CPU_INFO_SOURCE="nproc"
277 LCPU_COUNT="$(${nproc})"
278 elif [ -d /sys/devices/system/cpu ]; then
279 CPU_INFO_SOURCE="sysfs"
280 # This is potentially more accurate than checking `/proc/cpuinfo`.
281 LCPU_COUNT="$(find /sys/devices/system/cpu -mindepth 1 -maxdepth 1 -type d -name 'cpu*' | grep -cEv 'idle|freq')"
282 elif [ -r /proc/cpuinfo ]; then
283 CPU_INFO_SOURCE="procfs"
284 LCPU_COUNT="$(grep -c ^processor /proc/cpuinfo)"
285 fi
286
287 if [ "${KERNEL_NAME}" = Darwin ]; then
288 CPU_MODEL="$(sysctl -n machdep.cpu.brand_string)"
289 if [ "${ARCHITECTURE}" = "x86_64" ]; then
290 CPU_VENDOR="$(sysctl -n machdep.cpu.vendor)"
291 else
292 CPU_VENDOR="Apple"
293 fi
294 echo "${CPU_INFO_SOURCE}" | grep -qv sysctl && CPU_INFO_SOURCE="${CPU_INFO_SOURCE} sysctl"
295 elif uname --version 2>/dev/null | grep -qF 'GNU coreutils'; then
296 CPU_INFO_SOURCE="${CPU_INFO_SOURCE} uname"
297 CPU_MODEL="$(uname -p)"
298 CPU_VENDOR="$(uname -i)"
299 elif [ "${KERNEL_NAME}" = FreeBSD ]; then
300 if (echo "${CPU_INFO_SOURCE}" | grep -qv sysctl); then
301 CPU_INFO_SOURCE="${CPU_INFO_SOURCE} sysctl"
302 fi
303
304 CPU_MODEL="$(sysctl -n hw.model)"
305 elif [ -r /proc/cpuinfo ]; then
306 if (echo "${CPU_INFO_SOURCE}" | grep -qv procfs); then
307 CPU_INFO_SOURCE="${CPU_INFO_SOURCE} procfs"
308 fi
309
310 CPU_MODEL="$(grep -F "model name" /proc/cpuinfo | head -n 1 | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
311 CPU_VENDOR="$(grep -F "vendor_id" /proc/cpuinfo | head -n 1 | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
312 fi
313 fi
314
315 if [ "${KERNEL_NAME}" = Darwin ] && [ "${ARCHITECTURE}" = "x86_64" ]; then
316 CPU_FREQ="$(sysctl -n hw.cpufrequency)"
317 elif [ -r /sys/devices/system/cpu/cpu0/cpufreq/base_frequency ]; then
318 if (echo "${CPU_INFO_SOURCE}" | grep -qv sysfs); then
319 CPU_INFO_SOURCE="${CPU_INFO_SOURCE} sysfs"
320 fi
321
322 value="$(cat /sys/devices/system/cpu/cpu0/cpufreq/base_frequency)"
323 CPU_FREQ="$((value * 1000))"
324 elif [ -n "${possible_cpu_freq}" ]; then
325 CPU_FREQ="${possible_cpu_freq}"
326 elif [ -r /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]; then
327 if (echo "${CPU_INFO_SOURCE}" | grep -qv sysfs); then
328 CPU_INFO_SOURCE="${CPU_INFO_SOURCE} sysfs"
329 fi
330
331 value="$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq)"
332 CPU_FREQ="$((value * 1000))"
333 elif [ -r /proc/cpuinfo ]; then
334 if (echo "${CPU_INFO_SOURCE}" | grep -qv procfs); then
335 CPU_INFO_SOURCE="${CPU_INFO_SOURCE} procfs"
336 fi
337 value=$(grep "cpu MHz" /proc/cpuinfo 2>/dev/null | grep -o "[0-9]*" | head -n 1 | awk '{printf "%0.f",int($0*1000000)}')
338 [ -n "$value" ] && CPU_FREQ="$value"
339 fi
340
341 freq_units="$(echo "${CPU_FREQ}" | cut -f 2 -d ' ')"
342
343 case "${freq_units}" in
344 GHz)
345 value="$(echo "${CPU_FREQ}" | cut -f 1 -d ' ')"
346 CPU_FREQ="$((value * 1000 * 1000 * 1000))"
347 ;;
348 MHz)
349 value="$(echo "${CPU_FREQ}" | cut -f 1 -d ' ')"
350 CPU_FREQ="$((value * 1000 * 1000))"
351 ;;
352 KHz)
353 value="$(echo "${CPU_FREQ}" | cut -f 1 -d ' ')"
354 CPU_FREQ="$((value * 1000))"
355 ;;
356 *) ;;
357
358 esac
359
360 # -------------------------------------------------------------------------------------------------
361 # Detect the total system RAM
362
363 TOTAL_RAM="unknown"
364 RAM_DETECTION="none"
365
366 if [ "${KERNEL_NAME}" = FreeBSD ]; then
367 RAM_DETECTION="sysctl"
368 TOTAL_RAM="$(sysctl -n hw.physmem)"
369 elif [ "${KERNEL_NAME}" = Darwin ]; then
370 RAM_DETECTION="sysctl"
371 TOTAL_RAM="$(sysctl -n hw.memsize)"
372 elif [ -r /proc/meminfo ]; then
373 RAM_DETECTION="procfs"
374 TOTAL_RAM="$(grep -F MemTotal /proc/meminfo | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | cut -f 1 -d ' ')"
375 TOTAL_RAM="$((TOTAL_RAM * 1024))"
376 fi
377
378 # -------------------------------------------------------------------------------------------------
379 # Detect the total system disk space
380
381 is_inside_lxc_container() {
382 mounts_file="/proc/self/mounts"
383
384 [ ! -r "$mounts_file" ] && return 1
385
386 # Check if lxcfs is mounted on /proc
387 awk '$1 == "lxcfs" && $2 ~ "^/proc" { found=1; exit } END { exit !found }' "$mounts_file"
388
389 return $?
390 }
391
392 DISK_SIZE="unknown"
393 DISK_DETECTION="none"
394
395 if [ "${KERNEL_NAME}" = "Darwin" ]; then
396 if DISK_SIZE=$(diskutil info / 2>/dev/null | awk '/Disk Size/ {total += substr($5,2,length($5))} END { print total }') &&
397 [ -n "$DISK_SIZE" ] && [ "$DISK_SIZE" != "0" ]; then
398 DISK_DETECTION="diskutil"
399 else
400 types='hfs'
401
402 if (lsvfs | grep -q apfs); then
403 types="${types},apfs"
404 fi
405
406 if (lsvfs | grep -q ufs); then
407 types="${types},ufs"
408 fi
409
410 DISK_DETECTION="df"
411 DISK_SIZE=$(($(/bin/df -k -t ${types} | tail -n +2 | sed -E 's/\/dev\/disk([[:digit:]]*)s[[:digit:]]*/\/dev\/disk\1/g' | sort -k 1 | awk -F ' ' '{s=$NF;for(i=NF-1;i>=1;i--)s=s FS $i;print s}' | uniq -f 9 | awk '{print $8}' | tr '\n' '+' | rev | cut -f 2- -d '+' | rev) * 1024))
412 fi
413 elif [ "${KERNEL_NAME}" = FreeBSD ]; then
414 types='ufs'
415
416 if (lsvfs | grep -q zfs); then
417 types="${types},zfs"
418 fi
419
420 DISK_DETECTION="df"
421 total="$(df -t ${types} -c -k | tail -n 1 | awk '{print $2}')"
422 DISK_SIZE="$((total * 1024))"
423 else
424 if [ -d /sys/block ] && [ -r /proc/devices ] && ! is_inside_lxc_container; then
425 dev_major_whitelist=''
426
427 # This is a list of device names used for block storage devices.
428 # These translate to the prefixs of files in `/dev` indicating the device type.
429 # They are sorted by lowest used device major number, with dynamically assigned ones at the end.
430 # We use this to look up device major numbers in `/proc/devices`
431 device_names='hd sd mfm ad ftl pd nftl dasd intfl mmcblk mmc ub xvd rfd vbd nvme virtblk blkext'
432
433 for name in ${device_names}; do
434 if grep -qE " ${name}\$" /proc/devices; then
435 dev_major_whitelist="${dev_major_whitelist}:$(grep -E "${name}\$" /proc/devices | sed -e 's/^[[:space:]]*//' | cut -f 1 -d ' ' | tr '\n' ':'):"
436 fi
437 done
438
439 DISK_DETECTION="sysfs"
440 DISK_SIZE="0"
441 for disk in /sys/block/*; do
442 if [ -r "${disk}/size" ] &&
443 (echo "${dev_major_whitelist}" | grep -q ":$(cut -f 1 -d ':' "${disk}/dev"):") &&
444 grep -qv 1 "${disk}/removable"; then
445 size="$(($(cat "${disk}/size") * 512))"
446 DISK_SIZE="$((DISK_SIZE + size))"
447 fi
448 done
449 elif df --version 2>/dev/null | grep -qF "GNU coreutils"; then
450 DISK_DETECTION="df"
451 DISK_SIZE=$(($(df -x tmpfs -x devtmpfs -x squashfs -l -B1 --output=source,size | tail -n +2 | sort -u -k 1 | awk '{print $2}' | tr '\n' '+' | head -c -1)))
452 else
453 DISK_DETECTION="df"
454 include_fs_types="ext*|btrfs|xfs|jfs|reiser*|zfs"
455 DISK_SIZE=$(($(df -T -P | tail -n +2 | sort -u -k 1 | grep -E "${include_fs_types}" | awk '{print $3}' | tr '\n' '+' | head -c -1) * 1024))
456 fi
457 fi
458
459 # -------------------------------------------------------------------------------------------------
460 # Detect whether the node is kubernetes node
461
462 HOST_IS_K8S_NODE="false"
463
464 if [ -n "${KUBERNETES_SERVICE_HOST}" ] && [ -n "${KUBERNETES_SERVICE_PORT}" ]; then
465 # These env vars are set for every container managed by k8s.
466 HOST_IS_K8S_NODE="true"
467 elif pgrep "kubelet"; then
468 # The kubelet is the primary "node agent" that runs on each node.
469 HOST_IS_K8S_NODE="true"
470 fi
471
472 # ------------------------------------------------------------------------------------------------
473 # Detect instance metadata for VMs running on cloud providers
474
475 CLOUD_TYPE="unknown"
476 CLOUD_INSTANCE_TYPE="unknown"
477 CLOUD_INSTANCE_REGION="unknown"
478
479 if [ "${VIRTUALIZATION}" != "none" ] && command -v curl >/dev/null 2>&1; then
480 # Returned HTTP status codes: GCP is 200, AWS is 200, DO is 404.
481 curl --fail -s -m 1 --noproxy "*" http://169.254.169.254 >/dev/null 2>&1
482 ret=$?
483 # anything but operation timeout.
484 if [ "$ret" != 28 ]; then
485 # Try AWS IMDSv2
486 if [ "${CLOUD_TYPE}" = "unknown" ]; then
487 AWS_IMDS_TOKEN="$(curl --fail -s --connect-timeout 1 -m 3 --noproxy "*" -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")"
488 if [ -n "${AWS_IMDS_TOKEN}" ]; then
489 CLOUD_TYPE="AWS"
490 CLOUD_INSTANCE_TYPE="$(curl --fail -s --connect-timeout 1 -m 3 --noproxy "*" -H "X-aws-ec2-metadata-token: $AWS_IMDS_TOKEN" -v "http://169.254.169.254/latest/meta-data/instance-type" 2>/dev/null)"
491 CLOUD_INSTANCE_REGION="$(curl --fail -s --connect-timeout 1 -m 3 --noproxy "*" -H "X-aws-ec2-metadata-token: $AWS_IMDS_TOKEN" -v "http://169.254.169.254/latest/meta-data/placement/region" 2>/dev/null)"
492 fi
493 fi
494
495 # Try GCE computeMetadata v1
496 if [ "${CLOUD_TYPE}" = "unknown" ]; then
497 if curl --fail -s --connect-timeout 1 -m 3 --noproxy "*" -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1" | grep -sq computeMetadata; then
498 CLOUD_TYPE="GCP"
499 CLOUD_INSTANCE_TYPE="$(curl --fail -s --connect-timeout 1 -m 3 --noproxy "*" -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/machine-type")"
500 [ -n "$CLOUD_INSTANCE_TYPE" ] && CLOUD_INSTANCE_TYPE=$(basename "$CLOUD_INSTANCE_TYPE")
501 CLOUD_INSTANCE_REGION="$(curl --fail -s --connect-timeout 1 -m 3 --noproxy "*" -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/zone")"
502 [ -n "$CLOUD_INSTANCE_REGION" ] && CLOUD_INSTANCE_REGION=$(basename "$CLOUD_INSTANCE_REGION") && CLOUD_INSTANCE_REGION=${CLOUD_INSTANCE_REGION%-*}
503 fi
504 fi
505
506 # Try Azure IMDS
507 if [ "${CLOUD_TYPE}" = "unknown" ]; then
508 AZURE_IMDS_DATA="$(curl --fail -s --connect-timeout 1 -m 3 -H "Metadata: true" --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-10-01")"
509 if [ -n "${AZURE_IMDS_DATA}" ] && echo "${AZURE_IMDS_DATA}" | grep -sq azEnvironment; then
510 CLOUD_TYPE="Azure"
511 CLOUD_INSTANCE_TYPE="$(curl --fail -s --connect-timeout 1 -m 3 -H "Metadata: true" --noproxy "*" "http://169.254.169.254/metadata/instance/compute/vmSize?api-version=2021-10-01&format=text")"
512 CLOUD_INSTANCE_REGION="$(curl --fail -s --connect-timeout 1 -m 3 -H "Metadata: true" --noproxy "*" "http://169.254.169.254/metadata/instance/compute/location?api-version=2021-10-01&format=text")"
513 fi
514 fi
515 fi
516 fi
517
518 # -------------------------------------------------------------------------------------------------
519 # Detect the IP address of the interface on the default route
520
521 get_default_interface_ip() {
522 DEFAULT_INTERFACE_IP="unknown"
523 DEFAULT_INTERFACE_NAME="unknown"
524 DEFAULT_INTERFACE_DETECTION="none"
525
526 # Optional parameter for IP version: "-4" (default) or "-6"
527 ip_version="${1:--4}"
528
529 # Check if timeout command is available
530 timeout_cmd=""
531 if command -v timeout >/dev/null 2>&1; then
532 timeout_cmd="timeout 2"
533 fi
534
535 # Find default interface based on OS
536 default_if=""
537
538 case "${KERNEL_NAME}" in
539 Linux)
540 # Ultra-safe: Try /proc first (never hangs - just file reading)
541 if [ "${ip_version}" = "-4" ] && [ -r /proc/net/route ]; then
542 # Default route has destination 00000000
543 default_if=$(awk '$2 == "00000000" && $1 != "lo" {print $1; exit}' /proc/net/route)
544 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="procfs"
545 elif [ "${ip_version}" = "-6" ] && [ -r /proc/net/ipv6_route ]; then
546 # IPv6 default route - field 10 is the interface
547 # Look for ::/0 route (all zeros with prefix 00) that's not on lo
548 default_if=$(awk '$1 == "00000000000000000000000000000000" && $2 == "00" && $10 != "lo" {print $10; exit}' /proc/net/ipv6_route)
549 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="procfs"
550 fi
551
552 # Fallback to ip command if /proc didn't work
553 if [ -z "${default_if}" ] && command -v ip >/dev/null 2>&1; then
554 if [ "${ip_version}" = "-4" ]; then
555 # Extract interface after "dev" keyword
556 default_if=$(${timeout_cmd} ip -o -4 route list default 2>/dev/null | \
557 awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); exit}}')
558 else
559 default_if=$(${timeout_cmd} ip -o -6 route list default 2>/dev/null | \
560 awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); exit}}')
561 fi
562 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="iproute2"
563 fi
564
565 # Last resort: netstat (if available)
566 if [ -z "${default_if}" ] && command -v netstat >/dev/null 2>&1; then
567 if [ "${ip_version}" = "-4" ]; then
568 default_if=$(${timeout_cmd} netstat -rn 2>/dev/null | \
569 awk '$1 == "0.0.0.0" && $2 != "0.0.0.0" {print $NF; exit}')
570 else
571 default_if=$(${timeout_cmd} netstat -rn -A inet6 2>/dev/null | \
572 awk '$1 == "::/0" {print $NF; exit}')
573 fi
574 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="netstat"
575 fi
576 ;;
577
578 Darwin)
579 # macOS specific handling
580 if [ "${ip_version}" = "-4" ]; then
581 # Try route first
582 default_if=$(${timeout_cmd} route -n get default 2>/dev/null | \
583 awk '/interface:/ {print $2; exit}')
584 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="route"
585
586 # Fallback to netstat
587 if [ -z "${default_if}" ]; then
588 default_if=$(${timeout_cmd} netstat -rnf inet 2>/dev/null | \
589 awk '/^default/ {print $4; exit}')
590 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="netstat"
591 fi
592 else
593 # IPv6 - route doesn't work, use netstat
594 # Note: may have multiple defaults (utun interfaces)
595 default_if=$(${timeout_cmd} netstat -rnf inet6 2>/dev/null | \
596 awk '/^default/ && $4 !~ /^utun/ {print $4; exit}')
597 if [ -z "${default_if}" ]; then
598 # If no non-utun default, take first one
599 default_if=$(${timeout_cmd} netstat -rnf inet6 2>/dev/null | \
600 awk '/^default/ {print $4; exit}')
601 fi
602 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="netstat"
603 fi
604 ;;
605
606 *BSD)
607 # FreeBSD, OpenBSD, NetBSD
608 inet_flag=""
609 [ "${ip_version}" = "-6" ] && inet_flag="-inet6"
610
611 # route with -n to prevent DNS lookups
612 default_if=$(${timeout_cmd} route -n get ${inet_flag} default 2>/dev/null | \
613 awk '/interface:/ {print $2; exit}')
614 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="route"
615
616 # Fallback to netstat
617 if [ -z "${default_if}" ]; then
618 if [ "${ip_version}" = "-4" ]; then
619 default_if=$(${timeout_cmd} netstat -rnf inet 2>/dev/null | \
620 awk '/^default/ {print $4; exit}')
621 else
622 default_if=$(${timeout_cmd} netstat -rnf inet6 2>/dev/null | \
623 awk '/^default/ {print $4; exit}')
624 fi
625 [ -n "${default_if}" ] && DEFAULT_INTERFACE_DETECTION="netstat"
626 fi
627 ;;
628 esac
629
630 # Get IP address from interface
631 if [ -n "${default_if}" ] && [ "${default_if}" != "lo" ]; then
632 # Skip if interface is down (Linux only)
633 if [ "${KERNEL_NAME}" = "Linux" ] && [ -r "/sys/class/net/${default_if}/operstate" ]; then
634 state=$(cat "/sys/class/net/${default_if}/operstate" 2>/dev/null)
635 if [ "${state}" = "down" ]; then
636 return
637 fi
638 fi
639
640 if [ "${ip_version}" = "-4" ]; then
641 # IPv4 handling
642 # Try ip command first on Linux
643 if [ "${KERNEL_NAME}" = "Linux" ] && command -v ip >/dev/null 2>&1; then
644 # With -o flag, inet is field 3, IP is field 4
645 ip_addr=$(${timeout_cmd} ip -o -4 addr show dev "${default_if}" 2>/dev/null | \
646 awk '$3 == "inet" && $0 !~ /secondary/ {gsub(/\/.*/, "", $4); print $4; exit}')
647 fi
648
649 # Fallback to ifconfig (if available)
650 if [ -z "${ip_addr}" ] && command -v ifconfig >/dev/null 2>&1; then
651 ip_addr=$(${timeout_cmd} ifconfig "${default_if}" 2>/dev/null | \
652 awk '/inet / && !/127.0.0.1/ {gsub(/addr:/, "", $2); print $2; exit}')
653 fi
654
655 # Validate IPv4
656 if [ -n "${ip_addr}" ]; then
657 if echo "${ip_addr}" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' && \
658 echo "${ip_addr}" | awk -F. '$1<=255 && $2<=255 && $3<=255 && $4<=255 {exit 0} {exit 1}'; then
659 DEFAULT_INTERFACE_IP="${ip_addr}"
660 DEFAULT_INTERFACE_NAME="${default_if}"
661 fi
662 fi
663 else
664 # IPv6 handling
665 if [ "${KERNEL_NAME}" = "Linux" ] && command -v ip >/dev/null 2>&1; then
666 # Get global scope IPv6, not link-local
667 ip_addr=$(${timeout_cmd} ip -o -6 addr show dev "${default_if}" scope global 2>/dev/null | \
668 awk '$3 == "inet6" && $0 !~ /deprecated/ {gsub(/\/.*/, "", $4); print $4; exit}')
669 fi
670
671 if [ -z "${ip_addr}" ] && command -v ifconfig >/dev/null 2>&1; then
672 # Skip fe80:: (link-local) and ::1 (loopback)
673 ip_addr=$(${timeout_cmd} ifconfig "${default_if}" 2>/dev/null | \
674 awk '/inet6 / && !/fe80:/ && !/::1/ {sub(/%.*/, "", $2); print $2; exit}')
675 fi
676
677 # Basic IPv6 validation
678 if [ -n "${ip_addr}" ] && echo "${ip_addr}" | grep -qE '^[0-9a-fA-F:]+$' && \
679 echo "${ip_addr}" | grep -q ':'; then
680 DEFAULT_INTERFACE_IP="${ip_addr}"
681 DEFAULT_INTERFACE_NAME="${default_if}"
682 fi
683 fi
684 fi
685 }
686
687 get_default_interface_ip -4
688
689 echo "NETDATA_CONTAINER_OS_NAME=${CONTAINER_NAME}"
690 echo "NETDATA_CONTAINER_OS_ID=${CONTAINER_ID}"
691 echo "NETDATA_CONTAINER_OS_ID_LIKE=${CONTAINER_ID_LIKE}"
692 echo "NETDATA_CONTAINER_OS_VERSION=${CONTAINER_VERSION}"
693 echo "NETDATA_CONTAINER_OS_VERSION_ID=${CONTAINER_VERSION_ID}"
694 echo "NETDATA_CONTAINER_OS_DETECTION=${CONTAINER_OS_DETECTION}"
695 echo "NETDATA_CONTAINER_IS_OFFICIAL_IMAGE=${CONTAINER_IS_OFFICIAL_IMAGE}"
696 echo "NETDATA_HOST_OS_NAME=${HOST_NAME}"
697 echo "NETDATA_HOST_OS_ID=${HOST_ID}"
698 echo "NETDATA_HOST_OS_ID_LIKE=${HOST_ID_LIKE}"
699 echo "NETDATA_HOST_OS_VERSION=${HOST_VERSION}"
700 echo "NETDATA_HOST_OS_VERSION_ID=${HOST_VERSION_ID}"
701 echo "NETDATA_HOST_OS_DETECTION=${HOST_OS_DETECTION}"
702 echo "NETDATA_HOST_IS_K8S_NODE=${HOST_IS_K8S_NODE}"
703 echo "NETDATA_SYSTEM_KERNEL_NAME=${KERNEL_NAME}"
704 echo "NETDATA_SYSTEM_KERNEL_VERSION=${KERNEL_VERSION}"
705 echo "NETDATA_SYSTEM_ARCHITECTURE=${ARCHITECTURE}"
706 echo "NETDATA_SYSTEM_VIRTUALIZATION=${VIRTUALIZATION}"
707 echo "NETDATA_SYSTEM_VIRT_DETECTION=${VIRT_DETECTION}"
708 echo "NETDATA_SYSTEM_CONTAINER=${CONTAINER}"
709 echo "NETDATA_SYSTEM_CONTAINER_DETECTION=${CONT_DETECTION}"
710 echo "NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT=${LCPU_COUNT}"
711 echo "NETDATA_SYSTEM_CPU_VENDOR=${CPU_VENDOR}"
712 echo "NETDATA_SYSTEM_CPU_MODEL=${CPU_MODEL}"
713 echo "NETDATA_SYSTEM_CPU_FREQ=${CPU_FREQ}"
714 echo "NETDATA_SYSTEM_CPU_DETECTION=${CPU_INFO_SOURCE}"
715 echo "NETDATA_SYSTEM_TOTAL_RAM=${TOTAL_RAM}"
716 echo "NETDATA_SYSTEM_RAM_DETECTION=${RAM_DETECTION}"
717 echo "NETDATA_SYSTEM_TOTAL_DISK_SIZE=${DISK_SIZE}"
718 echo "NETDATA_SYSTEM_DISK_DETECTION=${DISK_DETECTION}"
719 echo "NETDATA_INSTANCE_CLOUD_TYPE=${CLOUD_TYPE}"
720 echo "NETDATA_INSTANCE_CLOUD_INSTANCE_TYPE=${CLOUD_INSTANCE_TYPE}"
721 echo "NETDATA_INSTANCE_CLOUD_INSTANCE_REGION=${CLOUD_INSTANCE_REGION}"
722 echo "NETDATA_SYSTEM_DEFAULT_INTERFACE_NAME=${DEFAULT_INTERFACE_NAME}"
723 echo "NETDATA_SYSTEM_DEFAULT_INTERFACE_IP=${DEFAULT_INTERFACE_IP}"
724 echo "NETDATA_SYSTEM_DEFAULT_INTERFACE_DETECTION=${DEFAULT_INTERFACE_DETECTION}"