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