#!/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) 2014 Red Hat. # 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. # # Get standard environment . $PCP_DIR/etc/pcp.env status=1 tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 trap "rm -rf $tmp; exit \$status" 0 1 2 3 15 #debug# tmp=`pwd`/tmp prog=`basename $0` cat > $tmp/usage << EOF # Usage: [options] configfile Options: -c add message and timestamp (not for interactive use) -d=DIR,--groups=DIR specify path to the pmlogconf groups directory --host -q,--quiet quiet, suppress logging interval dialog -r,--reprobe every group reconsidered for inclusion in configfile -v,--verbose increase diagnostic verbosity --help EOF _usage() { pmgetopt --progname=$prog --config=$tmp/usage --usage exit } quick=false pat='' prompt=true reprobe=false autocreate=false BASE='' HOST='' verbose=false setupflags='' ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"` [ $? != 0 ] && exit 1 eval set -- "$ARGS" while [ $# -gt 0 ] do case "$1" in -c) # automated, non-interactive file creation autocreate=true prompt=false ;; -d) # base directory for the group files BASE="$2" shift ;; -h) # host to contact for "probe" tests HOST="$2" shift ;; -q) # "quick" mode, don't change logging intervals quick=true ;; -r) # reprobe reprobe=true ;; -v) # verbose verbose=true setupflags="$setupflags -v" ;; --) # end options shift break ;; -\?) _usage ;; esac shift done [ $# -eq 1 ] || _usage if [ -n "$BASE" -a ! -d "$BASE" ] then echo "$prog: Error: base directory ($BASE) for group files does not exist" exit fi config="$1" # 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=local: if $PCP_BINADM_DIR/pmlogconf-setup -h $HOST $setupflags $BASE/"$tag" 2>$tmp/err >$tmp/out then : else echo "$prog: Warning: $BASE/$tag: pmlogconf-setup failed" sts=1 fi sed -e "s;$BASE/;;" <$tmp/out >$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=local: if $PCP_BINADM_DIR/pmlogconf-setup -h $HOST $setupflags $BASE/"$tag" 2>$tmp/err >$tmp/out then : else echo "$prog: Warning: $BASE/$tag: pmlogconf-setup failed" sts=1 fi sed -e "s;$BASE/;;" <$tmp/out >$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 "\" \", where 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 fi $PCP_ECHO_PROG "Creating config file \"$config\" using default settings ..." prompt=false new=true [ -z "$HOST" ] && HOST=local: [ -z "$BASE" ] && BASE=$PCP_VAR_DIR/config/pmlogconf cat <$tmp/in #pmlogconf 2.0 # # pmlogger(1) config file created and updated by pmlogconf End-of-File $autocreate && echo "# Auto-generated by pmlogconf on: "`date` >>$tmp/in cat <>$tmp/in # # 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 if $PCP_BINADM_DIR/pmlogconf-setup -h $HOST $setupflags "$tag" 2>$tmp/err >$tmp/out then : else echo "$prog: Warning: $BASE/$tag: pmlogconf-setup failed" [ -s $tmp/err ] && cat $tmp/err sts=1 fi sed -e "s;$BASE/;;" <$tmp/out >>$tmp/in [ -s $tmp/err ] && cat $tmp/err done cat <>$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 ... # [access] disallow .* : all; disallow :* : all; allow local:* : enquire; 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 fi else echo "$prog: Error: existing \"$config\" is not a $prog control file" exit fi if [ ! -w "$config" ] then echo "$prog: Error: existing config file \"$config\" is not writeable" exit 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 status=0 exit