|  | #! /bin/bash | 
|  |  | 
|  | help=$" | 
|  | dreport creates an archive(xz compressed) consisting of the following: | 
|  | * Configuration information | 
|  | * Debug information | 
|  | * A summary report | 
|  | The type parameter controls the content of the data. The generated | 
|  | archive is stored in the user specified location. | 
|  |  | 
|  | usage: dreport [OPTION] | 
|  |  | 
|  | Options: | 
|  | -n, —-name <name>     Name to be used for the archive. | 
|  | Default name format obmcdump_<id>_<epochtime> | 
|  | -d, —-dir <directory> Archive directory to copy the compressed report. | 
|  | Default output directory is /tmp | 
|  | -i, —-id <id>         Dump identifier to associate with the archive. | 
|  | Identifiers include numeric characters. | 
|  | Default dump identifier is 0 | 
|  | -t, —-type <type>     Data collection type. Valid types are | 
|  | "user", "core", "elog". | 
|  | Default type is "user" initiated. | 
|  | -p, —-path <path>     Optional contents to be included in the archive. | 
|  | Valid paths are absolute file path or d-bus path | 
|  | based on type parameter. | 
|  | -Absolute file path for "core" type. | 
|  | -elog d-bus object for "elog" type. | 
|  | -s, --size <size>     Maximum allowed size(in KB) of the archive. | 
|  | Report will be truncated in case size exceeds | 
|  | this limit. Default size is unlimited. | 
|  | -v, —-verbose         Increase logging verbosity. | 
|  | -V, --version         Output version information. | 
|  | -q, —-quiet           Only log fatal errors to stderr | 
|  | -h, —-help            Display this help and exit. | 
|  | " | 
|  |  | 
|  | #CONSTANTS | 
|  | declare -rx TRUE=1 | 
|  | declare -rx FALSE=0 | 
|  | declare -rx UNLIMITED="unlimited" | 
|  | declare -rx SUMMARY_DUMP="summary" | 
|  | declare -rx TYPE_USER="user" | 
|  | declare -rx TYPE_CORE="core" | 
|  | declare -rx TYPE_ELOG="elog" | 
|  | declare -rx TYPE_CHECKSTOP="checkstop" | 
|  | declare -rx TYPE_RAMOOPS="ramoops" | 
|  | declare -rx SUMMARY_LOG="summary.log" | 
|  | declare -rx DREPORT_LOG="dreport.log" | 
|  | declare -rx TMP_DIR="/tmp" | 
|  | declare -rx EPOCHTIME=$(date +"%s") | 
|  | declare -rx TIME_STAMP="date -u" | 
|  | declare -rx PLUGIN="pl_" | 
|  | declare -rx DREPORT_SOURCE="/usr/share/dreport.d" | 
|  | declare -rx DREPORT_INCLUDE="$DREPORT_SOURCE/include.d" | 
|  | declare -rx ZERO="0" | 
|  | declare -rx JOURNAL_LINE_LIMIT="500" | 
|  | declare -rx HEADER_EXTENSION="$DREPORT_INCLUDE/gendumpheader" | 
|  |  | 
|  | #Error Codes | 
|  | declare -rx SUCCESS="0" | 
|  | declare -rx INTERNAL_FAILURE="1" | 
|  | declare -rx RESOURCE_UNAVAILABLE="2" | 
|  |  | 
|  | #VARIABLES | 
|  | declare -x name="" | 
|  | declare -x dump_dir="/tmp" | 
|  | declare -x dump_id="00000000" | 
|  | declare -x dump_type=$TYPE_USER | 
|  | declare -x verbose=$FALSE | 
|  | declare -x quiet=$FALSE | 
|  | declare -x dump_size="unlimited" | 
|  | declare -x name_dir="" | 
|  | declare -x optional_path="" | 
|  | declare -x dreport_log="" | 
|  | declare -x summary_log="" | 
|  | declare -x cur_dump_size=0 | 
|  | declare -x pid=$ZERO | 
|  | declare -x elog_id="" | 
|  |  | 
|  | #Source dreport common functions | 
|  | . $DREPORT_INCLUDE/functions | 
|  |  | 
|  | # @brief Initiate data collection based on the type. | 
|  | # @return 0 on success, error code otherwise | 
|  | function collect_data() | 
|  | { | 
|  | case $dump_type in | 
|  | $TYPE_USER) | 
|  | ;; | 
|  | $TYPE_CORE) | 
|  | log_summary "Core: $optional_path" | 
|  | set_core_pid | 
|  | ;; | 
|  | $TYPE_RAMOOPS) | 
|  | log_summary "Ramoops: $optional_path" | 
|  | ;; | 
|  | $TYPE_ELOG) | 
|  | log_summary "ELOG: $optional_path" | 
|  | elog_id=$(basename "$optional_path") | 
|  | set_elog_pid | 
|  | ;; | 
|  | $TYPE_CHECKSTOP) | 
|  | log_summary "CHECKSTOP: $optional_path" | 
|  | elog_id=$(basename "$optional_path") | 
|  | set_elog_pid | 
|  | ;; | 
|  |  | 
|  | $SUMMARY_DUMP) | 
|  | #No data collection is required. | 
|  | return | 
|  | ;; | 
|  | *) # unknown option | 
|  | log_error "Skipping: Unknown dump type: $dump_type" | 
|  | return | 
|  | ;; | 
|  | esac | 
|  |  | 
|  | plugin_path=$DREPORT_SOURCE/$PLUGIN$dump_type.d | 
|  |  | 
|  | # check plugin directory for this dump type? | 
|  | if [ ! -d $plugin_path ]; then | 
|  | log_error "$plugin_path does not exist, skipping dump collection" | 
|  | return 0 | 
|  | fi | 
|  |  | 
|  | #Executes plugins based on the type. | 
|  | for i in $plugin_path/* ; do | 
|  | $i | 
|  | done | 
|  | } | 
|  |  | 
|  | # @brief set pid by reading information from the optional path. | 
|  | #        dreport "core" type user provides core file as optional path parameter. | 
|  | #        As per coredump source code systemd-coredump uses below format | 
|  | #        https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c | 
|  | #        /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “. | 
|  | #        <process ID>.%s000000" | 
|  | function set_core_pid() | 
|  | { | 
|  | #Escape bash characters in file name | 
|  | file=$(printf %q "$optional_path") | 
|  |  | 
|  | #matching systemd-coredump core file format. | 
|  | pid=$(echo $file | awk -F . '{ print $5}') | 
|  | } | 
|  |  | 
|  | # @brief set elog pid by reading _PID information from the elog d-bus object. | 
|  | #        _PID information is stored  elog Additional data field | 
|  | #        Data format  "_PID=<pid>" | 
|  | function set_elog_pid() | 
|  | { | 
|  | additional_data=$(busctl get-property xyz.openbmc_project.Logging \ | 
|  | $optional_path \ | 
|  | xyz.openbmc_project.Logging.Entry \ | 
|  | AdditionalData) | 
|  |  | 
|  | #read _PID data. | 
|  | if [ ! -z "$additional_data" ]; then | 
|  | pid=$(echo $additional_data | \ | 
|  | awk -F _PID= '{ print ($2+0)}') | 
|  | fi | 
|  | } | 
|  |  | 
|  | # @brief Initial version of the summary log | 
|  | init_summary() | 
|  | { | 
|  | log_summary "Name:          $name.tar.xz" | 
|  | log_summary "Epochtime:     $EPOCHTIME" | 
|  | log_summary "ID:            $dump_id" | 
|  | log_summary "Type:          $dump_type" | 
|  | } | 
|  |  | 
|  | # @brief Check the validity of user inputs and initialize global | 
|  | #        variables. Create directory for temporary data collection | 
|  | # @return 0 on success, error code otherwise | 
|  |  | 
|  | function initialize() | 
|  | { | 
|  | #Dump file name | 
|  | if [ -z $name ]; then | 
|  | name=$"obmcdump_"$dump_id"_$EPOCHTIME" | 
|  | fi | 
|  |  | 
|  | #Create temporary data directory. | 
|  | mkdir -p "$TMP_DIR/$name" | 
|  | if [ $? -ne 0 ]; then | 
|  | echo "Error: Failed to create the temporary directory." | 
|  | return $RESOURCE_UNAVAILABLE; | 
|  | fi | 
|  |  | 
|  | #name directory | 
|  | name_dir="$TMP_DIR/$name" | 
|  |  | 
|  | #dreport log file | 
|  | dreport_log="$name_dir/$DREPORT_LOG" | 
|  |  | 
|  | #summary log file | 
|  | summary_log="$name_dir/$SUMMARY_LOG" | 
|  |  | 
|  | #Type | 
|  | if [[ ! ($dump_type = $TYPE_USER || \ | 
|  | $dump_type = $TYPE_CORE || \ | 
|  | $dump_type = $TYPE_ELOG || \ | 
|  | $dump_type = $TYPE_RAMOOPS || \ | 
|  | $dump_type = $TYPE_CHECKSTOP) ]]; then | 
|  | log_error "Invalid -type, Only summary log is available" | 
|  | dump_type=$SUMMARY_DUMP | 
|  | fi | 
|  |  | 
|  | #Size | 
|  | #Check the input is integer. | 
|  | if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then | 
|  | #Converts in to bytes. | 
|  | dump_size="$((dump_size * 1024))" | 
|  | else | 
|  | dump_size=$UNLIMITED | 
|  | fi | 
|  |  | 
|  | return $SUCCESS | 
|  | } | 
|  |  | 
|  | # @brief Packaging the dump and transferring to dump location. | 
|  | function package() | 
|  | { | 
|  | mkdir -p "$dump_dir" | 
|  | if [ $? -ne 0 ]; then | 
|  | log_error "Could not create the destination directory $dump_dir" | 
|  | dest_dir=$TMP_DIR | 
|  | fi | 
|  |  | 
|  | #tar and compress the files. | 
|  | if [ -f "$HEADER_EXTENSION" ]; then | 
|  | tar -Jcf "$name_dir.tar.xz" -C \ | 
|  | $(dirname "$name_dir") $(basename "$name_dir") | 
|  | echo "Adding Dump Header :"$HEADER_EXTENSION | 
|  | ("$HEADER_EXTENSION") | 
|  | cat "$name_dir.tar.xz" | tee -a "/tmp/dumpheader_$EPOCHTIME" > /dev/null | 
|  | mv "/tmp/dumpheader_$EPOCHTIME" "$name_dir.tar.xz" | 
|  | else | 
|  | tar -Jcf "$name_dir.tar.xz" -C \ | 
|  | $(dirname "$name_dir") $(basename "$name_dir") | 
|  | fi | 
|  |  | 
|  | if [ $? -ne 0 ]; then | 
|  | echo $($TIME_STAMP) "Could not create the compressed tar file" | 
|  | rm -r "$name_dir" | 
|  | return $INTERNAL_FAILURE | 
|  | fi | 
|  |  | 
|  | #remove the temporary name specific directory | 
|  | rm -r "$name_dir" | 
|  |  | 
|  | echo $($TIME_STAMP) "Report is available in $dump_dir" | 
|  |  | 
|  | if [ "$TMP_DIR" == "$dump_dir" ] || [ "$TMP_DIR/" == "$dump_dir" ]; then | 
|  | return $SUCCESS | 
|  | fi | 
|  |  | 
|  | #copy the compressed tar file into the destination | 
|  | cp "$name_dir.tar.xz" "$dump_dir" | 
|  | if [ $? -ne 0 ]; then | 
|  | echo "Failed to copy the $name_dir.tar.xz to $dump_dir" | 
|  | rm "$name_dir.tar.xz" | 
|  | return $INTERNAL_FAILURE | 
|  | fi | 
|  |  | 
|  | #Remove the temporary copy of the file | 
|  | rm "$name_dir.tar.xz" | 
|  | } | 
|  |  | 
|  | # @brief Main function | 
|  | function main() | 
|  | { | 
|  | #initialize the global variables and | 
|  | #create temporary storage locations | 
|  | initialize | 
|  | result=$? | 
|  | if [[ ${result} -ne $SUCCESS ]]; then | 
|  | echo $($TIME_STAMP) "Error: Failed to initialize, Exiting" | 
|  | exit; | 
|  | fi | 
|  |  | 
|  | #Initialize the summary log | 
|  | init_summary | 
|  |  | 
|  | #collect data based on the type. | 
|  | collect_data | 
|  |  | 
|  | package  #package the dump | 
|  | result=$? | 
|  | if [[ ${result} -ne $SUCCESS ]]; then | 
|  | echo $($TIME_STAMP) "Error: Failed to package, Exiting" | 
|  | else | 
|  | echo $($TIME_STAMP) "Successfully completed" | 
|  | exit; | 
|  | fi | 
|  | } | 
|  |  | 
|  | TEMP=`getopt -o n:d:i:t:s:p:vVqh \ | 
|  | --long name:,dir:,dumpid:,type:,size:,path:,verbose,version,quiet,help \ | 
|  | -- "$@"` | 
|  |  | 
|  | if [ $? -ne 0 ] | 
|  | then | 
|  | echo "Error: Invalid options" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | eval set -- "$TEMP" | 
|  |  | 
|  | while [[ $# -gt 1 ]]; do | 
|  | key="$1" | 
|  | case $key in | 
|  | -n|--name) | 
|  | name=$2 | 
|  | shift 2;; | 
|  | -d|--dir) | 
|  | dump_dir=$2 | 
|  | shift 2;; | 
|  | -i|--dumpid) | 
|  | dump_id=$2 | 
|  | shift 2;; | 
|  | -t|--type) | 
|  | dump_type=$2 | 
|  | shift 2;; | 
|  | -s|--size) | 
|  | dump_size=$2 | 
|  | shift 2;; | 
|  | -p|--path) | 
|  | optional_path=$2 | 
|  | shift 2;; | 
|  | -v|—-verbose) | 
|  | verbose=$TRUE | 
|  | shift;; | 
|  | -V|--version) | 
|  | shift;; | 
|  | -q|—-quiet) | 
|  | quiet=$TRUE | 
|  | shift;; | 
|  | -h|--help) | 
|  | echo "$help" | 
|  | exit;; | 
|  | *) # unknown option | 
|  | log_error "Unknown argument: $1" | 
|  | log_info "$help" | 
|  | exit 1;; | 
|  | esac | 
|  | done | 
|  |  | 
|  | main #main program | 
|  | exit $? |