#!/bin/sh
# Gathers information about system state and creates an lzop
# compressed rich core

# This file is part of sp-rich-core
#
# Copyright (C) 2006-2008 Nokia Corporation.
#
# Contact: Eero Tamminen <eero.tamminen@nokia.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA

#
# Helper functions
#

_obtain_configuration()
{
  # default fallback values, overruled by configuration file if one exists

  system_settings=/usr/share/crash-reporter-settings
  user_settings=/home/user

  INCLUDE_CORE=true
  INCLUDE_SYSLOG=true
  INCLUDE_PKGLIST=true

	DEFAULT_CORE_NAME="unknown"

  if [ -e $system_settings/crash-reporter-privacy.conf ]; then
    . $system_settings/crash-reporter-privacy.conf 2>/dev/null
  fi
  if [ -e $user_settings/crash-reporter-privacy.conf ]; then
    . $user_settings/crash-reporter-privacy.conf 2>/dev/null
  fi
}

_parse_arguments()
{
  while [ $# -ge 1 ]; do
    case $1 in 
      "--no-section-header")
      NO_SECTION_HEADER=true
      ;;
      "--default-name")
			shift
      DEFAULT_CORE_NAME="$1"
      ;;
      --pid=*)
      core_pid=${1#--pid=}     
      ;;
      --signal=*)
      core_sig=${1#--signal=}
      ;;
    esac
    shift
  done
}

_print_header()
{
    printf '\n[---rich-core: %s---]\n' "$@"
}

_print_separator()
{
    printf '\n--- %s ---\n' "$@"
}

_print_file_with_header()
{
  if [ -f $1 ]; then
    _print_header $1
    cat $1
  fi
}

_print_twofiles_with_header()
{
  if [ -f $1 ]; then
    _print_header $1
    if [ -f $2 ]; then
	_print_separator $2
	cat $2
	_print_separator $1
    fi
    cat $1
  fi
}

_print_command_with_header()
{
  _cmd=$1
  shift
  if [ -x ${_cmd} ]; then
    _print_header ${_cmd##*/}
    { ${_cmd} $@ 2>&1; }
  fi
}

_free_space()
{
  # first we cut the header line with sed
  # then put everything on one line with tr
  #  (long device name may split the line)
  # and then pick available space with another sed
  # -> feel free to optimize
  df -k $1 |
    sed -e '1d' |
    tr '\n' ' ' |
    sed -e 's/^[^ ]* *[0-9]* *[0-9]* *\([0-9]*\) *.*$/\1/g'
}

#
# Information collection functions
#

_section_date()
{
  _print_command_with_header /bin/date
}

_section_proc_fd()
{
  _print_header fd
  ls -l /proc/${core_pid}/fd/
}

_section_proc_smaps()
{
  _print_file_with_header /proc/${core_pid}/smaps
}

_section_osso_software_version()
{
  _print_file_with_header /tmp/osso_software_version
}

_section_component_version()
{
  _print_file_with_header /proc/component_version
}

_section_ifconfig()
{
  _print_command_with_header /sbin/ifconfig -a
}

_section_df()
{
  _print_command_with_header /bin/df
}

_section_ls_proc()
{
  _print_header ls_proc
  ls -l /proc/${core_pid}/
}

_section_cmdline()
{
  _print_file_with_header /proc/${core_pid}/cmdline
}

_section_slabinfo()
{
  _print_file_with_header /proc/slabinfo
}

_section_xmeminfo()
{
  _print_command_with_header /usr/bin/xmeminfo -d :0
}

_section_proc2csv()
{
  _print_command_with_header /usr/bin/proc2csv
}

_section_packagelist()
{
  _print_header packagelist
  awk '/^Package:/{printf("%s ",$2)}/^Version:/{print $2}' /var/lib/dpkg/status | /bin/grep -v -e -l10n -e -dbg | sort
}

_section_syslog()
{
# as syslog has existed in two different places, try the old one first
# to support older releases

  if [ -e /var/ftd-log/syslog ]; then
    _print_file_with_header /var/ftd-log/syslog
  elif [ -e /var/log/syslog ]; then
    _print_twofiles_with_header /var/log/syslog /tmp/Xorg.0.log
  fi
}

_section_osso_product_info()
{
  if [ -f /tmp/osso-product-info ]; then
    _print_file_with_header /tmp/osso-product-info
  else
    _print_command_with_header /usr/bin/osso-product-info
  fi
}

#
# Main program
#

_obtain_configuration

_parse_arguments $*

# if dumping disabled in settings, don't bother going further
if [ x"${coredumping}" == x"false" ]; then
  cat > /dev/null
  exit
fi

# figure out where to dump core
for loc in "/media/mmc1" "/home/user/MyDocs" "/media/mmc2" ; do
    if [ -d $loc/core-dumps ]; then
	_free=`_free_space $loc/core-dumps`
	if [ -n "$_free" -a "$_free" -gt 20000 ]; then
            # more than 20MB free, go ahead
	    core_location="$loc/core-dumps"
	    break;
	fi
    fi
done

if [ x"${core_location}" == x ]; then
  cat > /dev/null
  exit
fi

# If invoked from command line, we won't have a valid pid, so a dummy
# value is assigned instead

if [ x"$NO_SECTION_HEADER" == x"true" ] && [ -z ${core_pid} ]; then
   core_pid=0
fi

# figure out the executable
core_exe=`readlink -f /proc/${core_pid}/exe`
core_exe_basename=${core_exe##*/}

# if process is maemo-invoker, don't create a core
if [ x"${core_exe_basename}" == x"maemo-invoker" ]; then
  cat > /dev/null
  exit
fi


# Only attempt to find the core name if we have a valid PID, try
# alternatives if this is not the case.

if [ "${core_pid}" -gt 0 ]; then
core_tmp=`tr '\0' ' ' < /proc/${core_pid}/cmdline | cut -d' ' -f1`
core_name=${core_tmp##*/}
fi

if [ -z "${core_name}" ]; then
  if [ -n "${core_exe_basename}" ]; then
    core_name="${core_exe_basename}"
  else
    core_name="$DEFAULT_CORE_NAME"
  fi
fi

# Check whether the dump request can be discarded based
# on white/blacklisting rules.

if [ -e /etc/rich-core.include ]; then
    if [ -z "$(grep "^${core_name}$" /etc/rich-core.include)" ]; then
        # Not in the whitelist; do not dump a core
        cat > /dev/null
        exit
    fi
fi
if [ -e /etc/rich-core.exclude ]; then
    if [ -n "$(grep "^${core_name}$" /etc/rich-core.exclude)" ]; then
        # Is in the blacklist; do not dump a core
        cat > /dev/null
        exit
    fi
fi

# count cores per application
if [ ! -d /var/lib/dsme/rich-cores ]; then
  mkdir -p /var/lib/dsme/rich-cores;
fi
counterfile=/var/lib/dsme/rich-cores/${core_name}
echo $(($(cat $counterfile 2>/dev/null)+1)) > $counterfile


# We will lack the value for the signal if we're invoked from the
# script. Provide a dummy value in that case.

if [ -z "${core_sig}" ]; then
  core_sig=0
fi

# Make up a wannabe-unique HW ID from two last octets of WLAN MAC
mac_suffix=$(ifconfig wlan0 | awk 'BEGIN {FS=":"; RS=" *\n"} {print $6$7; exit}')
if [ -z ${mac_suffix} ]; then
    mac_suffix="xxxx";
fi
 
# Make a naming distinction between oopslogs and rich-cores
if [ -n "${IS_OOPSLOG}" ]; then
	timestamp=$(date +%F-%S)
	rcorefilename=${core_location}/oopslog-${mac_suffix}-${timestamp}
else
	rcorefilename=${core_location}/${core_name}-${mac_suffix}-${core_sig}-${core_pid}
fi
(
  _section_date
  _section_osso_software_version
  _section_component_version

# This and some other sections do not make sense or are of little use
# with oopslogs or with --no-section-header, so omitting them
if [ -z "${IS_OOPSLOG}" ]; then
  _section_ifconfig
fi
  _section_df
if [ -z "${IS_OOPSLOG}" ] && [ "${core_pid}" -gt 0 ]; then
  _section_ls_proc
  _section_cmdline
  _section_proc_fd
  _section_proc_smaps
fi
if [ -z "${IS_OOPSLOG}" ]; then
    _section_slabinfo
    # disable xmeminfo for now as it may hang
    # _section_xmeminfo
    _section_proc2csv
fi
if [ x"$INCLUDE_PKGLIST" == x"true" ]; then
  _section_packagelist
fi
if [ x"$INCLUDE_SYSLOG" == x"true" ]; then
  _section_syslog
fi
  _section_osso_product_info
if [ x"$NO_SECTION_HEADER" == x"true" ]; then
cat
elif [ -n "${IS_OOPSLOG}" ]; then
  _print_header oopslog
  cat
else 
  if [ x"$INCLUDE_CORE" == x"true" ]; then
    _print_header coredump
    cat
  fi
fi
) | lzop > ${rcorefilename}.rcore.lzo 
