a2203bc834a24743d75d9832601bba1e4c2545a4
[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   exectest sysdump  && sysdump > ./sysdump 2>./sysdump.error
218
219   exectest cpuid && cpuid > ./cpuid 2>./cpuid.error
220
221   exectest uptime && uptime > ./uptime 2>./uptime.error
222
223   # log
224   dmesg > dmesg.cur
225
226   # hwinfo
227   exectest discover && discover -v --type-summary --enable-bus all > ./discover 2>./discover.error
228   exectest hwinfo   && hwinfo log=hwinfo
229   exectest numactl  && numactl --hardware > ./numactl
230   exectest x86info  && x86info > ./x86info 2>./x86info.error
231   exectest lscpu    && lscpu > ./lscpu
232
233   # EFI
234   exectest efibootmgr && efibootmgr -v > efibootmgr
235
236   # net stuff, net-tools:
237   exectest ifconfig && ifconfig -v -a > ./ifconfig
238   exectest route    && route -n       > ./route
239
240   # net stuff, iproute:
241   exectest ip && ip addrlabel list > ip_addrlabel
242   exectest ip && ip addr show      > ip_addr
243   exectest ip && ip link show      > ip_link
244   exectest ip && ip maddr show     > ip_maddr
245   exectest ip && ip mroute show    > ip_mroute
246   exectest ip && ip mrule show     > ip_mrule 2>ip_mrule.error
247   exectest ip && ip neigh show     > ip_neigh
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}"
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     exectest sfdisk     && sfdisk -d > ./sfdisk 2>./sfdisk.error
322     exectest dmidecode  && dmidecode > ./dmidecode
323
324     exectest dconf && dconf -o dconf
325
326     if exectest mcelog ; then
327       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
328     fi
329
330     if exectest edac-util ; then
331       edac-util > ./edac-util 2>./edac-util.error
332       edac-util --report=full > edac-util_report 2>edac-util_report.error
333     fi
334
335     if exectest decode-dimms ; then
336       decode-dimms > ./decode-dimms 2>./decode-dimms.error
337     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
338       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
339     fi
340
341     if exectest acpidump ; then
342       acpidump > ./acpidump 2>./acpidump.error
343     fi
344
345     if exectest mokutil ; then
346       mokutil --sb-state > ./mokutil_state 2>./mokutil_state.error
347     fi
348
349     # proxmox
350     exectest qm && qm list > ./qm 2>./qm.error
351     # libvirt
352     exectest virsh && virsh list >./virsh 2>./virsh.error
353     # openvz
354     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
355     # vserver
356     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
357
358     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
359
360     # LVM
361     exectest pvs && pvs > ./pvs 2>./pvs.error
362     exectest vgs && vgs > ./vgs 2>./vgs.error
363     exectest lvs && lvs > ./lvs 2>./lvs.error
364     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
365
366     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
367     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
368     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
369
370     # iSCSI
371     if exectest iscsiadm ; then
372       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
373       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
374       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
375       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
376       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
377       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
378     fi
379
380     if exectest lsscsi ; then
381       lsscsi    > ./lsscsi 2>./lsscsi.error
382       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
383     fi
384
385     for disk in $disklist; do
386       if exectest smartctl ; then
387         echo -e "smartctl -a /dev/${disk}:\n" >> smartctl
388         smartctl -a "/dev/$disk" >> ./smartctl
389         echo -e "\n\n" >> ./smartctl
390       fi
391
392       if exectest hdparm ; then
393         echo -e "hdparm -iv /dev/${disk}:\n" >> hdparm
394         hdparm -iv "/dev/$disk" >> ./hdparm 2>> ./hdparm.error
395         echo -e "\n\n" >> hdparm
396       fi
397
398       if exectest fdisk ; then
399         echo -e "fdisk -lu /dev/${disk}:\n" >> fdisk
400         fdisk -lu "/dev/$disk" >> ./fdisk 2>> ./fdisk.error
401         echo -e "\n\n" >> fdisk
402       fi
403
404       if exectest parted ; then
405         echo -e "parted -s /dev/${disk}:\n" >> parted
406         parted -s "/dev/$disk" print >> ./parted
407         echo -e "\n\n" >> parted
408       fi
409
410       if exectest sdparm ; then
411         echo -e "sdparm --all --long /dev/${disk}:\n" >> sdparm
412         sdparm --all --long "/dev/$disk" >> ./sdparm
413         echo -e "\n\n" >> sdparm
414       fi
415
416       if exectest sg_inq ; then
417         echo -e "sg_inq /dev/${disk}:\n" >> sg_inq
418         sg_inq "/dev/$disk" >> ./sg_inq 2>> ./sg_inq.error
419         echo -e "\n\n" >> sg_inq
420       fi
421
422       file -s "/dev/${disk}"?* | grep -v ": empty" >> file_disk
423     done
424   fi
425 )
426
427 # get rid of empty files
428 for file in *.error ; do
429   test -s "$file" || rm -- "$file"
430 done
431
432 $_opt_quiet || echo
433
434 cd "${WORKING_DIR}"
435
436 # create tarball
437 if [ -n "$GENERATE_FILE" ] ; then
438   tar acf "${OUTFILE}" "${OUTDIRNAME}"
439   if ! $_opt_quiet ; then
440     # shellcheck disable=SC2012
441     [ -r "$OUTFILE" ] && echo "$OUTFILE ($(ls -ahl -- "$OUTFILE" | awk '{print $5}')) has been generated."
442   fi
443 fi
444
445 # remove (temporary) output directory if needed, else keep it, as it doubles
446 # as the real output directory.
447 if [ -z "$GENERATE_DIRECTORY" ] ; then
448   rm -r "${OUTDIR}"
449 else
450   if ! $_opt_quiet ; then
451     [ -r "${OUTDIR}" ] && echo "${OUTDIR} has been generated."
452   fi
453 fi
454
455 exit 0
456
457 ## END OF FILE##################################################################