blob: 8af95b5d1a5a1a5afd532e4c969045b67a5f5726 [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 Othayothacf46e32017-08-09 07:15:18 -0500309# @brief Initiate data collection based on the type.
310# @return 0 on success, error code otherwise
311function collect_data()
312{
313 case $dump_type in
314 $TYPE_USER)
315 runlevel_user
316 capture_data "${command_list[@]}"
317 ;;
318 $TYPE_CORE)
319 set_pid
320 runlevel_core
321 capture_data "${command_list[@]}"
322 ;;
323 $SUMMARY_DUMP)
324 #No data collection is required.
325 ;;
326 *) # unknown option
327 log_error "Skipping: Unknown dump type: $dump_type"
328 ;;
329 esac
330}
331
Jayanth Othayoth47a14162017-08-09 07:10:39 -0500332# @brief get pid from the file
333# dreport "core" type user provides core file as optional file parameter.
334# As per coredump source code systemd-coredump uses below format
335# https://github.com/systemd/systemd/blob/master/src/coredump/coredump.c
336# /var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR “.
337# <process ID>.%s000000"
338# Added additional check to make sure that user provided in
339# systemd-coredump format.
340function set_pid()
341{
342 #from coredump.c file.
343 CORE_BASE_STRING="/var/lib/systemd/coredump/core"
344
345 #Escape bash characters in file name
346 file=$(printf %q "$optional_file")
347
348 string=$(echo $file | awk -F . '{ print $1}')
349 if [ "$CORE_BASE_STRING" = "$string" ]; then
350 #matching systemd-coredump core file format.
351 core_pid=$(echo $file | awk -F . '{ print $5}')
352 fi
353}
354
Jayanth Othayothf1e02d32017-08-09 07:06:45 -0500355# @brief Capture debug data based on the input command array.
356# and stores in to global temporary name specific location.
357# @param $1 Source array
358# @return 0 on success, error code otherwise
359function capture_data()
360{
361 source=("$@")
362
363 for ((i=0;i<${#source[@]};i+=1)); do
364 ${source[i]}
365 done
366
367 return 0
368}
369
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500370# @brief Calculate file or directory compressed size based on input
371# and check whether the size in the the allowed size limit.
372# Remove the file or directory from the name_dir
373# if the check fails.
374# @param $1 Source file or directory
375# @return 0 on success, error code if size exceeds the limit.
376# Limitation: compress and tar will have few bytes size difference
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500377function check_size()
378{
379 source=$1
380
381 #No size check required incase dump_size is set to unlimited
382 if [ $dump_size = $UNLIMITED ]; then
383 return 0
384 fi
385
386 #get the file or directory size
387 if [[ -d $source ]] && [[ -n $source ]]; then
388 tar -cf "$source.tar" -C \
389 $(dirname "$source") $(basename "$source")
390 size=$(stat -c%s "$source.tar")
391 rm "$source.tar"
392 else
393 size=$(stat -c%s "$source")
394 fi
395
396 if [ $((size + cur_dump_size)) -gt $dump_size ]; then
397 #Exceed the allowed limit,
398 #tar and compress the files and check the size
399 tar -Jcf "$name_dir.tar.xz" -C \
400 $(dirname "$name_dir") $(basename "$name_dir")
401 size=$(stat -c%s "$name_dir.tar.xz")
402 if [ $size -gt $dump_size ]; then
403 #Remove the the specific data from the name_dir and contniue
404 rm "$source" "$name_dir.tar.xz"
405 return $RESOURCE_UNAVAILABLE
406 else
407 rm "$name_dir.tar.xz"
408 fi
409 fi
410
411 #Remove the compressed file from the name directory
412 cur_dump_size=$((size + cur_dump_size))
413 return $SUCCESS
414}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500415
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500416# @brief Initial version of the summary log
417init_summary()
418{
419 log_summary "Name: $name.tar.xz"
420 log_summary "Epochtime: $EPOCHTIME"
421 log_summary "ID: $dump_id"
422 log_summary "Type: $dump_type"
423 log_summary "Optional file: $optional_file"
424}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500425
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500426# @brief Check the validity of user inputs and initialize global
427# variables. Create directory for temporary data collection
428# @return 0 on success, error code otherwise
429
430function initialize()
431{
432 #Dump file name
433 if [ -z $name ]; then
434 name=$"obmcdump_"$dump_id"_$EPOCHTIME"
435 fi
436
437 #Create temporary data directory.
438 mkdir -p "$TMP_DIR/$name"
439 if [ $? -ne 0 ]; then
440 echo "Error: Failed to create the temporary directory."
441 return $RESOURCE_UNAVAILABLE;
442 fi
443
444 #name directory
445 name_dir="$TMP_DIR/$name"
446
447 #dreport log file
448 dreport_log="$name_dir/$DREPORT_LOG"
449
450 #summary log file
451 summary_log="$name_dir/$SUMMARY_LOG"
452
453 #Type
454 if [[ !($dump_type = $TYPE_USER || $dump_type = $TYPE_CORE) ]]; then
455 log_error "Invalid -type, Only summary log is available"
456 dump_type=$SUMMARY_DUMP
457 fi
458
459 #Size
460 #Check the input is integer.
461 if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then
462 #Converts in to bytes.
463 dump_size="$((dump_size * 1024))"
464 else
465 dump_size=$UNLIMITED
466 fi
467
468 return $SUCCESS
469}
470
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500471# @brief Packaging the dump and transferring to dump location.
472function package()
473{
474 mkdir -p "$dump_dir"
475 if [ $? -ne 0 ]; then
476 log_error "Could not create the destination directory $dump_dir"
477 dest_dir=$TMP_DIR
478 fi
479
480 #TODO openbmc/openbmc#1506 Enable file level compression.
481 #tar and compress the files.
482 tar_file="$name_dir.tar.xz"
483 tar -Jcf "$tar_file" -C "$TMP_DIR" "$name"
484
485 #remove the temporary name specific directory
486 rm -r "$name_dir"
487
488 #check the file size is in the allowed limit
489 if [ $(stat -c%s "$tar_file") -gt $dump_size ]; then
490 echo "File size exceeds the limit allowed"
491 rm -rf "$TMP_DIR"
492 exit 1
493 #TODO openbmc/openbmc#1506 Revisit the error handling
494 fi
495
496 echo "Report is available in $dump_dir"
497
498 if [ "$TMP_DIR" == "$dump_dir" ]; then
499 return
500 fi
501
502 #copy the compressed tar file into the destination
503 cp "$tar_file" "$dump_dir"
504 if [ $? -ne 0 ]; then
505 echo "Failed to copy the $tar_file to $dump_dir"
506 return
507 else
508 rm -rf "$TMP_DIR"
509 fi
510}
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
563 echo $(date -u)" Error: Failed to initialize, Exiting"
564 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 Othayoth7b774872017-07-26 05:02:53 -0500574}
575
576TEMP=`getopt -o n:d:i:t:s:f:vVqh \
577 --long name:,dir:,dumpid:,type:,size:,file:,verbose,version,quiet,help \
578 -- "$@"`
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500579
580if [ $? -ne 0 ]
581then
582 echo "Error: Invalid options"
583 exit 1
584fi
585
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500586eval set -- "$TEMP"
587
588while [[ $# -gt 1 ]]; do
589 key="$1"
590 case $key in
591 -n|--name)
592 name=$2
593 shift 2;;
594 -d|--dir)
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500595 dump_dir=$2
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500596 shift 2;;
597 -i|--dumpid)
598 dump_id=$2
599 shift 2;;
600 -t|--type)
601 dump_type=$2
602 shift 2;;
603 -s|--size)
604 dump_size=$2
605 shift 2;;
606 -f|--file)
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500607 optional_file=$2
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500608 shift 2;;
609 -v|—-verbose)
610 verbose=$TRUE
611 shift;;
612 -V|--version)
613 shift;;
614 -q|—-quiet)
615 quiet=$TRUE
616 shift;;
617 -h|--help)
618 echo "$help"
619 exit;;
620 *) # unknown option
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500621 log_error "Unknown argument: $1"
622 log_info "$help"
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500623 exit 1;;
624 esac
Jayanth Othayoth9e95f4b2017-07-24 06:42:24 -0500625done
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500626
627main #main program
628exit $?