Release new version 0.15.4
[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   exectest lscpu    && lscpu -e > ./lscpu_extended
233
234   # EFI
235   exectest efibootmgr && efibootmgr -v > efibootmgr
236
237   # net stuff, net-tools:
238   exectest ifconfig && ifconfig -v -a > ./ifconfig
239   exectest route    && route -n       > ./route
240
241   # net stuff, iproute:
242   exectest ip && ip addrlabel list > ip_addrlabel
243   exectest ip && ip addr show      > ip_addr
244   exectest ip && ip link show      > ip_link
245   exectest ip && ip maddr show     > ip_maddr
246   exectest ip && ip mroute show    > ip_mroute
247   exectest ip && ip mrule show     > ip_mrule 2>ip_mrule.error
248   exectest ip && ip neigh show     > ip_neigh
249   exectest ip && ip netconf        > ip_netconf
250   exectest ip && ip netns list     > ip_netns
251   exectest ip && ip ntable show    > ip_ntable
252   exectest ip && ip route show     > ip_route
253   exectest ip && if [ -r /etc/iproute2/rt_tables ] ; then
254                    grep -v '^#' /etc/iproute2/rt_tables | while read table _ ; do
255                      ip route show table "${table}" > "ip_route_table_${table}"
256                    done
257                  fi
258   exectest ip && ip rule show      > ip_rule
259   exectest ip && ip tunnel show    > ip_tunnel
260   exectest ip && ip tuntap show    > ip_tuntap
261
262   # software
263   if exectest dpkg ; then
264     dpkg --get-selections   > dpkg_get_selections
265     COLUMNS=300 dpkg --list > dpkg_list
266   fi
267
268   # power management
269   exectest laptop-detect  && laptop-detect >/dev/null 2>/dev/null && echo "0" > laptop_detected
270   if [ -r /proc/acpi/info ] ; then
271     cat /proc/acpi/info > acpi_info
272   fi
273
274   if exectest acpi ; then
275     acpi > ./acpi 2>acpi.error
276     acpi --everything > ./acpi.everything 2>./acpi.everything.error
277     acpi -v > ./acpi.version
278   fi
279   [ -r /proc/apm/ ] && apm > ./apm
280
281   if exectest mcelog ; then
282     mcelog > ./mcelog 2>./mcelog.error
283   fi
284
285   # kernel stuff
286   if [ -r /proc/config.gz ] ; then
287     zcat /proc/config.gz > kernelconfig
288   else
289     [ -r "/boot/config-${UNAME}" ] && cat "/boot/config-${UNAME}" > kernelconfig
290   fi
291
292   exectest dpkg && COLUMNS=1000 dpkg -l "linux-image-${UNAME}" 2>running_kernel.error \
293            | grep "linux-image-${UNAME}" | tr -s ' ' > running_kernel 2>>running_kernel.error
294   dpkg -S "/boot/vmlinuz-$(uname -r)" >> running_kernel 2>>running_kernel.error
295
296   # X stuff
297   if [ -n "${DISPLAY}" ] ; then
298     exectest xviddetect  && xviddetect         > ./xviddetect
299     exectest xvidtune    && xvidtune -show     > ./xdivtune
300     exectest xrandr      && xrandr             > ./xrandr
301     exectest xdpyinfo    && xdpyinfo           > ./xdpyinfo
302     X -version > x_version 2>&1
303   fi
304
305   for i in Xorg.0.log Xorg.7.log Xorg.8.log XFree86.0.log XFree86.7.log XFree86.8.log dmesg ; do
306     cp /var/log/$i log_$i 2>/dev/null
307   done
308
309   if [ -r "$HOME"/.local/share/xorg/Xorg.0.log ] ; then
310     cp "$HOME"/.local/share/xorg/Xorg.0.log user_Xorg.0.log
311   fi
312
313   cp /etc/X11/xorg.conf    xorg.conf    2>/dev/null
314   cp /etc/modules          modules      2>/dev/null
315   cp /etc/X11/XF86Config-4 XF86Config-4 2>/dev/null
316
317   # as root:
318   if [ -n "$NOTROOT" ] ; then
319     echo "not running as root" > root
320   else
321     echo "running as root" > root
322     disk_info
323     exectest sfdisk     && sfdisk -d > ./sfdisk 2>./sfdisk.error
324     exectest dmidecode  && dmidecode > ./dmidecode
325
326     exectest dconf && dconf -o dconf
327
328     if exectest mcelog ; then
329       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
330     fi
331
332     if exectest edac-util ; then
333       edac-util > ./edac-util 2>./edac-util.error
334       edac-util --report=full > edac-util_report 2>edac-util_report.error
335     fi
336
337     if exectest decode-dimms ; then
338       decode-dimms > ./decode-dimms 2>./decode-dimms.error
339     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
340       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
341     fi
342
343     if exectest acpidump ; then
344       acpidump > ./acpidump 2>./acpidump.error
345     fi
346
347     if exectest mokutil ; then
348       mokutil --sb-state > ./mokutil_state 2>./mokutil_state.error
349     fi
350
351     # proxmox
352     exectest qm && qm list > ./qm 2>./qm.error
353     # libvirt
354     exectest virsh && virsh list >./virsh 2>./virsh.error
355     # openvz
356     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
357     # vserver
358     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
359
360     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
361
362     # LVM
363     exectest pvs && pvs > ./pvs 2>./pvs.error
364     exectest vgs && vgs > ./vgs 2>./vgs.error
365     exectest lvs && lvs > ./lvs 2>./lvs.error
366     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
367
368     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
369     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
370     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
371
372     # iSCSI
373     if exectest iscsiadm ; then
374       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
375       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
376       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
377       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
378       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
379       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
380     fi
381
382     if exectest lsscsi ; then
383       lsscsi    > ./lsscsi 2>./lsscsi.error
384       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
385     fi
386
387     for disk in $disklist; do
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##################################################################