blob: 41b53d7d3e087e74d1294a1ba32536e072a174eb [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"
Jayanth Othayoth47a14162017-08-09 07:10:39 -0500339function set_pid()
340{
Jayanth Othayoth47a14162017-08-09 07:10:39 -0500341 #Escape bash characters in file name
342 file=$(printf %q "$optional_file")
343
Jayanth Othayoth9a56bfa2017-08-26 03:03:47 -0500344 #matching systemd-coredump core file format.
345 core_pid=$(echo $file | awk -F . '{ print $5}')
Jayanth Othayoth47a14162017-08-09 07:10:39 -0500346}
347
Jayanth Othayothf1e02d32017-08-09 07:06:45 -0500348# @brief Capture debug data based on the input command array.
349# and stores in to global temporary name specific location.
350# @param $1 Source array
351# @return 0 on success, error code otherwise
352function capture_data()
353{
354 source=("$@")
355
356 for ((i=0;i<${#source[@]};i+=1)); do
357 ${source[i]}
358 done
359
360 return 0
361}
362
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500363# @brief Calculate file or directory compressed size based on input
364# and check whether the size in the the allowed size limit.
365# Remove the file or directory from the name_dir
366# if the check fails.
367# @param $1 Source file or directory
368# @return 0 on success, error code if size exceeds the limit.
369# Limitation: compress and tar will have few bytes size difference
Jayanth Othayothc2ece2d2017-08-09 06:57:12 -0500370function check_size()
371{
372 source=$1
373
374 #No size check required incase dump_size is set to unlimited
375 if [ $dump_size = $UNLIMITED ]; then
376 return 0
377 fi
378
379 #get the file or directory size
380 if [[ -d $source ]] && [[ -n $source ]]; then
381 tar -cf "$source.tar" -C \
382 $(dirname "$source") $(basename "$source")
383 size=$(stat -c%s "$source.tar")
384 rm "$source.tar"
385 else
386 size=$(stat -c%s "$source")
387 fi
388
389 if [ $((size + cur_dump_size)) -gt $dump_size ]; then
390 #Exceed the allowed limit,
391 #tar and compress the files and check the size
392 tar -Jcf "$name_dir.tar.xz" -C \
393 $(dirname "$name_dir") $(basename "$name_dir")
394 size=$(stat -c%s "$name_dir.tar.xz")
395 if [ $size -gt $dump_size ]; then
396 #Remove the the specific data from the name_dir and contniue
397 rm "$source" "$name_dir.tar.xz"
398 return $RESOURCE_UNAVAILABLE
399 else
400 rm "$name_dir.tar.xz"
401 fi
402 fi
403
404 #Remove the compressed file from the name directory
405 cur_dump_size=$((size + cur_dump_size))
406 return $SUCCESS
407}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500408
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500409# @brief Initial version of the summary log
410init_summary()
411{
412 log_summary "Name: $name.tar.xz"
413 log_summary "Epochtime: $EPOCHTIME"
414 log_summary "ID: $dump_id"
415 log_summary "Type: $dump_type"
416 log_summary "Optional file: $optional_file"
417}
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500418
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500419# @brief Check the validity of user inputs and initialize global
420# variables. Create directory for temporary data collection
421# @return 0 on success, error code otherwise
422
423function initialize()
424{
425 #Dump file name
426 if [ -z $name ]; then
427 name=$"obmcdump_"$dump_id"_$EPOCHTIME"
428 fi
429
430 #Create temporary data directory.
431 mkdir -p "$TMP_DIR/$name"
432 if [ $? -ne 0 ]; then
433 echo "Error: Failed to create the temporary directory."
434 return $RESOURCE_UNAVAILABLE;
435 fi
436
437 #name directory
438 name_dir="$TMP_DIR/$name"
439
440 #dreport log file
441 dreport_log="$name_dir/$DREPORT_LOG"
442
443 #summary log file
444 summary_log="$name_dir/$SUMMARY_LOG"
445
446 #Type
447 if [[ !($dump_type = $TYPE_USER || $dump_type = $TYPE_CORE) ]]; then
448 log_error "Invalid -type, Only summary log is available"
449 dump_type=$SUMMARY_DUMP
450 fi
451
452 #Size
453 #Check the input is integer.
454 if [ "$dump_size" -eq "$dump_size" ] 2>/dev/null; then
455 #Converts in to bytes.
456 dump_size="$((dump_size * 1024))"
457 else
458 dump_size=$UNLIMITED
459 fi
460
461 return $SUCCESS
462}
463
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500464# @brief Packaging the dump and transferring to dump location.
465function package()
466{
467 mkdir -p "$dump_dir"
468 if [ $? -ne 0 ]; then
469 log_error "Could not create the destination directory $dump_dir"
470 dest_dir=$TMP_DIR
471 fi
472
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500473 #tar and compress the files.
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500474 tar -Jcf "$name_dir.tar.xz" -C \
475 $(dirname "$name_dir") $(basename "$name_dir")
476
477 if [ $? -ne 0 ]; then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500478 echo $($TIME_STAMP) "Could not create the compressed tar file"
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500479 rm -r "$name_dir"
480 return $INTERNAL_FAILURE
481 fi
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500482
483 #remove the temporary name specific directory
484 rm -r "$name_dir"
485
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500486 echo $($TIME_STAMP) "Report is available in $dump_dir"
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500487
488 if [ "$TMP_DIR" == "$dump_dir" ]; then
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500489 return $SUCCESS
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500490 fi
491
492 #copy the compressed tar file into the destination
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500493 cp "$name_dir.tar.xz" "$dump_dir"
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500494 if [ $? -ne 0 ]; then
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500495 echo "Failed to copy the $name_dir.tar.xz to $dump_dir"
496 rm "$name_dir.tar.xz"
497 return $INTERNAL_FAILURE
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500498 fi
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500499
500 #Remove the temporary copy of the file
501 rm "$name_dir.tar.xz"
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500502}
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500503
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500504# @brief log the error message
505# @param error message
506function log_error()
507{
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500508 echo $($TIME_STAMP) "ERROR: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500509 if ((quiet != TRUE)); then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500510 echo $($TIME_STAMP) "ERROR: $@" >&2
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500511 fi
512}
513
514# @brief log warning message
515# @param warning message
516function log_warning()
517{
518 if ((verbose == TRUE)); then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500519 echo $($TIME_STAMP) "WARNING: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500520 if ((quiet != TRUE)); then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500521 echo $($TIME_STAMP) "WARNING: $@" >&2
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500522 fi
523 fi
524}
525
526# @brief log info message
527# @param info message
528function log_info()
529{
530 if ((verbose == TRUE)); then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500531 echo $($TIME_STAMP) "INFO: $@" >> $dreport_log
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500532 if ((quiet != TRUE)); then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500533 echo $($TIME_STAMP) "INFO: $@" >&1
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500534 fi
535 fi
536}
537
538# @brief log summary message
539# @param message
540function log_summary()
541{
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500542 echo $($TIME_STAMP) "$@" >> $summary_log
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500543 if ((quiet != TRUE)); then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500544 echo $($TIME_STAMP) "$@" >&1
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500545 fi
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500546}
547
Jayanth Othayoth7b774872017-07-26 05:02:53 -0500548# @brief Main function
549function main()
550{
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500551 #initialize the global variables and
552 #create temporary storage locations
553 initialize
554 result=$?
555 if [[ ${result} -ne $SUCCESS ]]; then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500556 echo $($TIME_STAMP) "Error: Failed to initialize, Exiting"
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500557 exit;
558 fi
Jayanth Othayoth6d3ee1c2017-07-26 05:18:31 -0500559
Jayanth Othayothe20d5e02017-08-09 06:48:45 -0500560 #Initilize the summary log
561 init_summary
562
Jayanth Othayothacf46e32017-08-09 07:15:18 -0500563 #collect data based on the type.
564 collect_data
Jayanth Othayothff0699d2017-07-26 07:53:03 -0500565
Jayanth Othayoth230e9a32017-08-09 06:39:59 -0500566 package #package the dump
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500567 result=$?
568 if [[ ${result} -ne $SUCCESS ]]; then
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500569 echo $($TIME_STAMP) "Error: Failed to package, Exiting"
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500570 else
Jayanth Othayothea4b5e52017-08-09 07:31:24 -0500571 echo $($TIME_STAMP) "Sucessfully completed"
Jayanth Othayoth8d0446e2017-08-09 07:26:45 -0500572 exit;
573 fi
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 $?