557 lines
21 KiB
Bash
Executable File
557 lines
21 KiB
Bash
Executable File
#!/bin/sh
|
||
|
||
# This script collects all the necessary information/files for support, then bundle them into a single .tgz file.
|
||
|
||
# Copyright © 2022-2025 Audinate Pty Ltd ACN 120 828 006 (Audinate). All rights reserved.
|
||
#
|
||
#
|
||
# 1. Subject to the terms and conditions of this Licence, Audinate hereby grants you a worldwide, non-exclusive,
|
||
# no-charge, royalty free licence to copy, modify, merge, publish, redistribute, sublicense, and/or sell the
|
||
# Software, provided always that the following conditions are met:
|
||
# 1.1. the Software must accompany, or be incorporated in a licensed Audinate product, solution or offering
|
||
# or be used in a product, solution or offering which requires the use of another licensed Audinate
|
||
# product, solution or offering. The Software is not for use as a standalone product without any
|
||
# reference to Audinate's products;
|
||
# 1.2. the Software is provided as part of example code and as guidance material only without any warranty
|
||
# or expectation of performance, compatibility, support, updates or security; and
|
||
# 1.3. the above copyright notice and this License must be included in all copies or substantial portions
|
||
# of the Software, and all derivative works of the Software, unless the copies or derivative works are
|
||
# solely in the form of machine-executable object code generated by the source language processor.
|
||
#
|
||
# 2. TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||
# PURPOSE AND NONINFRINGEMENT.
|
||
#
|
||
# 3. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL AUDINATE BE LIABLE ON ANY LEGAL THEORY
|
||
# (INCLUDING, WITHOUT LIMITATION, IN AN ACTION FOR BREACH OF CONTRACT, NEGLIGENCE OR OTHERWISE) FOR ANY CLAIM,
|
||
# LOSS, DAMAGES OR OTHER LIABILITY HOWSOEVER INCURRED. WITHOUT LIMITING THE SCOPE OF THE PREVIOUS SENTENCE THE
|
||
# EXCLUSION OF LIABILITY SHALL INCLUDE: LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF
|
||
# DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC
|
||
# LOSS; OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES, ARISING OUT OF OR
|
||
# IN CONNECTION WITH THIS AGREEMENT, ACCESS OF THE SOFTWARE OR ANY OTHER DEALINGS WITH THE SOFTWARE, EVEN IF
|
||
# AUDINATE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH CLAIM, LOSS, DAMAGES OR OTHER LIABILITY.
|
||
#
|
||
# 4. APPLICABLE LEGISLATION SUCH AS THE AUSTRALIAN CONSUMER LAW MAY APPLY REPRESENTATIONS, WARRANTIES, OR CONDITIONS,
|
||
# OR IMPOSES OBLIGATIONS OR LIABILITY ON AUDINATE THAT CANNOT BE EXCLUDED, RESTRICTED OR MODIFIED TO THE FULL
|
||
# EXTENT SET OUT IN THE EXPRESS TERMS OF THIS CLAUSE ABOVE "CONSUMER GUARANTEES". TO THE EXTENT THAT SUCH CONSUMER
|
||
# GUARANTEES CONTINUE TO APPLY, THEN TO THE FULL EXTENT PERMITTED BY THE APPLICABLE LEGISLATION, THE LIABILITY OF
|
||
# AUDINATE UNDER THE RELEVANT CONSUMER GUARANTEE IS LIMITED (WHERE PERMITTED AT AUDINATE'S OPTION) TO ONE OF
|
||
# FOLLOWING REMEDIES OR SUBSTANTIALLY EQUIVALENT REMEDIES:
|
||
# 4.1. THE REPLACEMENT OF THE SOFTWARE, THE SUPPLY OF EQUIVALENT SOFTWARE, OR SUPPLYING RELEVANT SERVICES AGAIN;
|
||
# 4.2. THE REPAIR OF THE SOFTWARE;
|
||
# 4.3. THE PAYMENT OF THE COST OF REPLACING THE SOFTWARE, OF ACQUIRING EQUIVALENT SOFTWARE, HAVING THE RELEVANT
|
||
# SERVICES SUPPLIED AGAIN, OR HAVING THE SOFTWARE REPAIRED.
|
||
#
|
||
# 5. This License does not grant any permissions or rights to use the trade marks (whether registered or unregistered),
|
||
# the trade names, or product names of Audinate.
|
||
#
|
||
# 6. If you choose to redistribute or sell the Software you may elect to offer support, maintenance, warranties,
|
||
# indemnities or other liability obligations or rights consistent with this License. However, you may only act on
|
||
# your own behalf and must not bind Audinate. You agree to indemnify and hold harmless Audinate, and its affiliates
|
||
# from any liability claimed or incurred by reason of your offering or accepting any additional warranty or additional
|
||
# liability.
|
||
#
|
||
|
||
# NOTE: this script is intended to be run on production systems where the dante_package/development
|
||
# directory might not be available (thus no `jq` to rely on for JSON parsing) and basic tools such as `id`
|
||
# could be missing (e.g. BusyBox).
|
||
# Any changes to the script should take this into account.
|
||
|
||
RED_COLOR="\e[01;31m"
|
||
GREEN_COLOR="\e[01;32m"
|
||
YELLOW_COLOR="\e[01;33m"
|
||
BLUE_COLOR="\e[01;34m"
|
||
END_COLOR="\e[0m"
|
||
|
||
red() { printf '%b %s %b' "$RED_COLOR" "$*" "$END_COLOR"; }
|
||
green() { printf '%b %s %b' "$GREEN_COLOR" "$*" "$END_COLOR"; }
|
||
blue() { printf '%b %s %b' "$BLUE_COLOR" "$*" "$END_COLOR"; }
|
||
yellow() { printf '%b %s %b' "$YELLOW_COLOR" "$*" "$END_COLOR"; }
|
||
|
||
logerr() { echo "[ $(red ERROR)] $1"; }
|
||
logwarn() { echo "[$(yellow WARNING)] $1"; }
|
||
loginfo() { echo "[ $(blue INFO)] $1"; }
|
||
logok() { echo "[ $(green OK)] $1"; }
|
||
|
||
fail() { exit 1; }
|
||
|
||
cmd_exists() { command -v -- "$1" >/dev/null 2>&1; }
|
||
|
||
# This function assumes that:
|
||
# - the JSON file is well-formed
|
||
# - key and value appear on the same line
|
||
# - strings are double-quoted and don’t contain escaped quotes
|
||
# - assumes the key exists exactly once per line
|
||
get_json_field()
|
||
{
|
||
json_file=$1
|
||
field_name=$2
|
||
default="__NOT_FOUND__"
|
||
|
||
if [ ! -f "$json_file" ]; then
|
||
echo "error: file '$json_file' not found" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# explaining each sed:
|
||
# - 's/^[^:]*://' removes everything up to and including the first colon
|
||
# - 's/ //' removes the first space character after the colon, if present
|
||
# - 's/^"//' and 's/"$//' removes leading and trailing double quotes from the value
|
||
# - 's/,[[:space:]]*$//' removes a trailing comma and any following whitespace (e.g. to handle lists)
|
||
# - 's/[[:space:]]*$//' trims any remaining trailing whitespace from the value
|
||
value=$(grep "\"$field_name\"" "$json_file" | \
|
||
sed -e 's/^[^:]*://' -e 's/ //' | \
|
||
sed -e 's/^"//' -e 's/"$//' | \
|
||
sed -e 's/,[[:space:]]*$//' | \
|
||
sed -e 's/[[:space:]]*$//' | head -n 1)
|
||
|
||
if [ -z "$value" ]; then
|
||
echo "$default"
|
||
else
|
||
echo "$value"
|
||
fi
|
||
}
|
||
|
||
# where DEP is installed, default value
|
||
DEFAULT_DEP_PATH="/opt/dep"
|
||
|
||
# where DEP logs are stored, default value
|
||
DEFAULT_LOGS_PATH="/var/log"
|
||
|
||
# where temporary files created by this script will be stored, default value
|
||
DEFAULT_TEMP_PATH="/tmp"
|
||
|
||
# where the archive created by this script will be stored, default value
|
||
DEFAULT_OUTPUT_PATH=$(pwd)
|
||
|
||
# DEP container logs can only be stored in /var/log at the moment.
|
||
CONT_LOGS="/var/log/dante_container.log"
|
||
|
||
usage() {
|
||
loginfo "Usage: $0 [OPTIONS]"
|
||
loginfo ""
|
||
loginfo "This tool collects diagnostic data to help debug issues with the DEP software."
|
||
loginfo ""
|
||
loginfo "Options:"
|
||
loginfo " -c <path> Specify the directory where DEP is installed."
|
||
loginfo " Default is '${DEFAULT_DEP_PATH}'."
|
||
loginfo " -l <path> Specify the directory where DEP stores its log files."
|
||
loginfo " Default is '${DEFAULT_LOGS_PATH}'."
|
||
loginfo " -o <path> Specify the output directory for the final archive and any temporary"
|
||
loginfo " files or directories created in the process. This directory must be"
|
||
loginfo " writable by the user executing the script."
|
||
loginfo " Default is the current directory, '${DEFAULT_OUTPUT_PATH}'"
|
||
loginfo ""
|
||
loginfo "Examples:"
|
||
loginfo ""
|
||
loginfo " $0 -c /apps/dep -l /tmp/logs"
|
||
loginfo ""
|
||
loginfo " Collects diagnostic data from a DEP installation in /apps/dep, DEP log files in"
|
||
loginfo " /tmp/logs, and stores the output in the current directory."
|
||
loginfo ""
|
||
loginfo " $0 -c /apps/dep -l /tmp/logs -o /tmp/dep_diagnostics"
|
||
loginfo ""
|
||
loginfo " Collects diagnostic data from a DEP installation in /apps/dep, DEP log files in"
|
||
loginfo " /tmp/logs, and stores the output in /tmp/dep_diagnostics."
|
||
loginfo ""
|
||
loginfo " $0 -o /home/user/dep_diagnostics"
|
||
loginfo ""
|
||
loginfo " Uses the default DEP installation and log file paths, and stores the output in"
|
||
loginfo " /home/user/dep_diagnostics."
|
||
}
|
||
|
||
# Copy a file or directory from a source to a destination.
|
||
#
|
||
# Arguments:
|
||
# src (str): the source file or directory to be copied.
|
||
# dst (str): the destination where the source will be copied.
|
||
# msg (str): an error message to be logged if the copy operation fails.
|
||
#
|
||
# Behaviour:
|
||
# If the source is a directory, the function performs a recursive
|
||
# copy. If the copy operation fails for any reason, it logs a warning
|
||
# message using the provided `msg` argument along with the captured
|
||
# error message from the failed copy operation.
|
||
#
|
||
# NOTE: the function uses `eval` to allow for correct parameter expansion
|
||
# (e.g. "cp /var/log/dante_*" wouldn't work otherwise).
|
||
copy() {
|
||
src="$1"
|
||
dst="$2"
|
||
msg="$3"
|
||
cmd="cp ${src} ${dst}"
|
||
|
||
if [ -d "${src}" ]; then
|
||
cmd="cp -r ${src} ${dst}"
|
||
fi
|
||
|
||
err=$(eval "${cmd}" 2>&1)
|
||
res=$?
|
||
if [ "${res}" -ne 0 ]; then
|
||
logwarn "$msg: $err"
|
||
fi
|
||
}
|
||
|
||
# Checks if a specified directory exists and if it's writable.
|
||
#
|
||
# Arguments:
|
||
# path (str): Directory to check.
|
||
# check_write (str): '1' to check write permission, '0' otherwise.
|
||
# err_msg (str): Optional. Additional error message to display.
|
||
#
|
||
# Behaviour:
|
||
# Logs an error and exits if `path` is not a valid directory.
|
||
# If `check_write` is '1', also checks for write permission.
|
||
# Logs an error and exits if the directory is not writable.
|
||
check_path() {
|
||
path="$1"
|
||
check_write="$2"
|
||
err_msg="$3"
|
||
_ret_val=0
|
||
|
||
if [ ! -d "${path}" ]; then
|
||
logerr "${path} is not a valid path"
|
||
_ret_val=1
|
||
elif [ ! -w "${path}" ] && [ "${check_write}" = "1" ]; then
|
||
logerr "you don't have writing permission for the directory: $path"
|
||
_ret_val=1
|
||
fi
|
||
|
||
if [ "${err_msg}" ] && [ ${_ret_val} -eq 1 ]; then
|
||
logerr "${err_msg}"
|
||
fi
|
||
|
||
if [ ${_ret_val} -eq 1 ]; then
|
||
exit ${_ret_val}
|
||
fi
|
||
}
|
||
|
||
collect_kernel_config() {
|
||
dest_path="$1"
|
||
config_file=""
|
||
is_gzipped=0
|
||
|
||
if [ -f "/proc/config.gz" ]; then
|
||
config_file="/proc/config.gz"
|
||
is_gzipped=1
|
||
elif [ -f "/boot/config-$(uname -r)" ]; then
|
||
config_file="/boot/config-$(uname -r)"
|
||
elif [ -f "/boot/config" ]; then
|
||
config_file="/boot/config"
|
||
elif [ -f "/lib/modules/$(uname -r)/build/.config" ]; then
|
||
config_file="/lib/modules/$(uname -r)/build/.config"
|
||
fi
|
||
|
||
if [ -z "$config_file" ]; then
|
||
logerr "no kernel config found in standard locations"
|
||
return
|
||
fi
|
||
|
||
loginfo "found kernel config at: $config_file"
|
||
|
||
# for gzipped config, try to decompress and copy
|
||
if [ "$is_gzipped" -eq 1 ]; then
|
||
if cmd_exists gunzip; then
|
||
if gunzip -c "$config_file" > "$dest_path"/kernel_config.txt 2>/dev/null; then
|
||
# if gunzip suceeeds, early return to avoid copy
|
||
return
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
copy "$config_file" "$dest_path" "Failed to copy config from $config_file to $dest_path"
|
||
}
|
||
|
||
while getopts ":o:c:l:h" option; do
|
||
case $option in
|
||
o) # output directory
|
||
OUTPUT_PATH=$OPTARG
|
||
TEMP_PATH=$OPTARG
|
||
;;
|
||
l) # log directory
|
||
LOGS_PATH=$OPTARG
|
||
;;
|
||
c) # DEP install path
|
||
DEP_PATH=$OPTARG
|
||
;;
|
||
h) # display Help
|
||
usage
|
||
exit 0
|
||
;;
|
||
\?) # invalid option
|
||
errmsg="invalid option: -$OPTARG"
|
||
;;
|
||
:) # missing argument
|
||
errmsg="option -$OPTARG requires an argument."
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# if we have an error from getopts, log it and exit
|
||
if [ -n "$errmsg" ]; then
|
||
logerr "$errmsg"
|
||
fail
|
||
fi
|
||
|
||
# if we can't create archives, we can't proceed
|
||
if ! cmd_exists tar; then
|
||
logerr "'tar' not found, unable to create archives"
|
||
fail
|
||
fi
|
||
|
||
# check whether we need to use defaults
|
||
: "${DEP_PATH:=$DEFAULT_DEP_PATH}"
|
||
: "${LOGS_PATH:=$DEFAULT_LOGS_PATH}"
|
||
: "${TEMP_PATH:=$DEFAULT_TEMP_PATH}"
|
||
: "${OUTPUT_PATH:=$DEFAULT_OUTPUT_PATH}"
|
||
|
||
# if OUTPUT_PATH can't be written to, we can't proceed
|
||
# NOTE: by checking OUTPUT_PATH we also check TEMP_PATH:
|
||
# the latter is set to /tmp by default, so it is only necessary
|
||
# to make sure we can write to it when the user has specified
|
||
# a different directory, in which case OUTPUT_PATH would have
|
||
# the same value so it makes sense to only check OUTPUT_PATH
|
||
check_path "$OUTPUT_PATH" 1 "please chose a different directory using the -o option. Try $0 -h for more information"
|
||
|
||
# check that provided paths are valid
|
||
check_path "$DEP_PATH" 0 "please chose a different directory using the -c option. Try $0 -h for more information"
|
||
check_path "$LOGS_PATH" 0 "please chose a different directory using the -l option. Try $0 -h for more information"
|
||
|
||
# this script's own log file
|
||
LOGFILE="/tmp/collector.txt"
|
||
|
||
# start logging our own output:
|
||
# - create a named pipe
|
||
# - start tee reading from it in the background
|
||
# - redirect stdout and stderr to the named pipe
|
||
# trap command ensures that the named pipe gets deleted when the script exits.
|
||
mkfifo /tmp/tmpfifo
|
||
trap 'rm /tmp/tmpfifo && rm ${LOGFILE}' EXIT
|
||
tee -a "${LOGFILE}" < /tmp/tmpfifo &
|
||
exec > /tmp/tmpfifo 2>&1
|
||
|
||
# in a world where all shells support process substitution
|
||
# this is an alternative way
|
||
# exec > >(tee -a ${LOGFILE} )
|
||
# exec 2> >(tee -a ${LOGFILE} >&2)
|
||
|
||
# output what we're running with
|
||
loginfo "DEP install path: ${DEP_PATH}"
|
||
loginfo "DEP logs path: ${LOGS_PATH}"
|
||
loginfo "Temporary files will be saved in: ${TEMP_PATH}"
|
||
loginfo "Script output archive will be saved in: ${OUTPUT_PATH}"
|
||
|
||
# we'll use a subdir to store our data
|
||
SUPPORT_DIR=${TEMP_PATH}/dep_support
|
||
|
||
# where to store the ethtool output
|
||
ETHTOOL_FILE="${SUPPORT_DIR}/ethtoolinfo.txt"
|
||
|
||
# where to store the HW clock info
|
||
HW_CLKING_FILE="${SUPPORT_DIR}/hwclk.txt"
|
||
|
||
# in case the script was interrupted midway during a previous run
|
||
rm -rf "${SUPPORT_DIR}"
|
||
|
||
# if we can't create ${SUPPORT_DIR}, we can't proceed
|
||
if ! mkdir -p "${SUPPORT_DIR}" 2>/dev/null; then
|
||
logerr "cannot create directory ${SUPPORT_DIR}: permission denied"
|
||
fail
|
||
fi
|
||
|
||
DANTE_JSON="$DEP_PATH"/dante_package/dante_data/capability/dante.json
|
||
CONFIG_JSON="$DEP_PATH"/dante_package/dante_data/capability/config.json
|
||
CONFIG_DEP="$DEP_PATH"/dante_package/dante_data/config
|
||
ACTIVATION_DIR="${DEP_PATH}/dante_package/dante_data/activation"
|
||
|
||
loginfo "Collecting config files..."
|
||
|
||
# if found, get dante.json
|
||
if [ -f "${DANTE_JSON}" ]; then
|
||
copy "${DANTE_JSON}" "${SUPPORT_DIR}" "collection of ${DANTE_JSON} failed"
|
||
else
|
||
logerr "dante.json not found in $(dirname "${DANTE_JSON}")"
|
||
fi
|
||
|
||
# if found, get config.json
|
||
if [ -f "${CONFIG_JSON}" ]; then
|
||
copy "${CONFIG_JSON}" "${SUPPORT_DIR}" "collection of ${CONFIG_JSON} failed"
|
||
else
|
||
logerr "config.json not found in $(dirname "${CONFIG_JSON}")"
|
||
fi
|
||
|
||
# if found, get all content from dante_data/config
|
||
if [ -d "${CONFIG_DEP}" ]; then
|
||
copy "${CONFIG_DEP}" "${SUPPORT_DIR}" "collection of DEP ${CONFIG_DEP} directory failed"
|
||
else
|
||
logerr "DEP config directory not found in $(dirname "${CONFIG_DEP}")"
|
||
fi
|
||
|
||
# check and collect activation files
|
||
if [ -d "${ACTIVATION_DIR}" ]; then
|
||
# copy whatever we have in the activation directory
|
||
copy "${ACTIVATION_DIR}" "${SUPPORT_DIR}" "collection of DEP activation files failed"
|
||
# log errors related to single act
|
||
|
||
for actFile in device.lic manufacturer.cert; do
|
||
if [ ! -f "${ACTIVATION_DIR}/${actFile}" ]; then
|
||
logwarn "activation file '${actFile}' not found in ${ACTIVATION_DIR}"
|
||
fi
|
||
done
|
||
else
|
||
logerr "DEP activation directory not found in $(dirname "${ACTIVATION_DIR}")"
|
||
fi
|
||
|
||
loginfo "Collecting DEP logs..."
|
||
|
||
# get all DEP logs
|
||
mkdir -p "${SUPPORT_DIR}/logs"
|
||
copy "${LOGS_PATH}/dante_*" "${SUPPORT_DIR}/logs" "collection of DEP logs failed"
|
||
|
||
# get the container logs
|
||
mkdir -p "${SUPPORT_DIR}/logs"
|
||
copy "${CONT_LOGS}" "${SUPPORT_DIR}/logs" "collection of DEP container logs failed"
|
||
|
||
loginfo "Collecting system info..."
|
||
|
||
# get kernel config
|
||
collect_kernel_config "${SUPPORT_DIR}"
|
||
|
||
# get /proc/cpuinfo
|
||
copy "/proc/cpuinfo" "${SUPPORT_DIR}/cpuinfo.txt" "collection of /proc/cpuinfo failed"
|
||
|
||
# get /proc/interrupts
|
||
copy "/proc/interrupts" "${SUPPORT_DIR}/interrupts.txt" "collection of /proc/interrupts failed"
|
||
|
||
# get mount points
|
||
mount > "${SUPPORT_DIR}/mountinfo.txt" || logwarn "collection of mount points failed"
|
||
|
||
# get info about running processes: try including thread info first,
|
||
# in case of failure (e.g. "ps" is actually BusyBox) fall back to processes only
|
||
if ! ps -efL > "${SUPPORT_DIR}/processinfo.txt" 2> /dev/null; then
|
||
ps > "${SUPPORT_DIR}/processinfo.txt" || logwarn "unable to write process info into ${SUPPORT_DIR}/processinfo.txt"
|
||
fi
|
||
|
||
# get the list of active sockets
|
||
if cmd_exists netstat; then
|
||
netstat -anp 2>/dev/null > "${SUPPORT_DIR}/netstat.txt" || logwarn "unable to collect active socket info"
|
||
else
|
||
logwarn "netstat command not available"
|
||
fi
|
||
|
||
# get info about network interfaces
|
||
if cmd_exists ip; then
|
||
ip address > "${SUPPORT_DIR}/ipinfo.txt" || logwarn "unable to write ip info to ${SUPPORT_DIR}/ipinfo.txt"
|
||
else
|
||
logwarn "ip command not available"
|
||
fi
|
||
|
||
# get ALSA version (userspace libs)
|
||
if cmd_exists aplay; then
|
||
aplay --version > "${SUPPORT_DIR}/alsa.txt" || logwarn "unable to write ALSA version to ${SUPPORT_DIR}/alsa.txt"
|
||
fi
|
||
|
||
# get kernel messages
|
||
if cmd_exists dmesg; then
|
||
dmesg > "${SUPPORT_DIR}/dmesg.txt" || logwarn "unable to collect kernel messages - dmesg failed"
|
||
fi
|
||
|
||
# get device nodes
|
||
ls -l /dev > "${SUPPORT_DIR}/device_nodes.txt" || logwarn "unable to collect info about device nodes"
|
||
|
||
# get timestamp and coalesce info about each network interface
|
||
if cmd_exists ethtool; then
|
||
for NETWORK_INTERFACE in /sys/class/net/*; do
|
||
INTERFACE_NAME=$(basename "$NETWORK_INTERFACE")
|
||
|
||
{
|
||
echo "ethtool -c \"$INTERFACE_NAME\""
|
||
ethtool -c "$INTERFACE_NAME" 2>&1
|
||
echo "------------------------"
|
||
} >> "$ETHTOOL_FILE"
|
||
|
||
{
|
||
echo "ethtool -T \"$INTERFACE_NAME\""
|
||
ethtool -T "$INTERFACE_NAME" 2>&1
|
||
echo "------------------------"
|
||
} >> "$ETHTOOL_FILE"
|
||
done
|
||
else
|
||
logwarn "ethtool command not available"
|
||
fi
|
||
|
||
|
||
# get info for HW clocking, if enabled in dante.json
|
||
if [ -f "${DANTE_JSON}" ]; then
|
||
MNT_DIR="${SUPPORT_DIR}/mnt"
|
||
ROOTFS_FILE="$DEP_PATH/dante_package/dante_data/images/0/rootfs_squash"
|
||
useHwClock=$(get_json_field "${DANTE_JSON}" useHwClock)
|
||
|
||
if [ "$useHwClock" = "true" ]; then
|
||
circuitName=$(get_json_field "${DANTE_JSON}" circuitName)
|
||
i2cBus=$(get_json_field "${DANTE_JSON}" i2cBus)
|
||
i2cAddr=$(get_json_field "${DANTE_JSON}" i2cAddr)
|
||
|
||
{
|
||
echo "circuitName=$circuitName"
|
||
echo "i2cBus=$i2cBus"
|
||
echo "i2cAddr=$i2cAddr"
|
||
} >> "$HW_CLKING_FILE"
|
||
|
||
# hwclkcfg binary is in the DEP rootfs so mount rootfs first and then run it
|
||
mkdir -p "${MNT_DIR}"
|
||
if ! mount "$ROOTFS_FILE" "${MNT_DIR}"; then
|
||
logerr "unable to collect HW clocking info: rootfs mount failed"
|
||
else
|
||
"$MNT_DIR"/dante/hwclkcfg -c --i2cbus "$i2cBus" --i2caddr "$i2cAddr" "$circuitName" >> "$HW_CLKING_FILE" 2>&1
|
||
umount "${MNT_DIR}" 2> /dev/null
|
||
fi
|
||
rm -rf "${MNT_DIR}"
|
||
fi
|
||
fi
|
||
|
||
# if we are UID 0, run dep_check.sh and save its output
|
||
if [ "$(grep -E '^Uid:' /proc/self/status | awk '{print $2}')" -eq "0" ]; then
|
||
if [ ! -f "./development/dep_check.sh" ]; then
|
||
logwarn "dep_check.sh not found, skipping"
|
||
else
|
||
loginfo "Run dep_check and collect its output..."
|
||
{ ./development/dep_check.sh "${DEP_PATH}" > "${SUPPORT_DIR}/depcheck.txt"; } 2>&1
|
||
# remove escape characters from dep_check.sh output
|
||
sed -i 's/[^[:print:]]\[[0-9;]*[a-zA-Z]//g' "${SUPPORT_DIR}/depcheck.txt"
|
||
fi
|
||
else
|
||
logwarn "could not run dep_check.sh because user was not root"
|
||
fi
|
||
|
||
# add this script own logs to the bundle
|
||
if [ -f "$LOGFILE" ]; then
|
||
# remove escape characters from this script output
|
||
sed -i 's/[^[:print:]]\[[0-9;]*[a-zA-Z]//g' "$LOGFILE"
|
||
fi
|
||
|
||
loginfo "Create final archive..."
|
||
|
||
# copy our own logs to the support directory, fail silently
|
||
cp "$LOGFILE" "${SUPPORT_DIR}/collector.txt" || true
|
||
|
||
# bundle everything together
|
||
timestamp=$(date "+%Y.%m.%d-%H.%M.%S")
|
||
tgz_name="dep_support-${timestamp}.tgz"
|
||
|
||
if ! tar czf "${OUTPUT_PATH}"/"${tgz_name}" -C "$(dirname "${SUPPORT_DIR}")" "$(basename "${SUPPORT_DIR}")" > /dev/null 2>&1; then
|
||
logerr "unable to bundle support files in ${OUTPUT_PATH}/${tgz_name}"
|
||
_exit_val=1
|
||
else
|
||
logok "DEP log files and system info bundled in ${OUTPUT_PATH}/${tgz_name}"
|
||
_exit_val=0
|
||
fi
|
||
|
||
# remove temporary data
|
||
rm -rf "${SUPPORT_DIR}"
|
||
exit ${_exit_val}
|
||
|
||
#
|
||
# Copyright © 2022-2025 Audinate Pty Ltd ACN 120 828 006 (Audinate). All rights reserved.
|
||
#
|