6d98829bfdac8d617163fe784ab215076f77b845
[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 version ; do
215     [ -r /proc/$i ] && cat /proc/$i > proc_$i
216   done
217   exectest sysdump  && sysdump > ./sysdump 2>./sysdump.error
218
219   # log
220   dmesg > dmesg.cur
221
222   # hwinfo
223   exectest discover && discover -v --type-summary --enable-bus all > ./discover 2>./discover.error
224   exectest hwinfo   && hwinfo log=hwinfo
225   exectest numactl  && numactl --hardware > ./numactl
226   exectest x86info  && x86info > ./x86info 2>./x86info.error
227   exectest lscpu    && lscpu > ./lscpu
228
229   # EFI
230   exectest efibootmgr && efibootmgr -v > efibootmgr
231
232   # net stuff, net-tools:
233   exectest ifconfig && ifconfig -v -a > ./ifconfig
234   exectest route    && route -n       > ./route
235
236   # net stuff, iproute:
237   exectest ip && ip addrlabel list > ip_addrlabel
238   exectest ip && ip addr show      > ip_addr
239   exectest ip && ip link show      > ip_link
240   exectest ip && ip maddr show     > ip_maddr
241   exectest ip && ip mroute show    > ip_mroute
242   exectest ip && ip mrule show     > ip_mrule 2>ip_mrule.error
243   exectest ip && ip neigh show     > ip_neigh
244   exectest ip && ip netns list     > ip_netns
245   exectest ip && ip ntable show    > ip_ntable
246   exectest ip && ip route show     > ip_route
247   exectest ip && if [ -r /etc/iproute2/rt_tables ] ; then
248                    grep -v '^#' /etc/iproute2/rt_tables | while read table _ ; do
249                      ip route show table "${table}" > "ip_route_table_${table}"
250                    done
251                  fi
252   exectest ip && ip rule show      > ip_rule
253   exectest ip && ip tunnel show    > ip_tunnel
254   exectest ip && ip tuntap show    > ip_tuntap
255
256   # software
257   if exectest dpkg ; then
258     dpkg --get-selections   > dpkg_get_selections
259     COLUMNS=300 dpkg --list > dpkg_list
260   fi
261
262   # power management
263   exectest laptop-detect  && laptop-detect >/dev/null 2>/dev/null && echo "0" > laptop_detected
264   if [ -r /proc/acpi/info ] ; then
265     cat /proc/acpi/info > acpi_info
266   fi
267
268   if exectest acpi ; then
269     acpi > ./acpi 2>acpi.error
270     acpi --everything > ./acpi.everything 2>./acpi.everything.error
271     acpi -v > ./acpi.version
272   fi
273   [ -r /proc/apm/ ] && apm > ./apm
274
275   if exectest mcelog ; then
276     mcelog > ./mcelog 2>./mcelog.error
277   fi
278
279   # kernel stuff
280   if [ -r /proc/config.gz ] ; then
281     zcat /proc/config.gz > kernelconfig
282   else
283     [ -r "/boot/config-${UNAME}" ] && cat "/boot/config-${UNAME}" > kernelconfig
284   fi
285
286   exectest dpkg && COLUMNS=1000 dpkg -l "linux-image-${UNAME}" 2>running_kernel.error \
287            | grep "linux-image-${UNAME}" | tr -s ' ' > running_kernel 2>>running_kernel.error
288   dpkg -S "/boot/vmlinuz-$(uname -r)" >> running_kernel 2>>running_kernel.error
289
290   # X stuff
291   if [ -n "${DISPLAY}" ] ; then
292     exectest xviddetect  && xviddetect         > ./xviddetect
293     exectest xvidtune    && xvidtune -show     > ./xdivtune
294     exectest xrandr      && xrandr             > ./xrandr
295     exectest xdpyinfo    && xdpyinfo           > ./xdpyinfo
296     X -version > x_version 2>&1
297   fi
298
299   for i in Xorg.0.log Xorg.7.log Xorg.8.log XFree86.0.log XFree86.7.log XFree86.8.log dmesg ; do
300     cp /var/log/$i log_$i 2>/dev/null
301   done
302
303   if [ -r "$HOME"/.local/share/xorg/Xorg.0.log ] ; then
304     cp "$HOME"/.local/share/xorg/Xorg.0.log user_Xorg.0.log
305   fi
306
307   cp /etc/X11/xorg.conf    xorg.conf    2>/dev/null
308   cp /etc/modules          modules      2>/dev/null
309   cp /etc/X11/XF86Config-4 XF86Config-4 2>/dev/null
310
311   # as root:
312   if [ -n "$NOTROOT" ] ; then
313     echo "not running as root" > root
314   else
315     echo "running as root" > root
316     disk_info
317     exectest sfdisk     && sfdisk -d > ./sfdisk 2>./sfdisk.error
318     exectest dmidecode  && dmidecode > ./dmidecode
319
320     exectest dconf && dconf -o dconf
321
322     if exectest mcelog ; then
323       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
324     fi
325
326     if exectest edac-util ; then
327       edac-util > ./edac-util 2>./edac-util.error
328       edac-util --report=full > edac-util_report 2>edac-util_report.error
329     fi
330
331     if exectest decode-dimms ; then
332       decode-dimms > ./decode-dimms 2>./decode-dimms.error
333     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
334       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
335     fi
336
337     if exectest acpidump ; then
338       acpidump > ./acpidump 2>./acpidump.error
339     fi
340
341     if exectest mokutil ; then
342       mokutil --sb-state > ./mokutil_state 2>./mokutil_state.error
343     fi
344
345     # proxmox
346     exectest qm && qm list > ./qm 2>./qm.error
347     # libvirt
348     exectest virsh && virsh list >./virsh 2>./virsh.error
349     # openvz
350     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
351     # vserver
352     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
353
354     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
355
356     # LVM
357     exectest pvs && pvs > ./pvs 2>./pvs.error
358     exectest vgs && vgs > ./vgs 2>./vgs.error
359     exectest lvs && lvs > ./lvs 2>./lvs.error
360     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
361
362     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
363     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
364     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
365
366     # iSCSI
367     if exectest iscsiadm ; then
368       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
369       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
370       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
371       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
372       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
373       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
374     fi
375
376     if exectest lsscsi ; then
377       lsscsi    > ./lsscsi 2>./lsscsi.error
378       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
379     fi
380
381     for disk in $disklist; do
382       if exectest smartctl ; then
383         echo -e "smartctl -a /dev/${disk}:\n" >> smartctl
384         smartctl -a "/dev/$disk" >> ./smartctl
385         echo -e "\n\n" >> ./smartctl
386       fi
387
388       if exectest hdparm ; then
389         echo -e "hdparm -iv /dev/${disk}:\n" >> hdparm
390         hdparm -iv "/dev/$disk" >> ./hdparm 2>> ./hdparm.error
391         echo -e "\n\n" >> hdparm
392       fi
393
394       if exectest fdisk ; then
395         echo -e "fdisk -lu /dev/${disk}:\n" >> fdisk
396         fdisk -lu "/dev/$disk" >> ./fdisk 2>> ./fdisk.error
397         echo -e "\n\n" >> fdisk
398       fi
399
400       if exectest parted ; then
401         echo -e "parted -s /dev/${disk}:\n" >> parted
402         parted -s "/dev/$disk" print >> ./parted
403         echo -e "\n\n" >> parted
404       fi
405
406       if exectest sdparm ; then
407         echo -e "sdparm --all --long /dev/${disk}:\n" >> sdparm
408         sdparm --all --long "/dev/$disk" >> ./sdparm
409         echo -e "\n\n" >> sdparm
410       fi
411
412       if exectest sg_inq ; then
413         echo -e "sg_inq /dev/${disk}:\n" >> sg_inq
414         sg_inq "/dev/$disk" >> ./sg_inq 2>> ./sg_inq.error
415         echo -e "\n\n" >> sg_inq
416       fi
417
418       file -s "/dev/${disk}"?* | grep -v ": empty" >> file_disk
419     done
420   fi
421 )
422
423 # get rid of empty files
424 for file in *.error ; do
425   test -s "$file" || rm -- "$file"
426 done
427
428 $_opt_quiet || echo
429
430 cd "${WORKING_DIR}"
431
432 # create tarball
433 if [ -n "$GENERATE_FILE" ] ; then
434   tar acf "${OUTFILE}" "${OUTDIRNAME}"
435   if ! $_opt_quiet ; then
436     # shellcheck disable=SC2012
437     [ -r "$OUTFILE" ] && echo "$OUTFILE ($(ls -ahl -- "$OUTFILE" | awk '{print $5}')) has been generated."
438   fi
439 fi
440
441 # remove (temporary) output directory if needed, else keep it, as it doubles
442 # as the real output directory.
443 if [ -z "$GENERATE_DIRECTORY" ] ; then
444   rm -r "${OUTDIR}"
445 else
446   if ! $_opt_quiet ; then
447     [ -r "${OUTDIR}" ] && echo "${OUTDIR} has been generated."
448   fi
449 fi
450
451 exit 0
452
453 ## END OF FILE##################################################################