Support options --force + --quiet for usage with cron
[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 OUTDIR="${WORKING_DIR}/${OUTDIRNAME}"
116 if $_opt_force ; then
117   mkdir -p "${OUTDIR}"
118 else
119   mkdir "${OUTDIR}" || { echo "Directory '${OUTDIR}' already exists, aborting." >&2 ; exit 1; }
120 fi
121
122 if [ -n "$GENERATE_FILE" ] ; then
123   [ -n "$OUTFILE" ] && OUTFILE_="$OUTFILE" || OUTFILE_="${OUTDIR}.tar.bz2"
124   if ! $_opt_force ; then
125     [ -e "${OUTFILE_}" ] && { echo "File '${OUTFILE_}' already exists, aborting." >&2 ; rm -r "${OUTDIR}"; exit 1; }
126   fi
127   OUTFILE=${OUTFILE_}
128   touch "${OUTFILE}"
129 fi
130
131 if [ "$(id -u)" != "0" ] ; then
132   NOTROOT=1
133   $_opt_quiet || echo "W: Running without root permissions. Not all data will be collected."
134 fi
135
136 # check whether a binary is available and executable
137 exectest() {
138   if [ -z "$1" ] ; then
139     echo 'Usage: exectest <binary>'>&2
140     return 1
141   else
142     if test -e "$(which "$1")" ; then
143       return 0
144     else
145       if ! grep -q "^$1"'$' missing_tools 2>/dev/null ; then
146         $_opt_quiet || echo "$1" >> missing_tools
147       fi
148       return 1
149     fi
150   fi
151 }
152
153 # echo a list of all disks and their size
154 # taken from http://cvs.debian.org/fai/lib/disk-info
155 diskandsize() {
156   local isdisk major minor blocks device suffix
157   while read _ _ blocks device _ ; do
158     isdisk=1
159     # skip ide cdrom
160     [ -f "/proc/ide/${device}/media" ] && grep -q cdrom "/proc/ide/${device}/media" && isdisk=0
161     [ "$isdisk" -eq 1 ] && echo "$device $blocks"
162   done
163 }
164
165 list_disks() {
166   # print only every second entry; used by disk_info
167   i=0
168   for ent in "$@" ; do
169     if [ "$i" -eq 0 ]; then
170       echo "$ent"
171       i=1
172     else
173       i=0
174     fi
175   done
176 }
177
178 disk_info() {
179   # the variable holds a space separated list of devices and their block size
180   device_size=$(grep -E ' cciss/c.d.$| ida/c.d.$| rd/c.d.$| hd.$| sd.$|/disc$' /proc/partitions | diskandsize)
181   # a list of all local disks, without size
182   disklist=$(list_disks "$device_size")
183 }
184
185
186 cd "${OUTDIR}" || exit 1
187 (
188   if ! $_opt_quiet ; then
189     [ -n "$GENERATE_FILE" ]      && echo "Output file:      $OUTFILE"
190     [ -n "$GENERATE_DIRECTORY" ] && echo "Output directory: $OUTDIR"
191     echo
192     echo "This might take a few seconds/minutes. Please be patient..."
193   fi
194
195   # some sysinfo
196   date > ./date
197   if [ -r /etc/grml_version ] ; then
198      cat /etc/grml_version > grml_version
199   fi
200   if [ -r /etc/debian_version ] ; then
201      cat /etc/debian_version > debian_version
202   fi
203   uname -a > ./uname
204
205   # disks / devices
206   [ -f /proc/scsi/scsi ] && cat /proc/scsi/scsi > scsi
207   exectest lspci && lspci -nn > ./lspci
208   cat /proc/partitions > partitions
209   find /proc/ide/ -name geometry -exec grep . {} \; > proc_ide 2>/dev/null
210   df -h > ./df 2>/dev/null
211   for i in free lsmod mount lsdev lspnp lsusb ; do
212     exectest $i && $i > ./$i
213   done
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 version ; do
219     [ -r /proc/$i ] && cat /proc/$i > proc_$i
220   done
221   exectest sysdump  && sysdump > ./sysdump 2>./sysdump.error
222
223   # log
224   dmesg > dmesg.cur
225
226   # hwinfo
227   exectest discover && discover -v --type-summary --enable-bus all > ./discover 2>./discover.error
228   exectest hwinfo   && hwinfo log=hwinfo
229   exectest numactl  && numactl --hardware > ./numactl
230   exectest x86info  && x86info > ./x86info 2>./x86info.error
231   exectest lscpu    && lscpu > ./lscpu
232
233   # net stuff, net-tools:
234   exectest ifconfig && ifconfig -v -a > ./ifconfig
235   exectest route    && route -n       > ./route
236
237   # net stuff, iproute:
238   exectest ip && ip addrlabel list > ip_addrlabel
239   exectest ip && ip addr show      > ip_addr
240   exectest ip && ip link show      > ip_link
241   exectest ip && ip maddr show     > ip_maddr
242   exectest ip && ip mroute show    > ip_mroute
243   exectest ip && ip mrule show     > ip_mrule
244   exectest ip && ip neigh show     > ip_neigh
245   exectest ip && ip netns list     > ip_netns
246   exectest ip && ip ntable show    > ip_ntable
247   exectest ip && ip route show     > ip_route
248   exectest ip && if [ -r /etc/iproute2/rt_tables ] ; then
249                    grep -v '^#' /etc/iproute2/rt_tables | while read table _ ; do
250                      ip route show table "${table}" > "ip_route_table_${table}"
251                    done
252                  fi
253   exectest ip && ip rule show      > ip_rule
254   exectest ip && ip tunnel show    > ip_tunnel
255   exectest ip && ip tuntap show    > ip_tuntap
256
257   # software
258   if exectest dpkg ; then
259     dpkg --get-selections   > dpkg_get_selections
260     COLUMNS=300 dpkg --list > dpkg_list
261   fi
262
263   # power management
264   exectest laptop-detect  && laptop-detect >/dev/null 2>/dev/null && echo "0" > laptop_detected
265   if [ -r /proc/acpi/info ] ; then
266     cat /proc/acpi/info > acpi_info
267   fi
268
269   exectest acpi && acpi > ./acpi 2>acpi.error && acpi -v > ./acpi.version
270   [ -r /proc/apm/ ] && apm > ./apm
271
272   if exectest mcelog ; then
273     mcelog > ./mcelog 2>./mcelog.error
274   fi
275
276   # kernel stuff
277   if [ -r /proc/config.gz ] ; then
278     zcat /proc/config.gz > kernelconfig
279   else
280     [ -r "/boot/config-${UNAME}" ] && cat "/boot/config-${UNAME}" > kernelconfig
281   fi
282
283   exectest dpkg && COLUMNS=1000 dpkg -l "linux-image-${UNAME}" 2>running_kernel.error \
284            | grep "linux-image-${UNAME}" | tr -s ' ' > running_kernel 2>>running_kernel.error
285   dpkg -S "/boot/vmlinuz-$(uname -r)" >> running_kernel 2>>running_kernel.error
286
287   # X stuff
288   if [ -n "${DISPLAY}" ] ; then
289     exectest xviddetect  && xviddetect         > ./xviddetect
290     exectest xvidtune    && xvidtune -show     > ./xdivtune
291     exectest xrandr      && xrandr             > ./xrandr
292     exectest xdpyinfo    && xdpyinfo           > ./xdpyinfo
293     X -version > x_version 2>&1
294   fi
295
296   for i in Xorg.0.log Xorg.7.log Xorg.8.log XFree86.0.log XFree86.7.log XFree86.8.log dmesg ; do
297     cp /var/log/$i log_$i 2>/dev/null
298   done
299
300   cp /etc/X11/xorg.conf    xorg.conf    2>/dev/null
301   cp /etc/modules          modules      2>/dev/null
302   cp /etc/X11/XF86Config-4 XF86Config-4 2>/dev/null
303
304   # as root:
305   if [ -n "$NOTROOT" ] ; then
306     echo "not running as root" > root
307   else
308     echo "running as root" > root
309     disk_info
310     exectest sfdisk     && sfdisk -d > ./sfdisk 2>./sfdisk.error
311     exectest dmidecode  && dmidecode > ./dmidecode
312
313     exectest dconf && dconf -o dconf
314
315     if exectest mcelog ; then
316       mcelog --dmi > mcelog_dmi 2>mcelog_dmi.error
317     fi
318
319     if exectest edac-util ; then
320       edac-util > ./edac-util 2>./edac-util.error
321       edac-util --report=full > edac-util_report 2>edac-util_report.error
322     fi
323
324     if exectest decode-dimms ; then
325       decode-dimms > ./decode-dimms 2>./decode-dimms.error
326     elif [ -x /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl ] ; then
327       /usr/share/doc/lm-sensors/examples/eeprom/decode-dimms.pl > decode-dimms 2>decode-dimms.error
328     fi
329
330     # proxmox
331     exectest qm && qm list > ./qm 2>./qm.error
332     # libvirt
333     exectest virsh && virsh list >./virsh 2>./virsh.error
334     # openvz
335     exectest vzlist && vzlist >./vzlist 2>./vzlist.error
336     # vserver
337     exectest vserver-stat && vserver-stat >./vserver-stat 2>./vserver-stat.error
338
339     exectest mdadm && mdadm --detail /dev/md[0-9]* >> ./mdadm 2>./mdadm.error
340
341     # LVM
342     exectest pvs && pvs > ./pvs 2>./pvs.error
343     exectest vgs && vgs > ./vgs 2>./vgs.error
344     exectest lvs && lvs > ./lvs 2>./lvs.error
345     exectest lvdisplay && lvdisplay > ./lvdisplay 2>./lvdisplay.error
346
347     exectest dmsetup && dmsetup ls > dmsetup_ls 2>dmsetup_ls.error
348     exectest dmsetup && dmsetup ls --tree > dmsetup_ls_tree 2>dmsetup_ls_tree.error
349     exectest lsblk && lsblk > ./lsblk 2>./lsblk.error
350
351     # iSCSI
352     if exectest iscsiadm ; then
353       iscsiadm -m session > iscsiadm_session 2>iscsiadm_session.error
354       iscsiadm -m fw > iscsiadm_fw 2>iscsiadm_fw.error
355       iscsiadm -m host > iscsiadm_host 2>iscsiadm_host.error
356       iscsiadm -m iface > iscsiadm_iface 2>iscsiadm_iface.error
357       iscsiadm -m node > iscsiadm_node 2>iscsiadm_node.error
358       iscsiadm -m discovery > iscsiadm_discovery 2>iscsiadm_discovery.error
359     fi
360
361     if exectest lsscsi ; then
362       lsscsi    > ./lsscsi 2>./lsscsi.error
363       lsscsi -t > ./lsscsi_transport 2>./lsscsi_transport.error
364     fi
365
366     for disk in $disklist; do
367       if exectest smartctl ; then
368         echo -e "smartctl -a /dev/${disk}:\n" >> smartctl
369         smartctl -a "/dev/$disk" >> ./smartctl
370         echo -e "\n\n" >> ./smartctl
371       fi
372
373       if exectest hdparm ; then
374         echo -e "hdparm -iv /dev/${disk}:\n" >> hdparm
375         hdparm -iv "/dev/$disk" >> ./hdparm
376         echo -e "\n\n" >> hdparm
377       fi
378
379       if exectest fdisk ; then
380         echo -e "fdisk -lu /dev/${disk}:\n" >> fdisk
381         fdisk -lu "/dev/$disk" >> ./fdisk 2>> ./fdisk.error
382         echo -e "\n\n" >> fdisk
383       fi
384
385       if exectest parted ; then
386         echo -e "parted -s /dev/${disk}:\n" >> parted
387         parted -s "/dev/$disk" print >> ./parted
388         echo -e "\n\n" >> parted
389       fi
390
391       if exectest sdparm ; then
392         echo -e "sdparm --all --long /dev/${disk}:\n" >> sdparm
393         sdparm --all --long "/dev/$disk" >> ./sdparm
394         echo -e "\n\n" >> sdparm
395       fi
396
397       if exectest sg_inq ; then
398         echo -e "sg_inq /dev/${disk}:\n" >> sg_inq
399         sg_inq "/dev/$disk" >> ./sg_inq
400         echo -e "\n\n" >> sg_inq
401       fi
402
403       file -s "/dev/${disk}"?* | grep -v ": empty" >> file_disk
404     done
405   fi
406 )
407
408 # get rid of empty files
409 for file in *.error ; do
410   test -s "$file" || rm -- "$file"
411 done
412
413 $_opt_quiet || echo
414
415 cd "${WORKING_DIR}"
416
417 # create tarball
418 if [ -n "$GENERATE_FILE" ] ; then
419   tar acf "${OUTFILE}" "${OUTDIRNAME}"
420   if ! $_opt_quiet ; then
421     # shellcheck disable=SC2012
422     [ -r "$OUTFILE" ] && echo "$OUTFILE ($(ls -ahl -- "$OUTFILE" | awk '{print $5}')) has been generated."
423   fi
424 fi
425
426 # remove (temporary) output directory if needed, else keep it, as it doubles
427 # as the real output directory.
428 if [ -z "$GENERATE_DIRECTORY" ] ; then
429   rm -r "${OUTDIR}"
430 else
431   if ! $_opt_quiet ; then
432     [ -r "${OUTDIR}" ] && echo "${OUTDIR} has been generated."
433   fi
434 fi
435
436 exit 0
437
438 ## END OF FILE##################################################################