1#! /bin/bash
2
3help=$(cat << EOF
4        dreport creates an archive(xz compressed) 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        -V, --version         Output version information.
34        -q, —-quiet           Only log fatal errors to stderr
35        -h, —-help            Display this help and exit.
36EOF
37)
38
39#CONSTANTS
40declare -rx TRUE=1
41declare -rx FALSE=0
42declare -rx UNLIMITED="unlimited"
43declare -rx SUMMARY_DUMP="summary"
44declare -rx TYPE_USER="user"
45declare -rx TYPE_CORE="core"
46declare -rx TYPE_ELOG="elog"
47declare -rx TYPE_CHECKSTOP="checkstop"
48declare -rx TYPE_RAMOOPS="ramoops"
49declare -rx SUMMARY_LOG="summary.log"
50declare -rx DREPORT_LOG="dreport.log"
51declare -rx TMP_DIR="/tmp"
52declare -rx EPOCHTIME=$(date +"%s")
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. $DREPORT_INCLUDE/functions
84
85# @brief Initiate data collection based on the type.
86# @return 0 on success, error code otherwise
87function collect_data()
88{
89    case $dump_type in
90        $TYPE_USER)
91            ;;
92        $TYPE_CORE)
93            log_summary "Core: $optional_path"
94            set_core_pid
95            ;;
96        $TYPE_RAMOOPS)
97            log_summary "Ramoops: $optional_path"
98            ;;
99        $TYPE_ELOG)
100            log_summary "ELOG: $optional_path"
101            elog_id=$(basename "$optional_path")
102            set_elog_pid
103            ;;
104        $TYPE_CHECKSTOP)
105            log_summary "CHECKSTOP: $optional_path"
106            elog_id=$(basename "$optional_path")
107            set_elog_pid
108            ;;
109
110        $SUMMARY_DUMP)
111            #No data collection is required.
112            return
113            ;;
114        *) # unknown option
115            log_error "Skipping: Unknown dump type: $dump_type"
116            return
117            ;;
118    esac
119
120    plugin_path=$DREPORT_SOURCE/$PLUGIN$dump_type.d
121
122    # check plugin directory for this dump type?
123    if [ ! -d $plugin_path ]; then
124        log_error "$plugin_path does not exist, skipping dump collection"
125        return 0
126    fi
127
128    #Executes plugins based on the type.
129    for i in $plugin_path/* ; do
130        $i
131    done
132}
133
134# @brief set pid by reading information from the optional path.
135#        dreport "core" type user provides core file as optional path parameter.
136#        As per coredump source code systemd-coredump uses below format
137#        https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c
138#        /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “.
139#        <process ID>.%s000000"
140function set_core_pid()
141{
142    #Escape bash characters in file name
143    file=$(printf %q "$optional_path")
144
145    #matching systemd-coredump core file format.
146    pid=$(echo $file | awk -F . '{ print $5}')
147}
148
149# @brief set elog pid by reading _PID information from the elog d-bus object.
150#        _PID information is stored  elog Additional data field
151#        Data format  "_PID=<pid>"
152function set_elog_pid()
153{
154    additional_data=$(busctl get-property xyz.openbmc_project.Logging \
155            $optional_path \
156            xyz.openbmc_project.Logging.Entry \
157        AdditionalData)
158
159    #read _PID data.
160    if [ ! -z "$additional_data" ]; then
161        pid=$(echo $additional_data | \
162            awk -F _PID= '{ print ($2+0)}')
163    fi
164}
165
166# @brief Initial version of the summary log
167function init_summary()
168{
169    log_summary "Name:          $name.tar.xz"
170    log_summary "Epochtime:     $EPOCHTIME"
171    log_summary "ID:            $dump_id"
172    log_summary "Type:          $dump_type"
173}
174
175# @brief Check the validity of user inputs and initialize global
176#        variables. Create directory for temporary data collection
177# @return 0 on success, error code otherwise
178
179function initialize()
180{
181    #Dump file name
182    if [ -z $name ]; then
183        name=$"obmcdump_"$dump_id"_$EPOCHTIME"
184    fi
185
186    #Create temporary data directory.
187    mkdir -p "$TMP_DIR/$name"
188    if [ $? -ne 0 ]; 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    mkdir -p "$dump_dir"
228    if [ $? -ne 0 ]; then
229        log_error "Could not create the destination directory $dump_dir"
230        dest_dir=$TMP_DIR
231    fi
232
233    #tar and compress the files.
234    if [ -f "$HEADER_EXTENSION" ]; then
235        tar -Jcf "$name_dir.tar.xz" -C \
236            $(dirname "$name_dir") $(basename "$name_dir")
237        echo "Adding Dump Header :"$HEADER_EXTENSION
238        ("$HEADER_EXTENSION")
239        cat "$name_dir.tar.xz" | tee -a "/tmp/dumpheader_$EPOCHTIME" > /dev/null
240        mv "/tmp/dumpheader_$EPOCHTIME" "$name_dir.tar.xz"
241    else
242        tar -Jcf "$name_dir.tar.xz" -C \
243            $(dirname "$name_dir") $(basename "$name_dir")
244    fi
245
246    if [ $? -ne 0 ]; then
247        echo $($TIME_STAMP) "Could not create the compressed tar file"
248        rm -r "$name_dir"
249        return $INTERNAL_FAILURE
250    fi
251
252    #remove the temporary name specific directory
253    rm -r "$name_dir"
254
255    echo $($TIME_STAMP) "Report is available in $dump_dir"
256
257    if [ "$TMP_DIR" == "$dump_dir" ] || [ "$TMP_DIR/" == "$dump_dir" ]; then
258        return $SUCCESS
259    fi
260
261    #copy the compressed tar file into the destination
262    cp "$name_dir.tar.xz" "$dump_dir"
263    if [ $? -ne 0 ]; then
264        echo "Failed to copy the $name_dir.tar.xz to $dump_dir"
265        rm "$name_dir.tar.xz"
266        return $INTERNAL_FAILURE
267    fi
268
269    #Remove the temporary copy of the file
270    rm "$name_dir.tar.xz"
271}
272
273# @brief Main function
274function main()
275{
276    #initialize the global variables and
277    #create temporary storage locations
278    initialize
279    result=$?
280    if [[ ${result} -ne $SUCCESS ]]; then
281        echo $($TIME_STAMP) "Error: Failed to initialize, Exiting"
282        exit;
283    fi
284
285    #Initialize the summary log
286    init_summary
287
288    #collect data based on the type.
289    collect_data
290
291    package  #package the dump
292    result=$?
293    if [[ ${result} -ne $SUCCESS ]]; then
294        echo $($TIME_STAMP) "Error: Failed to package, Exiting"
295    else
296        echo $($TIME_STAMP) "Successfully completed"
297        exit;
298    fi
299}
300
301TEMP=`getopt -o n:d:i:t:s:p:vVqh \
302    --long name:,dir:,dumpid:,type:,size:,path:,verbose,version,quiet,help \
303    -- "$@"`
304
305if [ $? -ne 0 ]
306then
307    echo "Error: Invalid options"
308    exit 1
309fi
310
311eval set -- "$TEMP"
312
313while [[ $# -gt 1 ]]; do
314    key="$1"
315    case $key in
316        -n|--name)
317            name=$2
318            shift 2 ;;
319        -d|--dir)
320            dump_dir=$2
321            shift 2 ;;
322        -i|--dumpid)
323            dump_id=$2
324            shift 2 ;;
325        -t|--type)
326            dump_type=$2
327            shift 2 ;;
328        -s|--size)
329            dump_size=$2
330            shift 2 ;;
331        -p|--path)
332            optional_path=$2
333            shift 2 ;;
334        -v|—-verbose)
335            verbose=$TRUE
336            shift ;;
337        -V|--version)
338            shift ;;
339        -q|—-quiet)
340            quiet=$TRUE
341            shift ;;
342        -h|--help)
343            echo "$help"
344            exit ;;
345        *) # unknown option
346            log_error "Unknown argument: $1"
347            log_info "$help"
348            exit 1 ;;
349    esac
350done
351
352main #main program
353exit $?
354