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