xref: /openbmc/phosphor-debug-collector/tools/dreport.d/dreport (revision 8e81499b1f0922913de8968db58246422292c06b)
1#! /bin/bash
2
3help=$(cat << EOF
4        dreport creates an archive consisting of the following:
5                * Configuration information
6                * Debug information
7                * A summary report
8        The type parameter controls the content of the data. The generated
9        archive is stored in the user specified location.
10
11usage: dreport [OPTION]
12
13Options:
14        -n, —-name <name>     Name to be used for the archive.
15                              Default name format obmcdump_<id>_<epochtime>
16        -d, —-dir <directory> Archive directory to copy the compressed report.
17                              Default output directory is /tmp
18        -i, —-id <id>         Dump identifier to associate with the archive.
19                              Identifiers include numeric characters.
20                              Default dump identifier is 0
21        -t, —-type <type>     Data collection type. Valid types are
22                              "user", "core", "elog".
23                              Default type is "user" initiated.
24        -p, —-path <path>     Optional contents to be included in the archive.
25                              Valid paths are absolute file path or d-bus path
26                              based on type parameter.
27                                 -Absolute file path for "core" type.
28                                 -elog d-bus object for "elog" type.
29        -s, --size <size>     Maximum allowed size(in KB) of the archive.
30                              Report will be truncated in case size exceeds
31                              this limit. Default size is unlimited.
32        -v, —-verbose         Increase logging verbosity.
33        -q, —-quiet           Only log fatal errors to stderr
34        -h, —-help            Display this help and exit.
35EOF
36)
37
38#CONSTANTS
39declare -rx TRUE=1
40declare -rx FALSE=0
41declare -rx UNLIMITED="unlimited"
42declare -rx SUMMARY_DUMP="summary"
43declare -rx TYPE_USER="user"
44declare -rx TYPE_CORE="core"
45declare -rx TYPE_ELOG="elog"
46declare -rx TYPE_CHECKSTOP="checkstop"
47declare -rx TYPE_RAMOOPS="ramoops"
48declare -rx SUMMARY_LOG="summary.log"
49declare -rx DREPORT_LOG="dreport.log"
50declare -rx TMP_DIR="/tmp"
51EPOCHTIME=$(date +"%s")
52declare -rx EPOCHTIME
53declare -rx TIME_STAMP="date -u"
54declare -rx PLUGIN="pl_"
55declare -rx DREPORT_SOURCE="/usr/share/dreport.d"
56declare -rx DREPORT_INCLUDE="$DREPORT_SOURCE/include.d"
57declare -rx ZERO="0"
58declare -rx JOURNAL_LINE_LIMIT="500"
59declare -rx HEADER_EXTENSION="$DREPORT_INCLUDE/gendumpheader"
60
61#Error Codes
62declare -rx SUCCESS="0"
63declare -rx INTERNAL_FAILURE="1"
64declare -rx RESOURCE_UNAVAILABLE="2"
65
66#VARIABLES
67declare -x name=""
68declare -x dump_dir="/tmp"
69declare -x dump_id="00000000"
70declare -x dump_type=$TYPE_USER
71declare -x verbose=$FALSE
72declare -x quiet=$FALSE
73declare -x dump_size="unlimited"
74declare -x name_dir=""
75declare -x optional_path=""
76declare -x dreport_log=""
77declare -x summary_log=""
78declare -x cur_dump_size=0
79declare -x pid=$ZERO
80declare -x elog_id=""
81
82#Source dreport common functions
83# shellcheck source=tools/dreport.d/include.d/functions
84. "$DREPORT_INCLUDE"/functions
85
86# @brief Initiate data collection based on the type.
87# @return 0 on success, error code otherwise
88function collect_data()
89{
90    case $dump_type in
91        "$TYPE_USER")
92            ;;
93        "$TYPE_CORE")
94            log_summary "Core: $optional_path"
95            set_core_pid
96            ;;
97        "$TYPE_RAMOOPS")
98            log_summary "Ramoops: $optional_path"
99            ;;
100        "$TYPE_ELOG")
101            log_summary "ELOG: $optional_path"
102            elog_id=$(basename "$optional_path")
103            set_elog_pid
104            ;;
105        "$TYPE_CHECKSTOP")
106            log_summary "CHECKSTOP: $optional_path"
107            elog_id=$(basename "$optional_path")
108            set_elog_pid
109            ;;
110
111        "$SUMMARY_DUMP")
112            #No data collection is required.
113            return
114            ;;
115        *) # unknown option
116            log_error "Skipping: Unknown dump type: $dump_type"
117            return
118            ;;
119    esac
120
121    plugin_path=$DREPORT_SOURCE/$PLUGIN$dump_type.d
122
123    # check plugin directory for this dump type?
124    if [ ! -d "$plugin_path" ]; then
125        log_error "$plugin_path does not exist, skipping dump collection"
126        return 0
127    fi
128
129    #Executes plugins based on the type.
130    for i in "$plugin_path"/* ; do
131        "$i"
132    done
133}
134
135# @brief set pid by reading information from the optional path.
136#        dreport "core" type user provides core file as optional path parameter.
137#        As per coredump source code systemd-coredump uses below format
138#        https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c
139#        /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “.
140#        <process ID>.%s000000"
141function set_core_pid()
142{
143    #Escape bash characters in file name
144    file=$(printf %q "$optional_path")
145
146    #matching systemd-coredump core file format.
147    pid=$(echo "$file" | awk -F . '{ print $5}')
148}
149
150# @brief set elog pid by reading _PID information from the elog d-bus object.
151#        _PID information is stored  elog Additional data field
152#        Data format  "_PID=<pid>"
153function set_elog_pid()
154{
155    additional_data=$(busctl get-property xyz.openbmc_project.Logging \
156            "$optional_path" \
157            xyz.openbmc_project.Logging.Entry \
158        AdditionalData)
159
160    #read _PID data.
161    if [ -n "$additional_data" ]; then
162        pid=$(echo "$additional_data" | \
163            awk -F _PID= '{ print ($2+0)}')
164    fi
165}
166
167# @brief Initial version of the summary log
168function init_summary()
169{
170    log_summary "Name:          $name.$ARCHIVE_EXT"
171    log_summary "Epochtime:     $EPOCHTIME"
172    log_summary "ID:            $dump_id"
173    log_summary "Type:          $dump_type"
174}
175
176# @brief Check the validity of user inputs and initialize global
177#        variables. Create directory for temporary data collection
178# @return 0 on success, error code otherwise
179
180function initialize()
181{
182    #Dump file name
183    if [ -z "$name" ]; then
184        name=$"obmcdump_"$dump_id"_$EPOCHTIME"
185    fi
186
187    #Create temporary data directory.
188    if ! mkdir -p "$TMP_DIR/$name"; then
189        echo "Error: Failed to create the temporary directory."
190        return $RESOURCE_UNAVAILABLE;
191    fi
192
193    #name directory
194    name_dir="$TMP_DIR/$name"
195
196    #dreport log file
197    dreport_log="$name_dir/$DREPORT_LOG"
198
199    #summary log file
200    summary_log="$name_dir/$SUMMARY_LOG"
201
202    #Type
203    if ! { [[ $dump_type = "$TYPE_USER" ]] || \
204            [[ $dump_type = "$TYPE_CORE" ]] || \
205            [[ $dump_type = "$TYPE_ELOG" ]] || \
206            [[ $dump_type = "$TYPE_RAMOOPS" ]] || \
207            [[ $dump_type = "$TYPE_CHECKSTOP" ]]; }; then
208        log_error "Invalid -type, Only summary log is available"
209        dump_type=$SUMMARY_DUMP
210    fi
211
212    #Size
213    #Check the input is integer.
214    if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then
215        #Converts in to bytes.
216        dump_size="$((dump_size * 1024))"
217    else
218        dump_size=$UNLIMITED
219    fi
220
221    return $SUCCESS
222}
223
224# @brief Packaging the dump and transferring to dump location.
225function package()
226{
227    if ! mkdir -p "$dump_dir"; then
228        log_error "Could not create the destination directory $dump_dir"
229        dump_dir=$TMP_DIR
230    fi
231
232    #tar and compress the files.
233    create_archive "$name_dir" "$(dirname "$name_dir")" "$(basename "$name_dir")"
234
235    if [ -f "$HEADER_EXTENSION" ]; then
236        echo "Adding Dump Header :"$HEADER_EXTENSION
237        ("$HEADER_EXTENSION")
238        # shellcheck disable=SC2002
239        # gendumpheader was moved to openpower-debug-collector (commit 94bc05fc).
240        # It is unclear if this code path is still used.
241        cat "$ARCHIVE_PATH" | tee -a "/tmp/dumpheader_$EPOCHTIME" > /dev/null
242        mv "/tmp/dumpheader_$EPOCHTIME" "$ARCHIVE_PATH"
243    fi
244
245    # shellcheck disable=SC2181
246    # TODO: Refactor needed, create_archive is executed in both branches.
247    # This check captures the exit status of whichever branch ran.
248    # To be fixed in a separate patch.
249    if [ $? -ne 0 ]; then
250        echo "$($TIME_STAMP)" "Could not create the compressed tar file"
251        rm -r "$name_dir"
252        return $INTERNAL_FAILURE
253    fi
254
255    #remove the temporary name specific directory
256    rm -r "$name_dir"
257
258    echo "$($TIME_STAMP)" "Report is available in $dump_dir"
259
260    if [ "$TMP_DIR" == "$dump_dir" ] || [ "$TMP_DIR/" == "$dump_dir" ]; then
261        return $SUCCESS
262    fi
263
264    #copy the compressed tar file into the destination
265    if ! cp "$ARCHIVE_PATH" "$dump_dir"; then
266        echo "Failed to copy the $ARCHIVE_PATH to $dump_dir"
267        rm "$ARCHIVE_PATH"
268        return $INTERNAL_FAILURE
269    fi
270
271    #Remove the temporary copy of the file
272    rm "$ARCHIVE_PATH"
273}
274
275# @brief Main function
276function main()
277{
278    #initialize the global variables and
279    #create temporary storage locations
280    initialize
281    result=$?
282    if [[ ${result} -ne $SUCCESS ]]; then
283        echo "$($TIME_STAMP)" "Error: Failed to initialize, Exiting"
284        exit;
285    fi
286
287    #Initialize the summary log
288    init_summary
289
290    #collect data based on the type.
291    collect_data
292
293    package  #package the dump
294    result=$?
295    if [[ ${result} -ne $SUCCESS ]]; then
296        echo "$($TIME_STAMP)" "Error: Failed to package, Exiting"
297    else
298        echo "$($TIME_STAMP)" "Successfully completed"
299        exit;
300    fi
301}
302
303TEMP=$(getopt -o n:d:i:t:s:p:vqh \
304        --long name:,dir:,dumpid:,type:,size:,path:,verbose,quiet,help \
305    -- "$@")
306
307# shellcheck disable=SC2181
308# Common getopt usage, check exit status after capturing output
309if [ $? -ne 0 ]
310then
311    echo "Error: Invalid options"
312    exit 1
313fi
314
315eval set -- "$TEMP"
316
317while [[ $# -gt 1 ]]; do
318    key="$1"
319    case $key in
320        -n|--name)
321            name=$2
322            shift 2 ;;
323        -d|--dir)
324            dump_dir=$2
325            shift 2 ;;
326        -i|--dumpid)
327            dump_id=$2
328            shift 2 ;;
329        -t|--type)
330            dump_type=$2
331            shift 2 ;;
332        -s|--size)
333            dump_size=$2
334            shift 2 ;;
335        -p|--path)
336            optional_path=$2
337            shift 2 ;;
338        -v|--verbose)
339            verbose=$TRUE
340            shift ;;
341        -q|--quiet)
342            quiet=$TRUE
343            shift ;;
344        -h|--help)
345            echo "$help"
346            exit ;;
347        *) # unknown option
348            log_error "Unknown argument: $1"
349            log_info "$help"
350            exit 1 ;;
351    esac
352done
353
354main #main program
355exit $?
356