Reimplement disk_info() function
[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 if $_opt_output_directory ; then
116   OUTDIR="${OUTDIRNAME}"
117 else
118   OUTDIR="${WORKING_DIR}/${OUTDIRNAME}"
119 fi
120
121 if $_opt_force ; then
122   mkdir -p "${OUTDIR}"
123 else
124   mkdir "${OUTDIR}" || { echo "Directory '${OUTDIR}' already exists, aborting." >&2 ; exit 1; }
125 fi
126
127 if [ -n "$GENERATE_FILE" ] ; then
128   [ -n "$OUTFILE" ] && OUTFILE_="$OUTFILE" || OUTFILE_="${OUTDIR}.tar.bz2"
129   if ! $_opt_force ; then
130     [ -e "${OUTFILE_}" ] && { echo "File '${OUTFILE_}' already exists, aborting." >&2 ; rm -r "${OUTDIR}"; exit 1; }
131   fi
132   OUTFILE=${OUTFILE_}
133   touch "${OUTFILE}"
134 fi
135
136 if [ "$(id -u)" != "0" ] ; then
137   NOTROOT=1
138   $_opt_quiet || echo "W: Running without root permissions. Not all data will be collected."
139 fi
140
141 # check whether a binary is available and executable
142 exectest() {
143   if [ -z "$1" ] ; then
144     echo 'Usage: exectest <binary>'>&2
145     return 1
146   else
147     if test -e "$(which "$1")" ; then
148       return 0
149     else
150       if ! grep -q "^$1"'$' missing_tools 2>/dev/null ; then
151         $_opt_quiet || echo "$1" >> missing_tools
152       fi
153       return 1
154     fi
155   fi
156 }
157
158 disk_info() {
159   # the variable holds a newline separated list of disk block devices, excluding loopback and CD-ROM devices
160   disklist=$(lsblk -nd -o NAME -e 7,11)
161 }
162
163
164 cd "${OUTDIR}" || exit 1
165 (
166   if ! $_opt_quiet ; then
167     [ -n "$GENERATE_FILE" ]      && echo "Output file:      $OUTFILE"
168     [ -n "$GENERATE_DIRECTORY" ] && echo "Output directory: $OUTDIR"
169     echo
170     echo "This might take a few seconds/minutes. Please be patient..."
171   fi
172
173   # some sysinfo
174   date > ./date
175   if [ -r /etc/grml_version ] ; then
176      cat /etc/grml_version > grml_version
177   fi
178   if [ -r /etc/debian_version ] ; then
179      cat /etc/debian_version > debian_version
180   fi
181   uname -a > ./uname
182
183   # disks / devices
184   [ -f /proc/scsi/scsi ] && cat /proc/scsi/scsi > scsi
185   exectest lspci && lspci -nn > ./lspci
186   exectest lspci && lspci -vvnn > ./lspci_verbose
187   cat /proc/partitions > partitions
188   find /proc/ide/ -name geometry -exec grep . {} \; > proc_ide 2>/dev/null
189   df -h > ./df 2>/dev/null
190   for i in free lsmod mount lsdev lspnp ; do
191     exectest $i && $i > ./$i
192   done
193
194   if exectest lsusb ; then
195     lsusb    > ./lsusb
196     lsusb -v > ./lsusb_verbose 2>./lsusb_verbose.error
197   fi
198
199   swapon -s > ./swapon 2>./swapon.error
200
201   # proc stuff
202   for i in cpuinfo interrupts cmdline devices dma fb iomem ioports \
203     mdstat meminfo modules mtrr pci uptime version ; do
204     [ -r /proc/$i ] && cat /proc/$i > proc_$i
205   done
206
207   if ! $_opt_quiet ; then
208     echo "Starting sysdump..."
209     echo "  NOTE: if it seems to be hanging at this stage file a bug report with output of:"
210     echo "        lsof -p \$(pgrep -f "\$\(which sysdump\)")"
211   fi
212   exectest sysdump  && sysdump > ./sysdump 2>./sysdump.error
213   if ! $_opt_quiet ; then
214     echo "Execution of sysdump finished."
215   fi
216
217   exectest cpuid && cpuid > ./cpuid 2>./cpuid.error
218
219   exectest uptime && uptime > ./uptime 2>./uptime.error
220
221   # log
222   dmesg > dmesg.cur
223
224   # hwinfo
225   exectest discover && discover -v --type-summary --enable-bus all > ./discover 2>./discover.error
226   exectest hwinfo   && hwinfo log=hwinfo
227   exectest numactl  && numactl --hardware > ./numactl
228   exectest x86info  && x86info > ./x86info 2>./x86info.error
229   exectest lscpu    && lscpu > ./lscpu
230   exectest lscpu    && lscpu -e > ./lscpu_extended
231
232   # EFI
233   exectest efibootmgr && efibootmgr -v >efibootmgr 2>efibootmgr.error
234
235   # net stuff, net-tools:
236   exectest ifconfig && ifconfig -v -a > ./ifconfig
237   exectest route    && route -n       > ./route
238
239   # net stuff, iproute:
240   exectest ip && ip addrlabel list > ip_addrlabel
241   exectest ip && ip addr show      > ip_addr
242   exectest ip && ip link show      > ip_link
243   exectest ip && ip maddr show     > ip_maddr
244   exectest ip && ip mroute show    > ip_mroute
245   exectest ip && ip mrule show     > ip_mrule 2>ip_mrule.error
246   exectest ip && ip neigh show     > ip_neigh
247   exectest ip && ip netconf        > ip_netconf
248   exectest ip && ip netns list     > ip_netns
249   exectest ip && ip ntable show    > ip_ntable
250   exectest ip && ip route show     > ip_route
251   exectest ip && if [ -r /etc/iproute2/rt_tables ] ; then
252                    grep -v '^#' /etc/iproute2/rt_tables | while read table _ ; do
253                      ip route show table "${table}" > "ip_route_table_${table}" 2> "ip_route_table_${table}".error
254                    done
255                  fi
256   exectest ip && ip rule show      > ip_rule
257   exectest ip && ip tunnel show    > ip_tunnel
258   exectest ip && ip tuntap show    > ip_tuntap
259
260   # software
261   if exectest dpkg ; then
262     dpkg --get-selections   > dpkg_get_selections
263     COLUMNS=300 dpkg --list > dpkg_list
264   fi
265
266   # power management
267   exectest laptop-detect  && laptop-detect >/dev/null 2>/dev/null && echo "0" > laptop_detected
268   if [ -r /proc/acpi/info ] ; then
269     cat /proc/acpi/info > acpi_info
270   fi
271
272   if exectest acpi ; then
273     acpi > ./acpi 2>acpi.error
274     acpi --everything > ./acpi.everything 2>./acpi.everything.error
275     acpi -v > ./acpi.version
276   fi
277   [ -r /proc/apm/ ] && apm > ./apm
278
279   if exectest mcelog ; then
280     mcelog > ./mcelog 2>./mcelog.error
281   fi
282
283   # kernel stuff
284   if [ -r /proc/config.gz ] ; then
285     zcat /proc/config.gz > kernelconfig
286   else
287     [ -r "/boot/config-${UNAME}" ] && cat "/boot/config-${UNAME}" > kernelconfig
288   fi
289
290   exectest dpkg && COLUMNS=1000 dpkg -l "linux-image-${UNAME}" 2>running_kernel.error \
291            | grep "linux-image-${UNAME}" | tr -s ' ' > running_kernel 2>>running_kernel.error
292   dpkg -S "/boot/vmlinuz-$(uname -r)" >> running_kernel 2>>running_kernel.error
293
294   # X stuff
295   if [ -n "${DISPLAY}" ] ; then
296     exectest xviddetect  && xviddetect         > ./xviddetect
297     exectest xvidtune    && xvidtune -show     > ./xdivtune
298     exectest xrandr      && xrandr             > ./xrandr
299     exectest xdpyinfo    && xdpyinfo           > ./xdpyinfo
300     X -version > x_version 2>&1
301   fi
302
303   for i in Xorg.0.log Xorg.7.log Xorg.8.log XFree86.0.log XFree86.7.log XFree86.8.log dmesg ; do
304     cp /var/log/$i log_$i 2>/dev/null
305   done
306
307   if [ -r "$HOME"/.local/share/xorg/Xorg.0.log ] ; then
308     cp "$HOME"/.local/share/xorg/Xorg.0.log user_Xorg.0.log
309   fi
310
311   cp /etc/X11/xorg.conf    xorg.conf    2>/dev/null
312   cp /etc/modules          modules      2>/dev/null
313   cp /etc/X11/XF86Config-4 XF86Config-4 2>/dev/null
314
315   # as root:
316   if [ -n "$NOTROOT" ] ; then
317     echo "not running as root" > root
318   else
319     echo "running as root" > root
320     disk_info
321
322     exectest dmidecode  && dmidecode > ./dmidecode
323
324     if exectest mcelog ; then
325       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
326     fi
327
328     if exectest edac-util ; then
329       edac-util > ./edac-util 2>./edac-util.error
330       edac-util --report=full > edac-util_report 2>edac-util_report.error
331     fi
332
333     if exectest decode-dimms ; then
334       decode-dimms > ./decode-dimms 2>./decode-dimms.error
335     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
336       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
337     fi
338
339     if exectest acpidump ; then
340       acpidump > ./acpidump 2>./acpidump.error
341     fi
342
343     if exectest mokutil ; then
344       mokutil --sb-state > ./mokutil_state 2>./mokutil_state.error
345     fi
346
347     # proxmox
348     exectest qm && qm list > ./qm 2>./qm.error
349     # libvirt
350     exectest virsh && virsh list >./virsh 2>./virsh.error
351     # openvz
352     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
353     # vserver
354     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
355
356     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
357
358     # LVM
359     exectest pvs && pvs > ./pvs 2>./pvs.error
360     exectest vgs && vgs > ./vgs 2>./vgs.error
361     exectest lvs && lvs > ./lvs 2>./lvs.error
362     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
363
364     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
365     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
366     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
367
368     # iSCSI
369     if exectest iscsiadm ; then
370       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
371       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
372       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
373       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
374       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
375       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
376     fi
377
378     if exectest lsscsi ; then
379       lsscsi    > ./lsscsi 2>./lsscsi.error
380       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
381     fi
382
383     for disk in $disklist; do
384       if exectest sfdisk && [[ -b "/dev/${disk}" ]] ; then
385         sfdisk -d "/dev/${disk}" > "./sfdisk_${disk}" 2>"./sfdisk_${disk}.error"
386       fi
387
388       if exectest smartctl ; then
389         echo -e "smartctl -a /dev/${disk}:\n" >> smartctl
390         smartctl -a "/dev/$disk" >> ./smartctl
391         echo -e "\n\n" >> ./smartctl
392       fi
393
394       if exectest hdparm ; then
395         echo -e "hdparm -iv /dev/${disk}:\n" >> hdparm
396         hdparm -iv "/dev/$disk" >> ./hdparm 2>> ./hdparm.error
397         echo -e "\n\n" >> hdparm
398       fi
399
400       if exectest fdisk ; then
401         echo -e "fdisk -lu /dev/${disk}:\n" >> fdisk
402         fdisk -lu "/dev/$disk" >> ./fdisk 2>> ./fdisk.error
403         echo -e "\n\n" >> fdisk
404       fi
405
406       if exectest parted ; then
407         echo -e "parted -s /dev/${disk}:\n" >> parted
408         parted -s "/dev/$disk" print >> ./parted
409         echo -e "\n\n" >> parted
410       fi
411
412       if exectest sdparm ; then
413         echo -e "sdparm --all --long /dev/${disk}:\n" >> sdparm
414         sdparm --all --long "/dev/$disk" >> ./sdparm
415         echo -e "\n\n" >> sdparm
416       fi
417
418       if exectest sg_inq ; then
419         echo -e "sg_inq /dev/${disk}:\n" >> sg_inq
420         sg_inq "/dev/$disk" >> ./sg_inq 2>> ./sg_inq.error
421         echo -e "\n\n" >> sg_inq
422       fi
423
424       file -s "/dev/${disk}"?* | grep -v ": empty" >> file_disk
425     done
426   fi
427 )
428
429 # get rid of empty files
430 for file in *.error ; do
431   test -s "$file" || rm -- "$file"
432 done
433
434 $_opt_quiet || echo
435
436 cd "${WORKING_DIR}"
437
438 # create tarball
439 if [ -n "$GENERATE_FILE" ] ; then
440   tar acf "${OUTFILE}" "${OUTDIRNAME}"
441   if ! $_opt_quiet ; then
442     # shellcheck disable=SC2012
443     [ -r "$OUTFILE" ] && echo "$OUTFILE ($(ls -ahl -- "$OUTFILE" | awk '{print $5}')) has been generated."
444   fi
445 fi
446
447 # remove (temporary) output directory if needed, else keep it, as it doubles
448 # as the real output directory.
449 if [ -z "$GENERATE_DIRECTORY" ] ; then
450   rm -r "${OUTDIR}"
451 else
452   if ! $_opt_quiet ; then
453     [ -r "${OUTDIR}" ] && echo "${OUTDIR} has been generated."
454   fi
455 fi
456
457 exit 0
458
459 ## END OF FILE##################################################################