blob: 9f6afca618be2fab8906d0c056f82a8c5f023cf6 [file] [log] [blame]
Jayanth Othayoth9e95f4b2017-07-24 06:42:24 -05001#! /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.
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050017 Default output directory is /tmp
Jayanth Othayoth9e95f4b2017-07-24 06:42:24 -050018 -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".
23 Default type is "user" initiated.
24 -f, —-file <file> Optional file to be included in the archive.
25 Absolute path of the file must be passed as
26 parameter. This is useful to include application
27 core in the dump.
28 -s, --size <size> Maximum allowed size(in KB) of the archive.
29 Report will be truncated in case size exceeds
30 this limit. Default size is 500KB.
31 -v, —-verbose Increase logging verbosity.
32 -V, --version Output version information.
33 -q, —-quiet Only log fatal errors to stderr
34 -h, —-help Display this help and exit.
35"
36
Jayanth Othayoth7b774872017-07-26 05:02:53 -050037#CONSTANTS
38declare -r TRUE=1
39declare -r FALSE=0
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050040declare -r UNLIMITED="unlimited"
41declare -r SUMMARY_DUMP="summary"
42declare -r TYPE_USER="user"
43declare -r TYPE_CORE="core"
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -050044declare -r SUMMARY_LOG="summary.log"
45declare -r DREPORT_LOG="dreport.log"
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050046declare -r TMP_DIR="/tmp"
47declare -r EPOCHTIME=$(date +"%s")
48
49#Error Codes
50declare -r SUCCESS="0"
51declare -r INTERNAL_FAILURE="1"
52declare -r RESOURCE_UNAVAILABLE="2"
Jayanth Othayoth7b774872017-07-26 05:02:53 -050053
54#VARIABLES
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050055declare -x name=""
Jayanth Othayothff0699d2017-07-26 07:53:03 -050056declare -x dump_dir="/tmp"
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050057declare -x dump_id="00000000"
58declare -x dump_type=$TYPE_USER
Jayanth Othayoth7b774872017-07-26 05:02:53 -050059declare -x verbose=$FALSE
60declare -x quiet=$FALSE
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050061declare -x dump_size="unlimited"
62declare -x name_dir=""
63declare -x optional_file=""
64declare -x dreport_log=""
65declare -x summary_log=""
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -050066declare -x cur_dump_size=0
Jayanth Othayoth0862c482017-08-09 07:02:36 -050067declare -a command_list=("")
Jayanth Othayoth47a14162017-08-09 07:10:39 -050068declare -x core_pid="0"
Jayanth Othayoth0862c482017-08-09 07:02:36 -050069
70# @brief Initialize user type command array
71function runlevel_user()
72{
73command_list=(
74 get_fw_level
75 get_uname
76 get_uptime
77 get_disk_usage
78 bmc_state
79 host_state
80 chassis_state
81 get_host_info
82 get_obmc_console
83 get_cpuinfo
84 get_meminfo
85 get_top
86 get_esel
87 get_journal
88 get_failed_services
89 )
90}
91
Jayanth Othayothe61aee32017-08-09 07:05:06 -050092# @brief Initialize core type command array
93function runlevel_core()
94{
95command_list=(
96 move_optional_file
97 get_proc_journal
98 get_fw_level
99 get_uname
100 get_uptime
101 get_disk_usage
102 bmc_state
103 host_state
104 chassis_state
105 get_host_info
106 get_failed_services
107 get_obmc_console
108 get_cpuinfo
109 get_meminfo
110 get_top
111)
112}
113
Jayanth Othayoth0862c482017-08-09 07:02:36 -0500114function get_fw_level()
115{
116 desc="Firmware Release"
117 command="cat /etc/os-release"
118 copy_loc="firmware_release.log"
119 run_command "$command" "$copy_loc" "$desc"
120}
121
122function get_uname()
123{
124 desc="uname"
125 command="uname -a"
126 copy_loc="uname.log"
127 run_command "$command" "$copy_loc" "$desc"
128}
129
130function get_uptime()
131{
132 desc="uptime"
133 command="uptime"
134 copy_loc="uptime.log"
135 run_command "$command" "$copy_loc" "$desc"
136}
137
138function get_disk_usage()
139{
140 desc="Disk Usage"
141 command="df -hT"
142 copy_loc="disk_usage.log"
143 run_command "$command" "$copy_loc" "$desc"
144}
145
146function get_journal()
147{
148 desc="Journal log"
149 command="journalctl -o json-pretty -r"
150 copy_loc="journal.log"
151 run_command "$command" "$copy_loc" "$desc"
152}
153
Jayanth Othayothe61aee32017-08-09 07:05:06 -0500154function get_proc_journal()
155{
156 desc="Process Journal log"
157 command="journalctl -o verbose _PID=$core_pid"
158 copy_loc="proc_journal.log"
159 run_command "$command" "$copy_loc" "$desc"
160}
161
Jayanth Othayoth0862c482017-08-09 07:02:36 -0500162function get_host_info()
163{
164 desc="Host information"
165 command="hostnamectl status"
166 copy_loc="hostnamectl.log"
167 run_command "$command" "$copy_loc" "$desc"
168}
169
170function get_failed_services()
171{
172 desc="Failed Services"
173 command="systemctl --failed"
174 copy_loc="failed_services.log"
175 run_command "$command" "$copy_loc" "$desc"
176}
177
178function get_obmc_console()
179{
180 desc="OBMC Console"
181 command="cat /var/log/obmc-console.log"
182 copy_loc="obmc_console.log"
183 run_command "$command" "$copy_loc" "$desc"
184}
185
186function get_cpuinfo()
187{
188 desc="cpuinfo"
189 command="cat /proc/cpuinfo"
190 copy_loc="cpuinfo.log"
191 run_command "$command" "$copy_loc" "$desc"
192}
193
194function get_meminfo()
195{
196 desc="meminfo"
197 command="cat /proc/meminfo"
198 copy_loc="meminfo.log"
199 run_command "$command" "$copy_loc" "$desc"
200}
201
202function get_top()
203{
204 desc="top"
205 command="top -n 1 -b"
206 copy_loc="top.log"
207 run_command "$command" "$copy_loc" "$desc"
208}
209
210function bmc_state()
211{
212 desc="BMC State"
213 command="busctl get-property \
214 xyz.openbmc_project.State.BMC \
215 /xyz/openbmc_project/state/bmc0 \
216 xyz.openbmc_project.State.BMC \
217 CurrentBMCState"
218 copy_loc="BMCState.log"
219 run_command "$command" "$copy_loc" "$desc"
220}
221
222function host_state()
223{
224 desc="Host State"
225 command="busctl get-property \
226 xyz.openbmc_project.State.Host \
227 /xyz/openbmc_project/state/host0 \
228 xyz.openbmc_project.State.Host \
229 CurrentHostState"
230 copy_loc="HostState.log"
231 run_command "$command" "$copy_loc" "$desc"
232}
233
234function chassis_state()
235{
236 desc="Chassis State"
237 command="busctl get-property \
238 xyz.openbmc_project.State.Chassis \
239 /xyz/openbmc_project/state/chassis0 \
240 xyz.openbmc_project.State.Chassis \
241 CurrentPowerState"
242 copy_loc="HostState.log"
243 run_command "$command" "$copy_loc" "$desc"
244}
245
246function get_esel()
247{
248 desc="eSEL"
249
250 entries=$(busctl --list --no-pager tree \
251 xyz.openbmc_project.Logging | grep \
252 '/xyz/openbmc_project/logging/entry/')
253
254 #check for eSEL entries.
255 if [ -z "$entries" ]; then
256 log_info "No $desc entries"
257 return 0
258 fi
259
260 command="busctl --list --no-pager tree \
261 xyz.openbmc_project.Logging | grep \
262 '/xyz/openbmc_project/logging/entry/' \
263 | xargs -I {} busctl --verbose --no-pager \
264 call xyz.openbmc_project.Logging {} \
265 org.freedesktop.DBus.Properties GetAll s \
266 xyz.openbmc_project.Logging.Entry"
267 copy_loc="eSEL.log"
268 run_command "$command" "$copy_loc" "$desc"
269}
270
Jayanth Othayothe61aee32017-08-09 07:05:06 -0500271function move_optional_file()
272{
273 desc="Move Optional file"
274
275 mv $optional_file $name_dir
276 if [ $? -ne 0 ]; then
277 log_error "Failed to move file $optional_file"
278 return 1
279 fi
280 if check_size "$name_dir/$(basename "$optional_file")"; then
281 log_info "Moving file $file_name"
282 else
283 log_warning "Skipping $file_name move"
284 fi
285}
286
Jayanth Othayoth0862c482017-08-09 07:02:36 -0500287# @brief Run the requested command and save the output
288# into temporary location for successful size check
289
290function run_command()
291{
292 command="$1"
293 copy_loc="$2"
294 desc="$3"
295
296 eval $command >> "$name_dir/$copy_loc"
297 if [ $? -ne 0 ]; then
298 log_error "Failed to collect $desc"
299 return 1
300 fi
301
302 if check_size "$name_dir/$copy_loc"; then
303 log_info "Collected $desc"
304 else
305 log_warning "Skipping $desc"
306 fi
307}
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500308
Jayanth Othayoth47a14162017-08-09 07:10:39 -0500309# @brief get pid from the file
310# dreport "core" type user provides core file as optional file parameter.
311# As per coredump source code systemd-coredump uses below format
312# https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c
313# /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “.
314# <process ID>.%s000000"
315# Added additional check to make sure that user provided in
316# systemd-coredump format.
317function set_pid()
318{
319 #from coredump.c file.
320 CORE_BASE_STRING="/var/lib/systemd/coredump/core"
321
322 #Escape bash characters in file name
323 file=$(printf %q "$optional_file")
324
325 string=$(echo $file | awk -F . '{ print $1}')
326 if [ "$CORE_BASE_STRING" = "$string" ]; then
327 #matching systemd-coredump core file format.
328 core_pid=$(echo $file | awk -F . '{ print $5}')
329 fi
330}
331
Jayanth Othayothf1e02d32017-08-09 07:06:45 -0500332# @brief Capture debug data based on the input command array.
333# and stores in to global temporary name specific location.
334# @param $1 Source array
335# @return 0 on success, error code otherwise
336function capture_data()
337{
338 source=("$@")
339
340 for ((i=0;i<${#source[@]};i+=1)); do
341 ${source[i]}
342 done
343
344 return 0
345}
346
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500347# @brief Calculate file or directory compressed size based on input
348# and check whether the size in the the allowed size limit.
349# Remove the file or directory from the name_dir
350# if the check fails.
351# @param $1 Source file or directory
352# @return 0 on success, error code if size exceeds the limit.
353# Limitation: compress and tar will have few bytes size difference
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500354function check_size()
355{
356 source=$1
357
358 #No size check required incase dump_size is set to unlimited
359 if [ $dump_size = $UNLIMITED ]; then
360 return 0
361 fi
362
363 #get the file or directory size
364 if [[ -d $source ]] && [[ -n $source ]]; then
365 tar -cf "$source.tar" -C \
366 $(dirname "$source") $(basename "$source")
367 size=$(stat -c%s "$source.tar")
368 rm "$source.tar"
369 else
370 size=$(stat -c%s "$source")
371 fi
372
373 if [ $((size + cur_dump_size)) -gt $dump_size ]; then
374 #Exceed the allowed limit,
375 #tar and compress the files and check the size
376 tar -Jcf "$name_dir.tar.xz" -C \
377 $(dirname "$name_dir") $(basename "$name_dir")
378 size=$(stat -c%s "$name_dir.tar.xz")
379 if [ $size -gt $dump_size ]; then
380 #Remove the the specific data from the name_dir and contniue
381 rm "$source" "$name_dir.tar.xz"
382 return $RESOURCE_UNAVAILABLE
383 else
384 rm "$name_dir.tar.xz"
385 fi
386 fi
387
388 #Remove the compressed file from the name directory
389 cur_dump_size=$((size + cur_dump_size))
390 return $SUCCESS
391}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500392
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500393# @brief Initial version of the summary log
394init_summary()
395{
396 log_summary "Name: $name.tar.xz"
397 log_summary "Epochtime: $EPOCHTIME"
398 log_summary "ID: $dump_id"
399 log_summary "Type: $dump_type"
400 log_summary "Optional file: $optional_file"
401}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500402
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500403# @brief Check the validity of user inputs and initialize global
404# variables. Create directory for temporary data collection
405# @return 0 on success, error code otherwise
406
407function initialize()
408{
409 #Dump file name
410 if [ -z $name ]; then
411 name=$"obmcdump_"$dump_id"_$EPOCHTIME"
412 fi
413
414 #Create temporary data directory.
415 mkdir -p "$TMP_DIR/$name"
416 if [ $? -ne 0 ]; then
417 echo "Error: Failed to create the temporary directory."
418 return $RESOURCE_UNAVAILABLE;
419 fi
420
421 #name directory
422 name_dir="$TMP_DIR/$name"
423
424 #dreport log file
425 dreport_log="$name_dir/$DREPORT_LOG"
426
427 #summary log file
428 summary_log="$name_dir/$SUMMARY_LOG"
429
430 #Type
431 if [[ !($dump_type = $TYPE_USER || $dump_type = $TYPE_CORE) ]]; then
432 log_error "Invalid -type, Only summary log is available"
433 dump_type=$SUMMARY_DUMP
434 fi
435
436 #Size
437 #Check the input is integer.
438 if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then
439 #Converts in to bytes.
440 dump_size="$((dump_size * 1024))"
441 else
442 dump_size=$UNLIMITED
443 fi
444
445 return $SUCCESS
446}
447
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500448# @brief Packaging the dump and transferring to dump location.
449function package()
450{
451 mkdir -p "$dump_dir"
452 if [ $? -ne 0 ]; then
453 log_error "Could not create the destination directory $dump_dir"
454 dest_dir=$TMP_DIR
455 fi
456
457 #TODO openbmc/openbmc#1506 Enable file level compression.
458 #tar and compress the files.
459 tar_file="$name_dir.tar.xz"
460 tar -Jcf "$tar_file" -C "$TMP_DIR" "$name"
461
462 #remove the temporary name specific directory
463 rm -r "$name_dir"
464
465 #check the file size is in the allowed limit
466 if [ $(stat -c%s "$tar_file") -gt $dump_size ]; then
467 echo "File size exceeds the limit allowed"
468 rm -rf "$TMP_DIR"
469 exit 1
470 #TODO openbmc/openbmc#1506 Revisit the error handling
471 fi
472
473 echo "Report is available in $dump_dir"
474
475 if [ "$TMP_DIR" == "$dump_dir" ]; then
476 return
477 fi
478
479 #copy the compressed tar file into the destination
480 cp "$tar_file" "$dump_dir"
481 if [ $? -ne 0 ]; then
482 echo "Failed to copy the $tar_file to $dump_dir"
483 return
484 else
485 rm -rf "$TMP_DIR"
486 fi
487}
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500488# @brief log the error message
489# @param error message
490function log_error()
491{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500492 echo "ERROR: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500493 if ((quiet != TRUE)); then
494 echo "ERROR: $@" >&2
495 fi
496}
497
498# @brief log warning message
499# @param warning message
500function log_warning()
501{
502 if ((verbose == TRUE)); then
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500503 echo "WARNING: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500504 if ((quiet != TRUE)); then
505 echo "WARNING: $@" >&2
506 fi
507 fi
508}
509
510# @brief log info message
511# @param info message
512function log_info()
513{
514 if ((verbose == TRUE)); then
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500515 echo "INFO: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500516 if ((quiet != TRUE)); then
517 echo "INFO: $@" >&1
518 fi
519 fi
520}
521
522# @brief log summary message
523# @param message
524function log_summary()
525{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500526 echo "$@" >> $summary_log
527 if ((quiet != TRUE)); then
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500528 echo "$@" >&1
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500529 fi
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500530}
531
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500532# @brief Main function
533function main()
534{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500535 #initialize the global variables and
536 #create temporary storage locations
537 initialize
538 result=$?
539 if [[ ${result} -ne $SUCCESS ]]; then
540 echo $(date -u)" Error: Failed to initialize, Exiting"
541 exit;
542 fi
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500543
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500544 #Initilize the summary log
545 init_summary
546
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500547 #TODO Add Dump report generating script.
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500548
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500549 package #package the dump
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500550}
551
552TEMP=`getopt -o n:d:i:t:s:f:vVqh \
553 --long name:,dir:,dumpid:,type:,size:,file:,verbose,version,quiet,help \
554 -- "$@"`
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500555
556if [ $? -ne 0 ]
557then
558 echo "Error: Invalid options"
559 exit 1
560fi
561
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500562eval set -- "$TEMP"
563
564while [[ $# -gt 1 ]]; do
565 key="$1"
566 case $key in
567 -n|--name)
568 name=$2
569 shift 2;;
570 -d|--dir)
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500571 dump_dir=$2
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500572 shift 2;;
573 -i|--dumpid)
574 dump_id=$2
575 shift 2;;
576 -t|--type)
577 dump_type=$2
578 shift 2;;
579 -s|--size)
580 dump_size=$2
581 shift 2;;
582 -f|--file)
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500583 optional_file=$2
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500584 shift 2;;
585 -v|—-verbose)
586 verbose=$TRUE
587 shift;;
588 -V|--version)
589 shift;;
590 -q|—-quiet)
591 quiet=$TRUE
592 shift;;
593 -h|--help)
594 echo "$help"
595 exit;;
596 *) # unknown option
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500597 log_error "Unknown argument: $1"
598 log_info "$help"
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500599 exit 1;;
600 esac
Jayanth Othayoth9e95f4b2017-07-24 06:42:24 -0500601done
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500602
603main #main program
604exit $?