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