#!/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"
      ;;
      --name=*)
      argument_core_name=${1#--name=}
      ;;
      --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_syslog_files_with_header()
{
    _print_header $1
    if [ -f /tmp/Xorg.0.log ]; then
	_print_separator /tmp/Xorg.0.log
	cat /tmp/Xorg.0.log
    fi
    if [ -x /bin/dmesg ]; then
	_print_separator dmesg
	/bin/dmesg
    fi
    if [ -f $1 ]; then
	_print_separator $1
	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'

  df -k $1 | awk '/\/dev\//{print $4}'
}

#
# 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_syslog_files_with_header /var/ftd-log/syslog
  else
    _print_syslog_files_with_header /var/log/syslog
  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, check can we create a file there 
            touch $loc/core-dumps/testfile
            retval=$?
            if [ $retval -eq 0 ]; then
		rm $loc/core-dumps/testfile
	        core_location="$loc/core-dumps"
                break;
	   fi
	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

# in most cases, corename comes from argument.
# if not from  there, then try based on /proc/PID
core_name="$argument_core_name"

if [ "${core_pid}" -gt 0 ]; then
  # 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

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

# if still no core name, revert to exe name or "unknown"
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
    /bin/grep -q "${core_name}" /etc/rich-core.include
    if [ $? -ne 0 ]; then
        # Not in the whitelist; do not dump a core
        cat > /dev/null
        exit
    fi
fi
if [ -e /etc/rich-core.exclude ]; then
    /bin/grep -q "${core_name}" /etc/rich-core.exclude
    if [ $? -eq 0 ]; then
        # Is in the blacklist; do not dump a core
        cat > /dev/null
        exit
    fi
fi

# 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
# Collect process specific information first, only then system
# as process may disappear while this info is collected
(
if [ -z "${IS_OOPSLOG}" ] && [ "${core_pid}" -gt 0 ]; then
  _section_cmdline
  _section_ls_proc
  _section_proc_fd
  _section_proc_smaps
fi
  _section_date
  _section_osso_software_version
  _section_component_version
  _section_df
  _section_ifconfig

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 

# 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

