1#! /bin/bash
2
3help=$"
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.
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 SUMMARY_LOG="summary.log"
48declare -rx DREPORT_LOG="dreport.log"
49declare -rx TMP_DIR="/tmp"
50declare -rx EPOCHTIME=$(date +"%s")
51declare -rx TIME_STAMP="date -u"
52declare -rx PLUGIN="pl_"
53declare -rx DREPORT_SOURCE="/usr/share/dreport.d"
54declare -rx DREPORT_INCLUDE="$DREPORT_SOURCE/include.d"
55declare -rx ZERO="0"
56declare -rx JOURNAL_LINE_LIMIT="500"
57declare -rx HEADER_EXTENSION="$DREPORT_INCLUDE/gendumpheader"
58
59#Error Codes
60declare -rx SUCCESS="0"
61declare -rx INTERNAL_FAILURE="1"
62declare -rx RESOURCE_UNAVAILABLE="2"
63
64#VARIABLES
65declare -x name=""
66declare -x dump_dir="/tmp"
67declare -x dump_id="00000000"
68declare -x dump_type=$TYPE_USER
69declare -x verbose=$FALSE
70declare -x quiet=$FALSE
71declare -x dump_size="unlimited"
72declare -x name_dir=""
73declare -x optional_path=""
74declare -x dreport_log=""
75declare -x summary_log=""
76declare -x cur_dump_size=0
77declare -x pid=$ZERO
78declare -x elog_id=""
79
80#Source dreport common functions
81. $DREPORT_INCLUDE/functions
82
83# @brief Initiate data collection based on the type.
84# @return 0 on success, error code otherwise
85function collect_data()
86{
87    case $dump_type in
88        $TYPE_USER)
89            ;;
90        $TYPE_CORE)
91            log_summary "Core: $optional_path"
92            set_core_pid
93            ;;
94        $TYPE_ELOG)
95            log_summary "ELOG: $optional_path"
96            elog_id=$(basename "$optional_path")
97            set_elog_pid
98            ;;
99        $TYPE_CHECKSTOP)
100            log_summary "CHECKSTOP: $optional_path"
101            elog_id=$(basename "$optional_path")
102            set_elog_pid
103            ;;
104
105        $SUMMARY_DUMP)
106            #No data collection is required.
107            return
108            ;;
109        *) # unknown option
110            log_error "Skipping: Unknown dump type: $dump_type"
111            return
112            ;;
113    esac
114
115    plugin_path=$DREPORT_SOURCE/$PLUGIN$dump_type.d
116
117    # check plugin directory for this dump type?
118    if [ ! -d $plugin_path ]; then
119        log_error "$plugin_path does not exist, skipping dump collection"
120        return 0
121    fi
122
123    #Executes plugins based on the type.
124    for i in $plugin_path/* ; do
125       $i
126    done
127}
128
129# @brief set pid by reading information from the optional path.
130#        dreport "core" type user provides core file as optional path parameter.
131#        As per coredump source code systemd-coredump uses below format
132#        https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c
133#        /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “.
134#        <process ID>.%s000000"
135function set_core_pid()
136{
137    #Escape bash characters in file name
138    file=$(printf %q "$optional_path")
139
140    #matching systemd-coredump core file format.
141    pid=$(echo $file | awk -F . '{ print $5}')
142}
143
144# @brief set elog pid by reading _PID information from the elog d-bus object.
145#        _PID information is stored  elog Additional data field
146#        Data format  "_PID=<pid>"
147function set_elog_pid()
148{
149    additional_data=$(busctl get-property xyz.openbmc_project.Logging \
150                             $optional_path \
151                             xyz.openbmc_project.Logging.Entry \
152                             AdditionalData)
153
154    #read _PID data.
155    if [ ! -z "$additional_data" ]; then
156        pid=$(echo $additional_data | \
157                   awk -F _PID= '{ print ($2+0)}')
158    fi
159}
160
161# @brief Initial version of the summary log
162init_summary()
163{
164   log_summary "Name:          $name.tar.xz"
165   log_summary "Epochtime:     $EPOCHTIME"
166   log_summary "ID:            $dump_id"
167   log_summary "Type:          $dump_type"
168}
169
170# @brief Check the validity of user inputs and initialize global
171#        variables. Create directory for temporary data collection
172# @return 0 on success, error code otherwise
173
174function initialize()
175{
176    #Dump file name
177    if [ -z $name ]; then
178        name=$"obmcdump_"$dump_id"_$EPOCHTIME"
179    fi
180
181    #Create temporary data directory.
182    mkdir -p "$TMP_DIR/$name"
183    if [ $? -ne 0 ]; then
184        echo "Error: Failed to create the temporary directory."
185        return $RESOURCE_UNAVAILABLE;
186    fi
187
188    #name directory
189    name_dir="$TMP_DIR/$name"
190
191    #dreport log file
192    dreport_log="$name_dir/$DREPORT_LOG"
193
194    #summary log file
195    summary_log="$name_dir/$SUMMARY_LOG"
196
197    #Type
198    if [[ ! ($dump_type = $TYPE_USER || \
199             $dump_type = $TYPE_CORE || \
200             $dump_type = $TYPE_ELOG || \
201             $dump_type = $TYPE_CHECKSTOP) ]]; then
202       log_error "Invalid -type, Only summary log is available"
203       dump_type=$SUMMARY_DUMP
204    fi
205
206    #Size
207    #Check the input is integer.
208    if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then
209       #Converts in to bytes.
210       dump_size="$((dump_size * 1024))"
211    else
212       dump_size=$UNLIMITED
213    fi
214
215    return $SUCCESS
216}
217
218# @brief Packaging the dump and transferring to dump location.
219function package()
220{
221    mkdir -p "$dump_dir"
222    if [ $? -ne 0 ]; then
223        log_error "Could not create the destination directory $dump_dir"
224        dest_dir=$TMP_DIR
225    fi
226
227    #tar and compress the files.
228    if [ -f "$HEADER_EXTENSION" ]; then
229        tar -Jcf "$name_dir.tar.xz" -C \
230                 $(dirname "$name_dir") $(basename "$name_dir")
231        echo "Adding Dump Header :"$HEADER_EXTENSION
232        ("$HEADER_EXTENSION")
233        cat "$name_dir.tar.xz" | tee -a "/tmp/dumpheader_$EPOCHTIME" > /dev/null
234        mv "/tmp/dumpheader_$EPOCHTIME" "$name_dir.tar.xz"
235    else
236        tar -Jcf "$name_dir.tar.xz" -C \
237                 $(dirname "$name_dir") $(basename "$name_dir")
238    fi
239
240    if [ $? -ne 0 ]; then
241        echo $($TIME_STAMP) "Could not create the compressed tar file"
242        rm -r "$name_dir"
243        return $INTERNAL_FAILURE
244    fi
245
246    #remove the temporary name specific directory
247    rm -r "$name_dir"
248
249    echo $($TIME_STAMP) "Report is available in $dump_dir"
250
251    if [ "$TMP_DIR" == "$dump_dir" ] || [ "$TMP_DIR/" == "$dump_dir" ]; then
252       return $SUCCESS
253    fi
254
255    #copy the compressed tar file into the destination
256    cp "$name_dir.tar.xz" "$dump_dir"
257    if [ $? -ne 0 ]; then
258        echo "Failed to copy the $name_dir.tar.xz to $dump_dir"
259        rm "$name_dir.tar.xz"
260        return $INTERNAL_FAILURE
261    fi
262
263    #Remove the temporary copy of the file
264    rm "$name_dir.tar.xz"
265}
266
267# @brief Main function
268function main()
269{
270    #initialize the global variables and
271    #create temporary storage locations
272    initialize
273    result=$?
274    if [[ ${result} -ne $SUCCESS ]]; then
275        echo $($TIME_STAMP) "Error: Failed to initialize, Exiting"
276        exit;
277    fi
278
279    #Initialize the summary log
280    init_summary
281
282    #collect data based on the type.
283    collect_data
284
285    package  #package the dump
286    result=$?
287    if [[ ${result} -ne $SUCCESS ]]; then
288        echo $($TIME_STAMP) "Error: Failed to package, Exiting"
289    else
290        echo $($TIME_STAMP) "Successfully completed"
291        exit;
292    fi
293}
294
295TEMP=`getopt -o n:d:i:t:s:p:vVqh \
296      --long name:,dir:,dumpid:,type:,size:,path:,verbose,version,quiet,help \
297      -- "$@"`
298
299if [ $? -ne 0 ]
300then
301    echo "Error: Invalid options"
302    exit 1
303fi
304
305eval set -- "$TEMP"
306
307while [[ $# -gt 1 ]]; do
308    key="$1"
309    case $key in
310        -n|--name)
311            name=$2
312            shift 2;;
313        -d|--dir)
314            dump_dir=$2
315            shift 2;;
316        -i|--dumpid)
317            dump_id=$2
318            shift 2;;
319        -t|--type)
320            dump_type=$2
321            shift 2;;
322        -s|--size)
323            dump_size=$2
324            shift 2;;
325        -p|--path)
326            optional_path=$2
327            shift 2;;
328        -v|—-verbose)
329            verbose=$TRUE
330            shift;;
331        -V|--version)
332            shift;;
333        -q|—-quiet)
334            quiet=$TRUE
335            shift;;
336        -h|--help)
337            echo "$help"
338            exit;;
339        *) # unknown option
340            log_error "Unknown argument: $1"
341            log_info "$help"
342            exit 1;;
343    esac
344done
345
346main #main program
347exit $?
348