#! /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 $?