blob: 43d2acb81f4305bb7f05d28b69e427f57cbe34f7 [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")
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -050048declare -r TIME_STAMP="date -u"
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050049
50#Error Codes
51declare -r SUCCESS="0"
52declare -r INTERNAL_FAILURE="1"
53declare -r RESOURCE_UNAVAILABLE="2"
Jayanth Othayoth7b774872017-07-26 05:02:53 -050054
55#VARIABLES
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050056declare -x name=""
Jayanth Othayothff0699d2017-07-26 07:53:03 -050057declare -x dump_dir="/tmp"
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050058declare -x dump_id="00000000"
59declare -x dump_type=$TYPE_USER
Jayanth Othayoth7b774872017-07-26 05:02:53 -050060declare -x verbose=$FALSE
61declare -x quiet=$FALSE
Jayanth Othayoth230e9a32017-08-09 06:39:59 -050062declare -x dump_size="unlimited"
63declare -x name_dir=""
64declare -x optional_file=""
65declare -x dreport_log=""
66declare -x summary_log=""
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -050067declare -x cur_dump_size=0
Jayanth Othayoth0862c482017-08-09 07:02:36 -050068declare -a command_list=("")
Jayanth Othayoth47a14162017-08-09 07:10:39 -050069declare -x core_pid="0"
Jayanth Othayoth0862c482017-08-09 07:02:36 -050070
71# @brief Initialize user type command array
72function runlevel_user()
73{
74command_list=(
75 get_fw_level
76 get_uname
77 get_uptime
78 get_disk_usage
79 bmc_state
80 host_state
81 chassis_state
82 get_host_info
83 get_obmc_console
84 get_cpuinfo
85 get_meminfo
86 get_top
87 get_esel
88 get_journal
89 get_failed_services
90 )
91}
92
Jayanth Othayothe61aee32017-08-09 07:05:06 -050093# @brief Initialize core type command array
94function runlevel_core()
95{
96command_list=(
97 move_optional_file
98 get_proc_journal
99 get_fw_level
100 get_uname
101 get_uptime
102 get_disk_usage
103 bmc_state
104 host_state
105 chassis_state
106 get_host_info
107 get_failed_services
108 get_obmc_console
109 get_cpuinfo
110 get_meminfo
111 get_top
112)
113}
114
Jayanth Othayoth0862c482017-08-09 07:02:36 -0500115function get_fw_level()
116{
117 desc="Firmware Release"
118 command="cat /etc/os-release"
119 copy_loc="firmware_release.log"
120 run_command "$command" "$copy_loc" "$desc"
121}
122
123function get_uname()
124{
125 desc="uname"
126 command="uname -a"
127 copy_loc="uname.log"
128 run_command "$command" "$copy_loc" "$desc"
129}
130
131function get_uptime()
132{
133 desc="uptime"
134 command="uptime"
135 copy_loc="uptime.log"
136 run_command "$command" "$copy_loc" "$desc"
137}
138
139function get_disk_usage()
140{
141 desc="Disk Usage"
142 command="df -hT"
143 copy_loc="disk_usage.log"
144 run_command "$command" "$copy_loc" "$desc"
145}
146
147function get_journal()
148{
149 desc="Journal log"
150 command="journalctl -o json-pretty -r"
151 copy_loc="journal.log"
152 run_command "$command" "$copy_loc" "$desc"
153}
154
Jayanth Othayothe61aee32017-08-09 07:05:06 -0500155function get_proc_journal()
156{
157 desc="Process Journal log"
158 command="journalctl -o verbose _PID=$core_pid"
159 copy_loc="proc_journal.log"
160 run_command "$command" "$copy_loc" "$desc"
161}
162
Jayanth Othayoth0862c482017-08-09 07:02:36 -0500163function get_host_info()
164{
165 desc="Host information"
166 command="hostnamectl status"
167 copy_loc="hostnamectl.log"
168 run_command "$command" "$copy_loc" "$desc"
169}
170
171function get_failed_services()
172{
173 desc="Failed Services"
174 command="systemctl --failed"
175 copy_loc="failed_services.log"
176 run_command "$command" "$copy_loc" "$desc"
177}
178
179function get_obmc_console()
180{
181 desc="OBMC Console"
182 command="cat /var/log/obmc-console.log"
183 copy_loc="obmc_console.log"
184 run_command "$command" "$copy_loc" "$desc"
185}
186
187function get_cpuinfo()
188{
189 desc="cpuinfo"
190 command="cat /proc/cpuinfo"
191 copy_loc="cpuinfo.log"
192 run_command "$command" "$copy_loc" "$desc"
193}
194
195function get_meminfo()
196{
197 desc="meminfo"
198 command="cat /proc/meminfo"
199 copy_loc="meminfo.log"
200 run_command "$command" "$copy_loc" "$desc"
201}
202
203function get_top()
204{
205 desc="top"
206 command="top -n 1 -b"
207 copy_loc="top.log"
208 run_command "$command" "$copy_loc" "$desc"
209}
210
211function bmc_state()
212{
213 desc="BMC State"
214 command="busctl get-property \
215 xyz.openbmc_project.State.BMC \
216 /xyz/openbmc_project/state/bmc0 \
217 xyz.openbmc_project.State.BMC \
218 CurrentBMCState"
219 copy_loc="BMCState.log"
220 run_command "$command" "$copy_loc" "$desc"
221}
222
223function host_state()
224{
225 desc="Host State"
226 command="busctl get-property \
227 xyz.openbmc_project.State.Host \
228 /xyz/openbmc_project/state/host0 \
229 xyz.openbmc_project.State.Host \
230 CurrentHostState"
231 copy_loc="HostState.log"
232 run_command "$command" "$copy_loc" "$desc"
233}
234
235function chassis_state()
236{
237 desc="Chassis State"
238 command="busctl get-property \
239 xyz.openbmc_project.State.Chassis \
240 /xyz/openbmc_project/state/chassis0 \
241 xyz.openbmc_project.State.Chassis \
242 CurrentPowerState"
243 copy_loc="HostState.log"
244 run_command "$command" "$copy_loc" "$desc"
245}
246
247function get_esel()
248{
249 desc="eSEL"
250
251 entries=$(busctl --list --no-pager tree \
252 xyz.openbmc_project.Logging | grep \
253 '/xyz/openbmc_project/logging/entry/')
254
255 #check for eSEL entries.
256 if [ -z "$entries" ]; then
257 log_info "No $desc entries"
258 return 0
259 fi
260
261 command="busctl --list --no-pager tree \
262 xyz.openbmc_project.Logging | grep \
263 '/xyz/openbmc_project/logging/entry/' \
264 | xargs -I {} busctl --verbose --no-pager \
265 call xyz.openbmc_project.Logging {} \
266 org.freedesktop.DBus.Properties GetAll s \
267 xyz.openbmc_project.Logging.Entry"
268 copy_loc="eSEL.log"
269 run_command "$command" "$copy_loc" "$desc"
270}
271
Jayanth Othayothe61aee32017-08-09 07:05:06 -0500272function move_optional_file()
273{
274 desc="Move Optional file"
275
276 mv $optional_file $name_dir
277 if [ $? -ne 0 ]; then
278 log_error "Failed to move file $optional_file"
279 return 1
280 fi
281 if check_size "$name_dir/$(basename "$optional_file")"; then
282 log_info "Moving file $file_name"
283 else
284 log_warning "Skipping $file_name move"
285 fi
286}
287
Jayanth Othayoth0862c482017-08-09 07:02:36 -0500288# @brief Run the requested command and save the output
289# into temporary location for successful size check
290
291function run_command()
292{
293 command="$1"
294 copy_loc="$2"
295 desc="$3"
296
297 eval $command >> "$name_dir/$copy_loc"
298 if [ $? -ne 0 ]; then
299 log_error "Failed to collect $desc"
300 return 1
301 fi
302
303 if check_size "$name_dir/$copy_loc"; then
304 log_info "Collected $desc"
305 else
306 log_warning "Skipping $desc"
307 fi
308}
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500309
Jayanth Othayothacf46e32017-08-09 07:15:18 -0500310# @brief Initiate data collection based on the type.
311# @return 0 on success, error code otherwise
312function collect_data()
313{
314 case $dump_type in
315 $TYPE_USER)
316 runlevel_user
317 capture_data "${command_list[@]}"
318 ;;
319 $TYPE_CORE)
320 set_pid
321 runlevel_core
322 capture_data "${command_list[@]}"
323 ;;
324 $SUMMARY_DUMP)
325 #No data collection is required.
326 ;;
327 *) # unknown option
328 log_error "Skipping: Unknown dump type: $dump_type"
329 ;;
330 esac
331}
332
Jayanth Othayoth47a14162017-08-09 07:10:39 -0500333# @brief get pid from the file
334# dreport "core" type user provides core file as optional file parameter.
335# As per coredump source code systemd-coredump uses below format
336# https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c
337# /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “.
338# <process ID>.%s000000"
339# Added additional check to make sure that user provided in
340# systemd-coredump format.
341function set_pid()
342{
343 #from coredump.c file.
344 CORE_BASE_STRING="/var/lib/systemd/coredump/core"
345
346 #Escape bash characters in file name
347 file=$(printf %q "$optional_file")
348
349 string=$(echo $file | awk -F . '{ print $1}')
350 if [ "$CORE_BASE_STRING" = "$string" ]; then
351 #matching systemd-coredump core file format.
352 core_pid=$(echo $file | awk -F . '{ print $5}')
353 fi
354}
355
Jayanth Othayothf1e02d32017-08-09 07:06:45 -0500356# @brief Capture debug data based on the input command array.
357# and stores in to global temporary name specific location.
358# @param $1 Source array
359# @return 0 on success, error code otherwise
360function capture_data()
361{
362 source=("$@")
363
364 for ((i=0;i<${#source[@]};i+=1)); do
365 ${source[i]}
366 done
367
368 return 0
369}
370
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500371# @brief Calculate file or directory compressed size based on input
372# and check whether the size in the the allowed size limit.
373# Remove the file or directory from the name_dir
374# if the check fails.
375# @param $1 Source file or directory
376# @return 0 on success, error code if size exceeds the limit.
377# Limitation: compress and tar will have few bytes size difference
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500378function check_size()
379{
380 source=$1
381
382 #No size check required incase dump_size is set to unlimited
383 if [ $dump_size = $UNLIMITED ]; then
384 return 0
385 fi
386
387 #get the file or directory size
388 if [[ -d $source ]] && [[ -n $source ]]; then
389 tar -cf "$source.tar" -C \
390 $(dirname "$source") $(basename "$source")
391 size=$(stat -c%s "$source.tar")
392 rm "$source.tar"
393 else
394 size=$(stat -c%s "$source")
395 fi
396
397 if [ $((size + cur_dump_size)) -gt $dump_size ]; then
398 #Exceed the allowed limit,
399 #tar and compress the files and check the size
400 tar -Jcf "$name_dir.tar.xz" -C \
401 $(dirname "$name_dir") $(basename "$name_dir")
402 size=$(stat -c%s "$name_dir.tar.xz")
403 if [ $size -gt $dump_size ]; then
404 #Remove the the specific data from the name_dir and contniue
405 rm "$source" "$name_dir.tar.xz"
406 return $RESOURCE_UNAVAILABLE
407 else
408 rm "$name_dir.tar.xz"
409 fi
410 fi
411
412 #Remove the compressed file from the name directory
413 cur_dump_size=$((size + cur_dump_size))
414 return $SUCCESS
415}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500416
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500417# @brief Initial version of the summary log
418init_summary()
419{
420 log_summary "Name: $name.tar.xz"
421 log_summary "Epochtime: $EPOCHTIME"
422 log_summary "ID: $dump_id"
423 log_summary "Type: $dump_type"
424 log_summary "Optional file: $optional_file"
425}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500426
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500427# @brief Check the validity of user inputs and initialize global
428# variables. Create directory for temporary data collection
429# @return 0 on success, error code otherwise
430
431function initialize()
432{
433 #Dump file name
434 if [ -z $name ]; then
435 name=$"obmcdump_"$dump_id"_$EPOCHTIME"
436 fi
437
438 #Create temporary data directory.
439 mkdir -p "$TMP_DIR/$name"
440 if [ $? -ne 0 ]; then
441 echo "Error: Failed to create the temporary directory."
442 return $RESOURCE_UNAVAILABLE;
443 fi
444
445 #name directory
446 name_dir="$TMP_DIR/$name"
447
448 #dreport log file
449 dreport_log="$name_dir/$DREPORT_LOG"
450
451 #summary log file
452 summary_log="$name_dir/$SUMMARY_LOG"
453
454 #Type
455 if [[ !($dump_type = $TYPE_USER || $dump_type = $TYPE_CORE) ]]; then
456 log_error "Invalid -type, Only summary log is available"
457 dump_type=$SUMMARY_DUMP
458 fi
459
460 #Size
461 #Check the input is integer.
462 if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then
463 #Converts in to bytes.
464 dump_size="$((dump_size * 1024))"
465 else
466 dump_size=$UNLIMITED
467 fi
468
469 return $SUCCESS
470}
471
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500472# @brief Packaging the dump and transferring to dump location.
473function package()
474{
475 mkdir -p "$dump_dir"
476 if [ $? -ne 0 ]; then
477 log_error "Could not create the destination directory $dump_dir"
478 dest_dir=$TMP_DIR
479 fi
480
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500481 #tar and compress the files.
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500482 tar -Jcf "$name_dir.tar.xz" -C \
483 $(dirname "$name_dir") $(basename "$name_dir")
484
485 if [ $? -ne 0 ]; then
486 echo "Could not create the compressed tar file"
487 rm -r "$name_dir"
488 return $INTERNAL_FAILURE
489 fi
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500490
491 #remove the temporary name specific directory
492 rm -r "$name_dir"
493
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500494 echo "Report is available in $dump_dir"
495
496 if [ "$TMP_DIR" == "$dump_dir" ]; then
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500497 return $SUCCESS
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500498 fi
499
500 #copy the compressed tar file into the destination
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500501 cp "$name_dir.tar.xz" "$dump_dir"
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500502 if [ $? -ne 0 ]; then
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500503 echo "Failed to copy the $name_dir.tar.xz to $dump_dir"
504 rm "$name_dir.tar.xz"
505 return $INTERNAL_FAILURE
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500506 fi
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500507
508 #Remove the temporary copy of the file
509 rm "$name_dir.tar.xz"
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500510}
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500511# @brief log the error message
512# @param error message
513function log_error()
514{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500515 echo "ERROR: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500516 if ((quiet != TRUE)); then
517 echo "ERROR: $@" >&2
518 fi
519}
520
521# @brief log warning message
522# @param warning message
523function log_warning()
524{
525 if ((verbose == TRUE)); then
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500526 echo "WARNING: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500527 if ((quiet != TRUE)); then
528 echo "WARNING: $@" >&2
529 fi
530 fi
531}
532
533# @brief log info message
534# @param info message
535function log_info()
536{
537 if ((verbose == TRUE)); then
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500538 echo "INFO: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500539 if ((quiet != TRUE)); then
540 echo "INFO: $@" >&1
541 fi
542 fi
543}
544
545# @brief log summary message
546# @param message
547function log_summary()
548{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500549 echo "$@" >> $summary_log
550 if ((quiet != TRUE)); then
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500551 echo "$@" >&1
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500552 fi
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500553}
554
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500555# @brief Main function
556function main()
557{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500558 #initialize the global variables and
559 #create temporary storage locations
560 initialize
561 result=$?
562 if [[ ${result} -ne $SUCCESS ]]; then
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500563 echo $(TIME_STAMP)" Error: Failed to initialize, Exiting"
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500564 exit;
565 fi
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500566
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500567 #Initilize the summary log
568 init_summary
569
Jayanth Othayothacf46e32017-08-09 07:15:18 -0500570 #collect data based on the type.
571 collect_data
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500572
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500573 package #package the dump
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500574 result=$?
575 if [[ ${result} -ne $SUCCESS ]]; then
576 echo $($TIME_STAMP)" Error: Failed to package, Exiting"
577 else
578 echo $($TIME_STAMP)" Sucessfully completed"
579 exit;
580 fi
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500581}
582
583TEMP=`getopt -o n:d:i:t:s:f:vVqh \
584 --long name:,dir:,dumpid:,type:,size:,file:,verbose,version,quiet,help \
585 -- "$@"`
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500586
587if [ $? -ne 0 ]
588then
589 echo "Error: Invalid options"
590 exit 1
591fi
592
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500593eval set -- "$TEMP"
594
595while [[ $# -gt 1 ]]; do
596 key="$1"
597 case $key in
598 -n|--name)
599 name=$2
600 shift 2;;
601 -d|--dir)
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500602 dump_dir=$2
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500603 shift 2;;
604 -i|--dumpid)
605 dump_id=$2
606 shift 2;;
607 -t|--type)
608 dump_type=$2
609 shift 2;;
610 -s|--size)
611 dump_size=$2
612 shift 2;;
613 -f|--file)
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500614 optional_file=$2
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500615 shift 2;;
616 -v|—-verbose)
617 verbose=$TRUE
618 shift;;
619 -V|--version)
620 shift;;
621 -q|—-quiet)
622 quiet=$TRUE
623 shift;;
624 -h|--help)
625 echo "$help"
626 exit;;
627 *) # unknown option
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500628 log_error "Unknown argument: $1"
629 log_info "$help"
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500630 exit 1;;
631 esac
Jayanth Othayoth9e95f4b2017-07-24 06:42:24 -0500632done
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500633
634main #main program
635exit $?