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