#!/bin/sh
#
# pmlogconf - generate/edit a pmlogger configuration file
#
# control lines have this format
# #+ tag:on-off:delta
# where
#	tag	is arbitrary (no embedded :'s) and unique
#	on-off	y or n to enable or disable this group, else
#		x for groups excluded by probing from pmlogconf-setup
#		when the group was added to the configuration file
#	delta	delta argument for pmlogger "logging ... on delta" clause
#
# Copyright (c) 1998,2003 Silicon Graphics, Inc.  All Rights Reserved.
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
# 
# 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.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

# Get standard environment
. $PCP_DIR/etc/pcp.env

prog=`basename $0`

_usage()
{
    echo "Usage: $prog [-qrv] [-d groupsdir] [-h hostname] configfile"
    exit 1
}

quick=false
pat=''
prompt=true
reprobe=false
BASE=''
HOST=''
verbose=false
setupflags=''

while getopts d:h:qrv? c
do
    case $c
    in
	d)	# base directory for the group files
		BASE="$OPTARG"
		;;

	h)	# host to contact for "probe" tests
		HOST=$OPTARG
		;;

	q)	# "quick" mode, don't change logging intervals
		quick=true
		;;

	r)	# reprobe
		reprobe=true
		;;

	v)	# verbose
		verbose=true
		setupflags="$setupflags -v"
		;;

	?)	# eh?
		_usage
		# NOTREACHED
		;;
    esac
done
shift `expr $OPTIND - 1`

if [ $# -ne 1 ]
then
    _usage
    # NOTREACHED
fi

if [ ! -d $BASE ]
then
    echo "$prog: Error: base directory ($BASE) for group files does not exist"
    exit 1
fi

config=$1

tmp=/var/tmp/$$
trap "rm -f $tmp.*; exit \$sts" 0 1 2 3 15
#debug# tmp=`pwd`/tmp
rm -f $tmp.*

# split $tmp.ctl at the line containing the unprocessed tag to
# produce
# 	$tmp.head
# 	$tmp.tag	- one line
# 	$tmp.tail
#
_split()
{
    rm -f $tmp.head $tmp.tag $tmp.tail
    $PCP_AWK_PROG <$tmp.ctl '
BEGIN						{ out = "'"$tmp.head"'" }
/DO NOT UPDATE THE FILE ABOVE/			{ seen = 1 }
seen == 0 && /^#\? [^:]*:[ynx]:/		{ print >"'"$tmp.tag"'"
						  out = "'"$tmp.tail"'"
						  seen = 1
						  next
						}
						{ print >out }'
}

# do all of the real iterative work
#
_update()
{
    # strip the existing pmlogger config and leave the comments
    # and the control lines
    #

    $PCP_AWK_PROG <$tmp.in >$tmp.ctl '
/DO NOT UPDATE THE FILE ABOVE/	{ tail = 1 }
tail == 1			{ print; next }
/^\#\+ [^:]*:[ynx]:/		{ sub(/+/, "?", $1); print; skip = 1; next }
skip == 1 && /^\#----/		{ skip = 0; next }
skip == 1			{ next }
				{ print }'

    # now need to be a little smarter ... tags may have appeared or
    # disappeared from the shipped defaults, so need to munge the contents
    # of $tmp.ctl to reflect this
    #
    find $BASE -type f \
    | sed \
	-e "s;$BASE/;;" \
	-e '/^v1.0\//d' \
    | LC_COLLATE=POSIX sort \
    | while read tag
    do
	if sed 1q <$BASE/"$tag" | grep '^#pmlogconf-setup 2.0' >/dev/null
	then
	    :
	else
	    # not one of our group files, skip it ...
	    continue
	fi
	if grep "^#? $tag:" $tmp.ctl >/dev/null
	then
	    :
	else
	    $verbose && echo "need to add new group tag=$tag"
	    rm -f $tmp.pre $tmp.post
	    $PCP_AWK_PROG <$tmp.ctl '
BEGIN						{ out = "'"$tmp.pre"'" }
/DO NOT UPDATE THE FILE ABOVE/			{ out = "'"$tmp.post"'" }
						{ print >out }'
	    mv $tmp.pre $tmp.ctl
	    [ -z "$HOST" ] && HOST=localhost
	    $PCP_BINADM_DIR/pmlogconf-setup -h $HOST $setupflags $BASE/"$tag" 2>$tmp.err \
	    | sed -e "s;$BASE/;;" >$tmp.tmp
	    [ -s $tmp.err ] && cat $tmp.err
	    sed -e '/^#+/s/+/?/' <$tmp.tmp >>$tmp.ctl
	    cat $tmp.post >>$tmp.ctl
	fi
    done

    while true
    do
	_split
	[ ! -s $tmp.tag ] && break
	eval `sed <$tmp.tag -e 's/^#? /tag="/' -e 's/:/" onoff="/' -e 's/:/" delta="/' -e 's/:.*/"/'`
	[ -z "$delta" ] && delta=default

	if $reprobe
	then
	    [ -z "$HOST" ] && HOST=localhost
	    $PCP_BINADM_DIR/pmlogconf-setup -h $HOST $setupflags $BASE/"$tag" 2>$tmp.err \
	    | sed -e "s;$BASE/;;" >$tmp.tmp
	    [ -s $tmp.err ] && cat $tmp.err
	    if [ -s $tmp.tmp ]
	    then
		eval `sed <$tmp.tmp -e 's/^#+ /tag_r="/' -e 's/:/" onoff_r="/' -e 's/:/" delta_r="/' -e 's/:.*/"/'`
		[ -z "$delta_r" ] && delta_r=default
		if [ "$tag" != "$tag_r" ]
		then
		    echo "Botch: reprobe for $tag found new tag ${tag_r}, no change"
		    cat $tmp.tmp
		else
		    if [ "$onoff" = y ]
		    then
			# existing y takes precedence
			if [ "$onoff_r" = x ]
			then
			    echo "Warning: reprobe for $tag suggests exclude, keeping current include status"
			fi
		    else
			onoff=$onoff_r
			[ "$delta" != "default" ] && delta=$delta_r
		    fi
		fi
	    fi
	fi

	case $onoff
	in
	    y|n)    ;;
	    x)      # excluded group from setup
		    cat $tmp.head >$tmp.ctl
		    echo "#+ $tag:x::" >>$tmp.ctl
		    echo "#----" >>$tmp.ctl
		    cat $tmp.tail >>$tmp.ctl
		    continue
		    ;;
	    *)	echo "Warning: tag=$tag onoff is illegal ($onoff) ... setting to \"n\""
		    onoff=n
		    ;;
	esac

	if [ -f $BASE/$tag ]
	then
	    eval `$PCP_AWK_PROG <$BASE/$tag '
BEGIN		{ desc = ""; metrics = "" }
$1 == "ident"	{ if (desc != "") desc = desc "\n"
		  for (i = 2; i <= NF; i++) {
		      if (i == 2) desc = desc $2
		      else desc = desc " " $i
		  }
		  next
		}
END		{ printf "desc='"'"'%s'"'"'\n",desc }'`

	    sed -n <$BASE/$tag >$tmp.metrics \
		-e '/^[ 	]/s/[ 	]*//p'
	    #debug# echo $tag:
	    #debug# echo "desc: $desc"
	else
	    case "$tag"
	    in
		v1.0/*)
			# from migration, silently do nothing
			;;
		*)
			echo "Warning: cannot find group file ($tag) ... no change is possible"
			;;
	    esac
	    $PCP_AWK_PROG <"$config" >>$tmp.head '
BEGIN			{ tag="'"$tag"'" }
$1 == "#+" && $2 ~ tag	{ want = 1 }
want == 1		{ print }
want == 1 && /^#----/	{ exit }'
	    cat $tmp.head $tmp.tail >$tmp.ctl
	    continue
	fi

	if [ ! -z "$pat" ]
	then
	    if echo "$desc" | grep "$pat" >/dev/null
	    then
		pat=''
		prompt=true
	    fi
	    if grep "$pat" $tmp.metrics >/dev/null
	    then
		pat=''
		prompt=true
	    fi
	fi
	if $prompt
	then
	    # prompt for answers
	    #
	    echo
	    was_onoff=$onoff
	    echo "Group: $desc" | fmt -74 | sed -e '1!s/^/       /'
	    while true
	    do
		$PCP_ECHO_PROG $PCP_ECHO_N "Log this group? [$onoff] ""$PCP_ECHO_C"
		read ans
		if [ "$ans" = "?" ]
		then
		    echo 'Valid responses are:
m         report the names of the metrics in this group
n         do not log this group
q         quit; no change for this or any of the following groups
y         log this group
/pattern  no change for this group and search for a group containing pattern
	  in the description or the metrics associated with the group'
		    continue
		fi
		if [ "$ans" = m ]
		then
		    echo "Metrics in this group ($tag):"
		    sed -e 's/^/    /' $tmp.metrics
		    continue
		fi
		if [ "$ans" = q ]
		then
		    # quit ...
		    ans="$onoff"
		    prompt=false
		fi
		pat=`echo "$ans" | sed -n 's/^\///p'`
		if [ ! -z "$pat" ]
		then
		    echo "Searching for \"$pat\""
		    ans="$onoff"
		    prompt=false
		fi
		[ -z "$ans" ] && ans="$onoff"
		[ "$ans" = y -o "$ans" = n ] && break
		echo "Error: you must answer \"m\" or \"n\" or \"q\" or \"y\" or \"/pattern\" ... try again"
	    done
	    onoff="$ans"
	    if [ $prompt = true -a "$onoff" = y ]
	    then
		if $quick
		then
		    if [ $was_onoff = y ]
		    then
			# no change, be quiet
			:
		    else
			echo "Logging interval: $delta"
		    fi
		else
		    while true
		    do
			$PCP_ECHO_PROG $PCP_ECHO_N "Logging interval? [$delta] ""$PCP_ECHO_C"
			read ans
			if [ -z "$ans" ]
			then
			    # use suggested value, assume this is good
			    #
			    ans="$delta"
			    break
			else
			    # do some sanity checking ...
			    #
			    ok=`echo "$ans" \
			        | sed -e 's/^every //' \
				| $PCP_AWK_PROG '
/^once$/			{ print "true"; exit }
/^default$/			{ print "true"; exit }
/^[0-9][0-9]* *msec$/		{ print "true"; exit }
/^[0-9][0-9]* *msecs$/		{ print "true"; exit }
/^[0-9][0-9]* *millisecond$/	{ print "true"; exit }
/^[0-9][0-9]* *milliseconds$/	{ print "true"; exit }
/^[0-9][0-9]* *sec$/		{ print "true"; exit }
/^[0-9][0-9]* *secs$/		{ print "true"; exit }
/^[0-9][0-9]* *second$/		{ print "true"; exit }
/^[0-9][0-9]* *seconds$/	{ print "true"; exit }
/^[0-9][0-9]* *min$/		{ print "true"; exit }
/^[0-9][0-9]* *mins$/		{ print "true"; exit }
/^[0-9][0-9]* *minute$/		{ print "true"; exit }
/^[0-9][0-9]* *minutes$/	{ print "true"; exit }
/^[0-9][0-9]* *hour$/		{ print "true"; exit }
/^[0-9][0-9]* *hours$/		{ print "true"; exit }
				{ print "false"; exit }'`
			    if $ok
			    then
				delta="$ans"
				break
			    else

				echo "Error: logging interval must be of the form \"once\" or \"default\" or"
				echo "\"<integer> <scale>\", where <scale> is one of \"sec\", \"secs\", \"min\","
				echo "\"mins\", etc ... try again"
			    fi
			fi
		    done
		fi
	    fi
	else
	    $PCP_ECHO_PROG $PCP_ECHO_N ".""$PCP_ECHO_C"
	fi

	echo "#+ $tag:$onoff:$delta:" >>$tmp.head
	echo "$desc" | fmt | sed -e 's/^/## /' >>$tmp.head
	if [ "$onoff" = y ]
	then
	    if [ -s $tmp.metrics ]
	    then
		echo "log advisory on $delta {" >>$tmp.head
		sed -e 's/^/	/' <$tmp.metrics >>$tmp.head
		echo "}" >>$tmp.head
	    fi
	fi
	echo "#----" >>$tmp.head
	cat $tmp.head $tmp.tail >$tmp.ctl

    done
}

if [ ! -f "$config" ]
then
    # create a new config file
    #
    touch "$config"
    if [ ! -f "$config" ]
    then
	echo "$prog: Error: config file \"$config\" does not exist and cannot be created"
	exit 1
    fi

    $PCP_ECHO_PROG "Creating config file \"$config\" using default settings ..."
    prompt=false
    new=true
    [ -z "$HOST" ] && HOST=localhost
    [ -z "$BASE" ] && BASE=$PCP_VAR_DIR/config/pmlogconf

    cat <<End-of-File >$tmp.in
#pmlogconf 2.0
#
# pmlogger(1) config file created and updated by pmlogconf
#
# DO NOT UPDATE THE INITIAL SECTION OF THIS FILE.
# Any changes may be lost the next time pmlogconf is used
# on this file.
#
#+ groupdir $BASE
#
End-of-File

    find $BASE -type f \
    | sed \
	-e '/\/v1.0\//d' \
    | LC_COLLATE=POSIX sort \
    | while read tag
    do
	if sed 1q <"$tag" | grep '^#pmlogconf-setup 2.0' >/dev/null
	then
	    :
	else
	    # not one of our group files, skip it ...
	    continue
	fi
	$PCP_BINADM_DIR/pmlogconf-setup -h $HOST $setupflags "$tag" 2>$tmp.err \
	| sed -e "s;$BASE/;;" >>$tmp.in
	[ -s $tmp.err ] && cat $tmp.err
    done

    cat <<End-of-File >>$tmp.in

# DO NOT UPDATE THE FILE ABOVE THIS LINE
# Otherwise any changes may be lost the next time pmlogconf is
# used on this file.
#
# It is safe to make additions from here on ...
#

End-of-File

else
    # updating an existing config file
    #
    new=false
    magic=`sed 1q "$config"`
    if echo "$magic" | grep "^#pmlogconf" >/dev/null
    then
	version=`echo $magic | sed -e "s/^#pmlogconf//" -e 's/^  *//'`
	if [ "$version" = "1.0" ]
	then
	    echo "$prog: migrating \"$config\" from version 1.0 to 2.0 ..."
	    [ -z "$BASE" ] && BASE=$PCP_VAR_DIR/config/pmlogconf
	    sed <"$config" >$tmp.in \
		-e '1s/1\.0/2.0/' \
		-e "/# on this file./a\\
#\\
#+ groupdir $BASE" \
		-e '/^#\+/{
s; C0:; cpu/summary:;
s; C1:; cpu/percpu:;
s; C2:; v1.0/C2:;
s; C3:; v1.0/C3:;
s; D0:; disk/summary:;
s; D1:; disk/percontroller:;
s; D2:; disk/perdisk:;
s; D3:; v1.0/D3:;
s; F0:; filesystem/all:;
s; F1:; filesystem/xfs-io-irix:;
s; F2:; filesystem/xfs-all:;
s; F3:; sgi/xlv-activity:;
s; F4:; sgi/xlv-stripe-io:;
s; F5:; sgi/efs:;
s; F6:; sgi/xvm-ops:;
s; F7:; sgi/xvm-stats:;
s; F8:; sgi/xvm-all:;
s; H0:; sgi/craylink:;
s; H1:; sgi/hub:;
s; H2:; sgi/cpu-evctr:;
s; H3:; sgi/xbow:;
s; I0:; platform/hinv:;
s; K0:; v1.0/K0:;
s; K1:; kernel/syscalls-irix:;
s; K2:; kernel/syscalls-percpu-irix:;
s; K3:; kernel/read-write-data:;
s; K4:; kernel/interrupts-irix:;
s; K5:; kernel/bufcache-activity:;
s; K6:; kernel/bufcache-all:;
s; K7:; kernel/vnodes:;
s; K8:; kernel/inode-cache:;
s; K9:; sgi/kaio:;
s; Ka:; kernel/queues-irix:;
s; M0:; memory/swap-activity:;
s; M1:; memory/tlb-irix:;
s; M2:; kernel/memory-irix:;
s; M3:; memory/swap-all:;
s; M4:; memory/swap-config:;
s; M5:; sgi/node-memory:;
s; M6:; sgi/numa:;
s; M7:; sgi/numa-summary:;
s; N0:; networking/interface-summary:;
s; N1:; networking/interface-all:;
s; N2:; networking/tcp-activity-irix:;
s; N3:; networking/tcp-all:;
s; N4:; networking/udp-packets-irix:;
s; N5:; networking/udp-all:;
s; N6:; networking/socket-irix:;
s; N7:; networking/other-protocols:;
s; N8:; networking/mbufs:;
s; N9:; networking/multicast:;
s; Na:; networking/streams:;
s; S0:; v1.0/S0:;
s; S1:; v1.0/S1:;
s; S2:; networking/rpc:;
}'
	    reprobe=true
	elif [ "$version" = "2.0" ]
	then
	    # start with existing config file
	    #
	    cp "$config" $tmp.in
	else
	    echo "$prog: Error: existing config file \"$config\" is wrong version ($version)"
	    exit 1
	fi
    else
	echo "$prog: Error: existing \"$config\" is not a $prog control file"
	exit 1
    fi
    if [ ! -w "$config" ]
    then
	echo "$prog: Error: existing config file \"$config\" is not writeable"
	exit 1
    fi

    [ -n "$HOST" ] && echo "$prog: Warning: existing config file, -h $HOST will be ignored"

    CBASE=`sed -n -e '/^#+ groupdir /s///p' <$tmp.in`
    if [ -z "$BASE" ]
    then
	BASE="$CBASE"
    else
	if [ "$BASE" != "$CBASE" ]
	then
	    echo "$prog: Warning: using base directory for group files from command line ($BASE) which is different from that in $config ($CBASE)"
	fi
    fi
fi

while true
do
    _update

    [ -z "$pat" ] && break

    echo " not found."
    while true
    do
	$PCP_ECHO_PROG $PCP_ECHO_N "Continue searching from start of the file? [y] ""$PCP_ECHO_C"
	read ans
	[ -z "$ans" ] && ans=y
	[ "$ans" = y -o "$ans" = n ] && break
	echo "Error: you must answer \"y\" or \"n\" ... try again"
    done
    mv $tmp.ctl $tmp.in
    if [ "$ans" = n ]
    then
	pat=''
	prompt=true
    else
	echo "Searching for \"$pat\""
    fi
done

if $new
then
    echo
    cp $tmp.ctl "$config"
else
    echo
    if diff "$config" $tmp.ctl >/dev/null
    then
	echo "No changes"
    else
	echo "Differences ..."
	${DIFF-diff} -c "$config" $tmp.ctl
	while true
	do
	    $PCP_ECHO_PROG $PCP_ECHO_N "Keep changes? [y] ""$PCP_ECHO_C"
	    read ans
	    [ -z "$ans" ] && ans=y
	    [ "$ans" = y -o "$ans" = n ] && break
	    echo "Error: you must answer \"y\" or \"n\" ... try again"
	done
	[ "$ans" = y ] && cp $tmp.ctl "$config"
    fi
fi

exit 0
