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