Release new version 0.13.0
[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
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   exectest acpi && acpi > ./acpi 2>acpi.error && acpi -v > ./acpi.version
259   [ -r /proc/apm/ ] && apm > ./apm
260
261   if exectest mcelog ; then
262     mcelog > ./mcelog 2>./mcelog.error
263   fi
264
265   # kernel stuff
266   if [ -r /proc/config.gz ] ; then
267     zcat /proc/config.gz > kernelconfig
268   else
269     [ -r "/boot/config-${UNAME}" ] && cat "/boot/config-${UNAME}" > kernelconfig
270   fi
271
272   exectest dpkg && COLUMNS=1000 dpkg -l "linux-image-${UNAME}" 2>running_kernel.error \
273            | grep "linux-image-${UNAME}" | tr -s ' ' > running_kernel 2>>running_kernel.error
274   dpkg -S "/boot/vmlinuz-$(uname -r)" >> running_kernel 2>>running_kernel.error
275
276   # X stuff
277   if [ -n "${DISPLAY}" ] ; then
278     exectest xviddetect  && xviddetect         > ./xviddetect
279     exectest xvidtune    && xvidtune -show     > ./xdivtune
280     exectest xrandr      && xrandr             > ./xrandr
281     exectest xdpyinfo    && xdpyinfo           > ./xdpyinfo
282     X -version > x_version 2>&1
283   fi
284
285   for i in Xorg.0.log Xorg.7.log Xorg.8.log XFree86.0.log XFree86.7.log XFree86.8.log dmesg ; do
286     cp /var/log/$i log_$i 2>/dev/null
287   done
288
289   cp /etc/X11/xorg.conf    xorg.conf    2>/dev/null
290   cp /etc/modules          modules      2>/dev/null
291   cp /etc/X11/XF86Config-4 XF86Config-4 2>/dev/null
292
293   # as root:
294   if [ -n "$NOTROOT" ] ; then
295     echo "not running as root" > root
296   else
297     echo "running as root" > root
298     disk_info
299     exectest sfdisk     && sfdisk -d > ./sfdisk 2>./sfdisk.error
300     exectest dmidecode  && dmidecode > ./dmidecode
301
302     exectest dconf && dconf -o dconf
303
304     if exectest mcelog ; then
305       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
306     fi
307
308     if exectest edac-util ; then
309       edac-util > ./edac-util 2>./edac-util.error
310       edac-util --report=full > edac-util_report 2>edac-util_report.error
311     fi
312
313     if exectest decode-dimms ; then
314       decode-dimms > ./decode-dimms 2>./decode-dimms.error
315     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
316       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
317     fi
318
319     # proxmox
320     exectest qm && qm list > ./qm 2>./qm.error
321     # libvirt
322     exectest virsh && virsh list >./virsh 2>./virsh.error
323     # openvz
324     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
325     # vserver
326     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
327
328     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
329
330     # LVM
331     exectest pvs && pvs > ./pvs 2>./pvs.error
332     exectest vgs && vgs > ./vgs 2>./vgs.error
333     exectest lvs && lvs > ./lvs 2>./lvs.error
334     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
335
336     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
337     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
338     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
339
340     # iSCSI
341     if exectest iscsiadm ; then
342       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
343       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
344       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
345       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
346       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
347       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
348     fi
349
350     if exectest lsscsi ; then
351       lsscsi    > ./lsscsi 2>./lsscsi.error
352       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
353     fi
354
355     for disk in $disklist; do
356       if exectest smartctl ; then
357         echo -e "smartctl -a /dev/${disk}:\n" >> smartctl
358         smartctl -a "/dev/$disk" >> ./smartctl
359         echo -e "\n\n" >> ./smartctl
360       fi
361
362       if exectest hdparm ; then
363         echo -e "hdparm -iv /dev/${disk}:\n" >> hdparm
364         hdparm -iv "/dev/$disk" >> ./hdparm 2>> ./hdparm.error
365         echo -e "\n\n" >> hdparm
366       fi
367
368       if exectest fdisk ; then
369         echo -e "fdisk -lu /dev/${disk}:\n" >> fdisk
370         fdisk -lu "/dev/$disk" >> ./fdisk 2>> ./fdisk.error
371         echo -e "\n\n" >> fdisk
372       fi
373
374       if exectest parted ; then
375         echo -e "parted -s /dev/${disk}:\n" >> parted
376         parted -s "/dev/$disk" print >> ./parted
377         echo -e "\n\n" >> parted
378       fi
379
380       if exectest sdparm ; then
381         echo -e "sdparm --all --long /dev/${disk}:\n" >> sdparm
382         sdparm --all --long "/dev/$disk" >> ./sdparm
383         echo -e "\n\n" >> sdparm
384       fi
385
386       if exectest sg_inq ; then
387         echo -e "sg_inq /dev/${disk}:\n" >> sg_inq
388         sg_inq "/dev/$disk" >> ./sg_inq 2>> ./sg_inq.error
389         echo -e "\n\n" >> sg_inq
390       fi
391
392       file -s "/dev/${disk}"?* | grep -v ": empty" >> file_disk
393     done
394   fi
395 )
396
397 # get rid of empty files
398 for file in *.error ; do
399   test -s "$file" || rm -- "$file"
400 done
401
402 $_opt_quiet || echo
403
404 cd "${WORKING_DIR}"
405
406 # create tarball
407 if [ -n "$GENERATE_FILE" ] ; then
408   tar acf "${OUTFILE}" "${OUTDIRNAME}"
409   if ! $_opt_quiet ; then
410     # shellcheck disable=SC2012
411     [ -r "$OUTFILE" ] && echo "$OUTFILE ($(ls -ahl -- "$OUTFILE" | awk '{print $5}')) has been generated."
412   fi
413 fi
414
415 # remove (temporary) output directory if needed, else keep it, as it doubles
416 # as the real output directory.
417 if [ -z "$GENERATE_DIRECTORY" ] ; then
418   rm -r "${OUTDIR}"
419 else
420   if ! $_opt_quiet ; then
421     [ -r "${OUTDIR}" ] && echo "${OUTDIR} has been generated."
422   fi
423 fi
424
425 exit 0
426
427 ## END OF FILE##################################################################