Fix shellcheck issue with quoting
[grml-hwinfo.git] / grml-hwinfo
1 #!/bin/bash
2 # Filename:      grml-hwinfo
3 # Purpose:       get hardware information
4 # Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
5 # Bug-Reports:   see http://grml.org/bugs/
6 # License:       This file is licensed under the GPL v2.
7 ################################################################################
8 # Notice: Some ideas have been taken from
9 # http://club.black.co.at/david/hwdb/infodump
10 # by David Schmitt <david@schmitt.edv-bus.at>
11 ################################################################################
12
13 # variables
14 UNAME="$(uname -r)"
15 PN="$(basename "$0")"
16 [ -n "$WORKING_DIR" -a -d "$WORKING_DIR" ] || WORKING_DIR=$(pwd)
17 VERSION='***UNRELEASED***'
18
19 # data collection should not be affected by user locale
20 export LANG=C
21 export LC_ALL=C
22
23 TIMESTAMP='+%F--%H-%M-%S-%Z'
24 DATE="$(date $TIMESTAMP)"
25
26 # defaults
27 GENERATE_FILE='1'
28 GENERATE_DIRECTORY=''
29 _opt_output_directory=false
30 _opt_output_file=false
31 _opt_quiet=false
32 _opt_force=false
33
34 usage() {
35   echo "
36   This tool collects information of the hardware it is being executed on.
37   It can be executed as normal user to collect some basic information or
38   (recommended) with root permissions to collect more system information.
39   If executed without any options a file named grml-hwinfo-TIMESTAMP.tar.bz2
40   storing all collected information is created in the current working directory.
41
42   Options:
43
44   -b, --both                 Create directory + file grml-hwinfo-TIMESTAMP.tar.bz2
45   -d, --directory            Create grml-hwinfo-TIMESTAMP as a directory (no file)
46   -f, --file                 Create grml-hwinfo-TIMESTAMP.tar.bz2 [default action]
47   -h, --help                 Display this help message
48   -q, --quiet                Don't display informational text (useful for cron usage)
49   --force                    Don't abort but overwrite possibly existing output file
50   --output-directory <dir>   Store output files in specified directory
51   --output-file <file>       Store output in specified filename (tar.XX format)
52   "
53 }
54
55 CMDLINE_OPTS=output-directory:,output-file:,both,directory,file,help,quiet,force
56 _opt_temp=$(getopt --name grml-hwinfo -o +bdfhq --long $CMDLINE_OPTS -- "$@")
57 if [ $? -ne 0 ]; then
58   echo "Try 'grml-hwinfo --help' for more information." >&2
59   exit 1
60 fi
61 eval set -- "$_opt_temp"
62
63 while :; do
64   case "$1" in
65   --help|-h)
66     usage ; exit 0
67     ;;
68   --output-directory)
69     shift; OUTDIRNAME="$1"
70     GENERATE_DIRECTORY='1'
71     _opt_output_directory=true
72     $_opt_output_file && GENERATE_FILE='1' || GENERATE_FILE=''
73     ;;
74   --output-file)
75     shift; OUTFILE="$1"
76     GENERATE_FILE='1'
77     _opt_output_file=true
78     $_opt_output_directory && GENERATE_DIRECTORY='1' || GENERATE_DIRECTORY=''
79     ;;
80   -d|--directory)
81     GENERATE_DIRECTORY='1'
82     GENERATE_FILE=''
83     ;;
84   -f|--file)
85     GENERATE_DIRECTORY=''
86     GENERATE_FILE='1'
87     ;;
88   -b|--both)
89     GENERATE_DIRECTORY='1'
90     GENERATE_FILE='1'
91     ;;
92   -q|--quiet)
93     _opt_quiet=true
94     ;;
95   --force)
96     _opt_force=true
97     ;;
98   --)
99     shift; break
100     ;;
101   *)
102     echo "Internal getopt error!" >&2
103     exit 1
104     ;;
105   esac
106   shift
107 done
108
109 if ! $_opt_quiet ; then
110   echo "$PN ${VERSION} - collect hardware information"
111 fi
112
113 # Generate output/temporary directory name & path, and output file path
114 [ -n "$OUTDIRNAME" ] || OUTDIRNAME="grml-hwinfo-${DATE}"
115 OUTDIR="${WORKING_DIR}/${OUTDIRNAME}"
116 if $_opt_force ; then
117   mkdir -p "${OUTDIR}"
118 else
119   mkdir "${OUTDIR}" || { echo "Directory '${OUTDIR}' already exists, aborting." >&2 ; exit 1; }
120 fi
121
122 if [ -n "$GENERATE_FILE" ] ; then
123   [ -n "$OUTFILE" ] && OUTFILE_="$OUTFILE" || OUTFILE_="${OUTDIR}.tar.bz2"
124   if ! $_opt_force ; then
125     [ -e "${OUTFILE_}" ] && { echo "File '${OUTFILE_}' already exists, aborting." >&2 ; rm -r "${OUTDIR}"; exit 1; }
126   fi
127   OUTFILE=${OUTFILE_}
128   touch "${OUTFILE}"
129 fi
130
131 if [ "$(id -u)" != "0" ] ; then
132   NOTROOT=1
133   $_opt_quiet || echo "W: Running without root permissions. Not all data will be collected."
134 fi
135
136 # check whether a binary is available and executable
137 exectest() {
138   if [ -z "$1" ] ; then
139     echo 'Usage: exectest <binary>'>&2
140     return 1
141   else
142     if test -e "$(which "$1")" ; then
143       return 0
144     else
145       if ! grep -q "^$1"'$' missing_tools 2>/dev/null ; then
146         $_opt_quiet || echo "$1" >> missing_tools
147       fi
148       return 1
149     fi
150   fi
151 }
152
153 # based on https://github.com/faiproject/fai/blob/master/lib/fai-disk-info
154 checkdisk() {
155   local isdisk device
156   while read _ _ _ device _ ; do
157     isdisk=1
158     # skip CDROMs
159     [ "$(stat -c %G /dev/"${device}")" = "disk" ] || isdisk=0
160     [ "$isdisk" -eq 1 ] && echo "$device"
161   done
162 }
163
164 disk_info() {
165   # the variable holds a newline separated list of devices
166   disklist=$(egrep ' etherd/e[[:digit:]]+\.[[:digit:]]+\b| i2o/hd.+\b| cciss/c.+d.+\b| ida/c.+d.+\b| rd/c.+d.+\b| fio.\b| hd.\b| sd[a-z]{1,2}\b|/disc\b| vd.\b| xvd.\b' /proc/partitions | checkdisk)
167 }
168
169
170 cd "${OUTDIR}" || exit 1
171 (
172   if ! $_opt_quiet ; then
173     [ -n "$GENERATE_FILE" ]      && echo "Output file:      $OUTFILE"
174     [ -n "$GENERATE_DIRECTORY" ] && echo "Output directory: $OUTDIR"
175     echo
176     echo "This might take a few seconds/minutes. Please be patient..."
177   fi
178
179   # some sysinfo
180   date > ./date
181   if [ -r /etc/grml_version ] ; then
182      cat /etc/grml_version > grml_version
183   fi
184   if [ -r /etc/debian_version ] ; then
185      cat /etc/debian_version > debian_version
186   fi
187   uname -a > ./uname
188
189   # disks / devices
190   [ -f /proc/scsi/scsi ] && cat /proc/scsi/scsi > scsi
191   exectest lspci && lspci -nn > ./lspci
192   cat /proc/partitions > partitions
193   find /proc/ide/ -name geometry -exec grep . {} \; > proc_ide 2>/dev/null
194   df -h > ./df 2>/dev/null
195   for i in free lsmod mount lsdev lspnp lsusb ; do
196     exectest $i && $i > ./$i
197   done
198   swapon -s > ./swapon 2>./swapon.error
199
200   # proc stuff
201   for i in cpuinfo interrupts cmdline devices dma fb iomem ioports \
202     mdstat meminfo modules mtrr pci version ; do
203     [ -r /proc/$i ] && cat /proc/$i > proc_$i
204   done
205   exectest sysdump  && sysdump > ./sysdump 2>./sysdump.error
206
207   # log
208   dmesg > dmesg.cur
209
210   # hwinfo
211   exectest discover && discover -v --type-summary --enable-bus all > ./discover 2>./discover.error
212   exectest hwinfo   && hwinfo log=hwinfo
213   exectest numactl  && numactl --hardware > ./numactl
214   exectest x86info  && x86info > ./x86info 2>./x86info.error
215   exectest lscpu    && lscpu > ./lscpu
216
217   # net stuff, net-tools:
218   exectest ifconfig && ifconfig -v -a > ./ifconfig
219   exectest route    && route -n       > ./route
220
221   # net stuff, iproute:
222   exectest ip && ip addrlabel list > ip_addrlabel
223   exectest ip && ip addr show      > ip_addr
224   exectest ip && ip link show      > ip_link
225   exectest ip && ip maddr show     > ip_maddr
226   exectest ip && ip mroute show    > ip_mroute
227   exectest ip && ip mrule show     > ip_mrule
228   exectest ip && ip neigh show     > ip_neigh
229   exectest ip && ip netns list     > ip_netns
230   exectest ip && ip ntable show    > ip_ntable
231   exectest ip && ip route show     > ip_route
232   exectest ip && if [ -r /etc/iproute2/rt_tables ] ; then
233                    grep -v '^#' /etc/iproute2/rt_tables | while read table _ ; do
234                      ip route show table "${table}" > "ip_route_table_${table}"
235                    done
236                  fi
237   exectest ip && ip rule show      > ip_rule
238   exectest ip && ip tunnel show    > ip_tunnel
239   exectest ip && ip tuntap show    > ip_tuntap
240
241   # software
242   if exectest dpkg ; then
243     dpkg --get-selections   > dpkg_get_selections
244     COLUMNS=300 dpkg --list > dpkg_list
245   fi
246
247   # power management
248   exectest laptop-detect  && laptop-detect >/dev/null 2>/dev/null && echo "0" > laptop_detected
249   if [ -r /proc/acpi/info ] ; then
250     cat /proc/acpi/info > acpi_info
251   fi
252
253   exectest acpi && acpi > ./acpi 2>acpi.error && acpi -v > ./acpi.version
254   [ -r /proc/apm/ ] && apm > ./apm
255
256   if exectest mcelog ; then
257     mcelog > ./mcelog 2>./mcelog.error
258   fi
259
260   # kernel stuff
261   if [ -r /proc/config.gz ] ; then
262     zcat /proc/config.gz > kernelconfig
263   else
264     [ -r "/boot/config-${UNAME}" ] && cat "/boot/config-${UNAME}" > kernelconfig
265   fi
266
267   exectest dpkg && COLUMNS=1000 dpkg -l "linux-image-${UNAME}" 2>running_kernel.error \
268            | grep "linux-image-${UNAME}" | tr -s ' ' > running_kernel 2>>running_kernel.error
269   dpkg -S "/boot/vmlinuz-$(uname -r)" >> running_kernel 2>>running_kernel.error
270
271   # X stuff
272   if [ -n "${DISPLAY}" ] ; then
273     exectest xviddetect  && xviddetect         > ./xviddetect
274     exectest xvidtune    && xvidtune -show     > ./xdivtune
275     exectest xrandr      && xrandr             > ./xrandr
276     exectest xdpyinfo    && xdpyinfo           > ./xdpyinfo
277     X -version > x_version 2>&1
278   fi
279
280   for i in Xorg.0.log Xorg.7.log Xorg.8.log XFree86.0.log XFree86.7.log XFree86.8.log dmesg ; do
281     cp /var/log/$i log_$i 2>/dev/null
282   done
283
284   cp /etc/X11/xorg.conf    xorg.conf    2>/dev/null
285   cp /etc/modules          modules      2>/dev/null
286   cp /etc/X11/XF86Config-4 XF86Config-4 2>/dev/null
287
288   # as root:
289   if [ -n "$NOTROOT" ] ; then
290     echo "not running as root" > root
291   else
292     echo "running as root" > root
293     disk_info
294     exectest sfdisk     && sfdisk -d > ./sfdisk 2>./sfdisk.error
295     exectest dmidecode  && dmidecode > ./dmidecode
296
297     exectest dconf && dconf -o dconf
298
299     if exectest mcelog ; then
300       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
301     fi
302
303     if exectest edac-util ; then
304       edac-util > ./edac-util 2>./edac-util.error
305       edac-util --report=full > edac-util_report 2>edac-util_report.error
306     fi
307
308     if exectest decode-dimms ; then
309       decode-dimms > ./decode-dimms 2>./decode-dimms.error
310     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
311       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
312     fi
313
314     # proxmox
315     exectest qm && qm list > ./qm 2>./qm.error
316     # libvirt
317     exectest virsh && virsh list >./virsh 2>./virsh.error
318     # openvz
319     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
320     # vserver
321     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
322
323     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
324
325     # LVM
326     exectest pvs && pvs > ./pvs 2>./pvs.error
327     exectest vgs && vgs > ./vgs 2>./vgs.error
328     exectest lvs && lvs > ./lvs 2>./lvs.error
329     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
330
331     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
332     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
333     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
334
335     # iSCSI
336     if exectest iscsiadm ; then
337       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
338       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
339       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
340       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
341       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
342       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
343     fi
344
345     if exectest lsscsi ; then
346       lsscsi    > ./lsscsi 2>./lsscsi.error
347       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
348     fi
349
350     for disk in $disklist; do
351       if exectest smartctl ; then
352         echo -e "smartctl -a /dev/${disk}:\n" >> smartctl
353         smartctl -a "/dev/$disk" >> ./smartctl
354         echo -e "\n\n" >> ./smartctl
355       fi
356
357       if exectest hdparm ; then
358         echo -e "hdparm -iv /dev/${disk}:\n" >> hdparm
359         hdparm -iv "/dev/$disk" >> ./hdparm
360         echo -e "\n\n" >> hdparm
361       fi
362
363       if exectest fdisk ; then
364         echo -e "fdisk -lu /dev/${disk}:\n" >> fdisk
365         fdisk -lu "/dev/$disk" >> ./fdisk 2>> ./fdisk.error
366         echo -e "\n\n" >> fdisk
367       fi
368
369       if exectest parted ; then
370         echo -e "parted -s /dev/${disk}:\n" >> parted
371         parted -s "/dev/$disk" print >> ./parted
372         echo -e "\n\n" >> parted
373       fi
374
375       if exectest sdparm ; then
376         echo -e "sdparm --all --long /dev/${disk}:\n" >> sdparm
377         sdparm --all --long "/dev/$disk" >> ./sdparm
378         echo -e "\n\n" >> sdparm
379       fi
380
381       if exectest sg_inq ; then
382         echo -e "sg_inq /dev/${disk}:\n" >> sg_inq
383         sg_inq "/dev/$disk" >> ./sg_inq
384         echo -e "\n\n" >> sg_inq
385       fi
386
387       file -s "/dev/${disk}"?* | grep -v ": empty" >> file_disk
388     done
389   fi
390 )
391
392 # get rid of empty files
393 for file in *.error ; do
394   test -s "$file" || rm -- "$file"
395 done
396
397 $_opt_quiet || echo
398
399 cd "${WORKING_DIR}"
400
401 # create tarball
402 if [ -n "$GENERATE_FILE" ] ; then
403   tar acf "${OUTFILE}" "${OUTDIRNAME}"
404   if ! $_opt_quiet ; then
405     # shellcheck disable=SC2012
406     [ -r "$OUTFILE" ] && echo "$OUTFILE ($(ls -ahl -- "$OUTFILE" | awk '{print $5}')) has been generated."
407   fi
408 fi
409
410 # remove (temporary) output directory if needed, else keep it, as it doubles
411 # as the real output directory.
412 if [ -z "$GENERATE_DIRECTORY" ] ; then
413   rm -r "${OUTDIR}"
414 else
415   if ! $_opt_quiet ; then
416     [ -r "${OUTDIR}" ] && echo "${OUTDIR} has been generated."
417   fi
418 fi
419
420 exit 0
421
422 ## END OF FILE##################################################################