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