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