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