diff options
Diffstat (limited to 'src/pmlogger/pmnewlog.sh')
-rwxr-xr-x | src/pmlogger/pmnewlog.sh | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/src/pmlogger/pmnewlog.sh b/src/pmlogger/pmnewlog.sh new file mode 100755 index 0000000..ff68936 --- /dev/null +++ b/src/pmlogger/pmnewlog.sh @@ -0,0 +1,702 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 1995-2001,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. +# +# stop and restart a pmlogger instance +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + + +# error messages should go to stderr, not the GUI notifiers +# +unset PCP_STDERR + +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +status=0 +trap "rm -rf $tmp; exit \$status" 0 1 2 3 15 +prog=`basename $0` + +VERBOSE=false +SHOWME=false +CP=cp +MV=mv +RM=rm +KILL=pmsignal +primary=true +myname="primary pmlogger" +connect=primary +access="" +config="" +saveconfig="" +logfile="pmlogger.log" +namespace="" +args="" +sock_me="" +proxyhost="$PMPROXY_HOST" +proxyport="$PMPROXY_PORT" + +cat > $tmp/usage << EOF +# Usage: [options] archive + +pmnewlog options: + -a=FILE,--access=FILE specify access controls for the new pmlogger + -C=FILE,--save=FILE save the configuration of new pmlogger in FILE + -c=FILE,--config=FILE file to load configuration from + -N,--showme perform a dry run, showing what would be done + --namespace + -P,--primary execute as primary logger instance + -p=PID,--pid=PID restart non-primary logger with pid + -s,--socks use pmsocks + -V,--verbose turn on verbose reporting of pmnewlog progress + --help + +pmlogger options: + --debug + -c=FILE,--config=FILE file to load configuration from + -l=FILE, --log=FILE redirect diagnostics and trace output + -L, --linger run even if not primary logger instance and nothing to log + -m=MSG, --note=MSG descriptive note to be added to the port map file + --namespace + -P, --primary execute as primary logger instance + -r, --report report record sizes and archive growth rate + -t=DELTA, --interval=DELTA default logging interval + -T=TIME, --finish=TIME end of the time window + -v=SIZE, --volsize=SIZE switch log volumes after size has been accumulated + -y set timezone for times to local time rather than from PMCD host +EOF + +_abandon() +{ + echo + echo "Sorry, but this is fatal. No new pmlogger instance has been started." + status=1 + exit +} + +_check_pid() +{ + if $SHOWME + then + : + else + _get_pids_by_name pmlogger | grep "^$1\$" + fi +} + +_check_logfile() +{ + if [ ! -f $logfile ] + then + echo "Cannot find pmlogger output file at \"$logfile\"" + else + echo "Contents of pmlogger output file \"$logfile\" ..." + cat $logfile + fi +} + +_check_logger() +{ + # wait until pmlogger process starts, or exits + # + delay=5 + [ ! -z "$PMCD_CONNECT_TIMEOUT" ] && delay=$PMCD_CONNECT_TIMEOUT + x=5 + [ ! -z "$PMCD_REQUEST_TIMEOUT" ] && x=$PMCD_REQUEST_TIMEOUT + + # wait for maximum time of a connection and 20 requests + # + delay=`expr $delay + 20 \* $x` + i=0 + while [ $i -lt $delay ] + do + $VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N ".""$PCP_ECHO_C" + if $SHOWME + then + echo "+ echo 'connect $1' | $pmlc_prefix pmlc ..." + $VERBOSE && echo " done" + return 0 + elif echo "connect $1" | eval $pmlc_prefix pmlc 2>&1 | grep "Unable to connect" >/dev/null + then + : + else + sleep 5 + $VERBOSE && echo " done" + return 0 + fi + + if _get_pids_by_name pmlogger | grep "^$1\$" >/dev/null + then + : + else + $VERBOSE || _message restart + echo " process exited!" + _check_logfile + return 1 + fi + sleep 5 + i=`expr $i + 5` + done + $VERBOSE || _message restart + echo " timed out waiting!" + sed -e 's/^/ /' $tmp/out + _check_logfile + return 1 +} + +_message() +{ + case $1 + in + looking) + $PCP_ECHO_PROG $PCP_ECHO_N "Looking for $myname ...""$PCP_ECHO_C" + ;; + get_host) + $PCP_ECHO_PROG $PCP_ECHO_N "Getting logged host name from $myname ...""$PCP_ECHO_C" + ;; + get_state) + $PCP_ECHO_PROG $PCP_ECHO_N "Contacting $myname to get logging state ...""$PCP_ECHO_C" + ;; + restart) + $PCP_ECHO_PROG $PCP_ECHO_N "Waiting for new pmlogger to start ..""$PCP_ECHO_C" + ;; + esac +} + +_do_cmd() +{ + if $SHOWME + then + echo "+ $1" + else + eval $1 + fi +} + +# option parsing +# +# pmlogger, without -V version which is redefined as -V (verbose) +# and -s exit_size which is redefined as -s (pmsocks), and ignore +# [ -h host ] and [ -x fd ] as they make no sense in the argument +# part of the pmlogger control file for a long-running pmlogger. +# + +ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"` +[ $? != 0 ] && exit 1 + +eval set -- "$ARGS" +while [ $# -gt 0 ] +do + case "$1" + in + +# pmnewlog options and flags +# + + -a) access="$2" + shift + if [ ! -f "$access" ] + then + echo "$prog: Error: cannot find accessfile ($access)" + _abandon + fi + ;; + + -C) saveconfig="$2" + shift + ;; + + -N) SHOWME=true + CP="echo + cp" + MV="echo + mv" + RM="echo + rm" + KILL="echo + kill" + ;; + + -p) pid=$2 + shift + primary=false + myname="pmlogger (process $pid)" + connect=$pid + ;; + + -s) if which pmsocks >/dev/null 2>&1 + then + sock_me="pmsocks " + else + echo "$prog: Warning: no pmsocks available, would run without" + sock_me="" + fi + ;; + + -V) VERBOSE=true + ;; + +# pmlogger options and flags that need special handling +# + + -c) config="$2" + shift + if [ ! -f "$config" ] + then + if [ -f "$PCP_SYSCONF_DIR/pmlogger/$config" ] + then + config="$PCP_SYSCONF_DIR/pmlogger/$config" + else + echo "$prog: Error: cannot find configfile ($config)" + _abandon + fi + fi + ;; + + -l) logfile="$2" + shift + ;; + + -n) namespace="-n $2" + args="${args}$1 $2 " + shift + ;; + + -P) primary=true + myname="primary pmlogger" + ;; + +# pmlogger flags passed through +# + + -L|-r|-y) + args="${args}$1 " + ;; + + -D|-m|-t|-T|-v) + args="${args}$1 $2 " + shift + ;; + + --) # end of options, start of arguments + shift + break + ;; + + -\?) pmgetopt --usage --progname=$prog --config=$tmp/usage + _abandon + ;; + esac + shift +done + +if [ $# -ne 1 ] +then + echo "$prog: Insufficient arguments" >&2 + echo >&2 + pmgetopt --usage --progname=$prog --config=$tmp/usage + _abandon +fi + +# initial sanity checking for new archive name +# +archive="$1" + +# check that designated pmlogger is really running +# +$VERBOSE && _message looking +$PCP_PS_PROG $PCP_PS_ALL_FLAGS \ +| if $primary +then + grep 'pmlogger .*-P' | grep -v grep +else + $PCP_AWK_PROG '$2 == '"$pid"' && /pmlogger/ { print }' +fi >$tmp/out + +if [ -s $tmp/out ] +then + $VERBOSE && echo " found" + $VERBOSE && cat $tmp/out + # expecting something like + # pcp 30019 ... pmlogger ...args... -h hostname ...args... + # pick pid and hostname (safer to do it here if possible because it + # captures the possible -h hostname@proxy construct which is lost + # if one gets the hostname from pmlogger via pmlc) + # + pid=`$PCP_AWK_PROG '{ print $2 }' <$tmp/out` + hostname=`sed -n <$tmp/out -e '/ -h /{ +s/.* -h // +s/ .*// +p +}'` + # may have @proxyhost or @proxyhost:proxyport appended to hostname + # + proxyhost=`echo "$hostname" | sed -n -e '/@/{ +s/.*@// +s/:.*// +p +}'` + proxyport=`echo "$hostname" | sed -n -e '/@.*:/s/.*;//p'` +else + if $VERBOSE + then + : + else + _message looking + echo + fi + echo "$prog: Error: process not found" + _abandon +fi + +if [ -n "$proxyhost" ] +then + if [ -n "$proxyport" ] + then + pmlc_prefix="PMPROXY_HOST=$proxyhost PMPROXY_PORT=$proxyport" + else + pmlc_prefix="PMPROXY_HOST=$proxyhost" + fi +else + pmlc_prefix='' +fi + +# pass primary/not primary down +# +$primary && args="$args-P " + +# pass logfile option down +# +args="$args-l $logfile " + +# if not a primary pmlogger, get name of pmcd host pmlogger is connected to +# +if $primary +then + hostname=localhost +elif [ -z "$hostname" ] +then + # did not get pmcd hostname from ps output above, talk to pmlogger + # via pmlc + # + # start critical section ... no interrupts due to pmlogger SIGPIPE + # bug in PCP 1.1 + # + trap "echo; echo $prog:' Interrupt! ... I am talking to pmlogger, please wait ...'" 1 2 3 15 + $VERBOSE && _message get_host + + _do_cmd "( echo 'connect $connect' ; echo status ) | eval $pmlc_prefix pmlc 2>$tmp/err >$tmp/out" + + # end critical section + # + trap "rm -rf $tmp; exit \$status" 0 1 2 3 15 + + if $SHOWME || [ ! -s $tmp/err ] + then + $VERBOSE && echo " done" + else + if grep "Unable to connect" $tmp/err >/dev/null + then + $VERBOSE || _message get_host + echo " failed to connect" + echo + sed -e 's/^/ /' $tmp/err + _abandon + else + $VERBOSE || _message get_host + echo + echo "$prog: Warning: errors from talking to $myname via pmlc" + sed -e 's/^/ /' $tmp/err + echo + echo "continuing ..." + fi + fi + + if $SHOWME + then + hostname=somehost + else + hostname=`sed -n -e '/^pmlogger/s/.* from host //p' <$tmp/out` + if [ -z "$hostname" ] + then + echo "$prog: Error: failed to get host name from $myname" + echo "This is what was collected from $myname." + echo + sed -e 's/^/ /' $tmp/out + _abandon + fi + args="$args-h $hostname " + fi +else + # got hostname from ps output + # + args="$args-h $hostname " +fi + +# extract/construct config file if required +# +if [ -z "$config" ] +then + # start critical section ... no interrupts due to pmlogger SIGPIPE + # bug in PCP 1.1 + # + trap "echo; echo $prog:' Interrupt! ... I am talking to pmlogger, please wait ...'" 1 2 3 15 + $VERBOSE && _message get_state + + # iterate over top-level names in pmns, and query pmlc for + # current configuration ... note exclusion of "proc" metrics + # ... others may be excluded in a similar fashion + # + if $SHOWME + then + echo "+ ( echo 'connect $connect'; echo 'query ...'; ... ) | eval $pmlc_prefix pmlc $namespace | $PCP_AWK_PROG ..." + else + echo "connect $connect" >$tmp/pmlc.cmd + for top in `pminfo -h $hostname $namespace \ + | sed -e 's/\..*//' -e '/^proc$/d' \ + | sort -u` + do + echo "query $top" >>$tmp/pmlc.cmd + done + eval $pmlc_prefix pmlc $namespace <$tmp/pmlc.cmd 2>$tmp/err \ + | $PCP_AWK_PROG >$tmp/out ' +/^[^ ]/ { metric = $1; next } +$1 == "mand" || ( $1 == "adv" && $2 == "on" ) { print $0 " " metric }' + fi + + # end critical section + # + trap "rm -rf $tmp; exit \$status" 0 1 2 3 15 + + if $SHOWME || [ ! -s $tmp/err ] + then + $VERBOSE && echo " done" + else + if grep "Unable to connect" $tmp/err >/dev/null + then + $VERBOSE || _message get_state + echo " failed to connect" + echo + sed -e 's/^/ /' $tmp/err + _abandon + else + $VERBOSE || _message get_state + echo + echo "$prog: Warning: errors from talking to $myname via pmlc" + sed -e 's/^/ /' $tmp/err + echo + echo "continuing ..." + fi + fi + + if [ ! -s $tmp/out ] + then + if $SHOWME + then + : + else + echo "$prog: Error: failed to collect configuration info from $myname" + echo "Most likely this pmlogger instance is inactive." + _abandon + fi + fi + + # convert to a pmlogger config file + # + if $SHOWME + then + echo "+ create new pmlogger config file ..." + else + sed <$tmp/out >$tmp/config \ + -e 's/ on nl/ on/' \ + -e 's/ off nl/ off/'\ + -e 's/ *mand *\(o[nf]*\) /log mandatory \1 /' \ + -e 's/ *adv *on /log advisory on/' \ + -e 's/\[[0-9][0-9]* or /[/' \ + -e 's/\(\[[^]]*]\) \([^ ]*\)/\2 \1/' \ + -e 's/ */ /g' + + if [ ! -s $tmp/config ] + then + echo "$prog: Error: failed to generate a pmlogger configuration file for pmlogger" + echo "This is what was collected from $myname." + echo + sed -e 's/^/ /' $tmp/out + _abandon + fi + fi + config=$tmp/config +fi + +# optionally append access control specifications +# +if [ -n "$access" ] +then + if grep '\[access]' $config >/dev/null + then + echo "$prog: Error: pmlogger configuration file already contains an" + echo " access control section, specifications from \"$access\" cannot" + echo " be applied." + _abandon + fi + cat $access >>$config +fi + +# add config file to the args, save config file if -C +# +args="$args-c $config " +if [ -n "$saveconfig" ] +then + if eval $CP $config $saveconfig + then + echo "New pmlogger configuration file saved as $saveconfig" + else + echo "$prog: Warning: unable to save configuration file as $saveconfig" + fi +fi + +# kill off existing pmlogger +# +$VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "Terminating $myname ...""$PCP_ECHO_C" +for sig in USR1 TERM KILL +do + $VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N " SIG$sig ...""$PCP_ECHO_C" + eval $KILL -s $sig $pid + sleep 5 + [ "`_check_pid $pid`" = "" ] && break +done + +if [ "`_check_pid $pid`" = "" ] +then + $VERBOSE && echo " done" +else + echo " failed!" + _abandon +fi + +# the archive folio Latest is for the most recent archive in this directory +# +dir=`dirname $archive` +eval $RM -f $dir/Latest + +# clean up port-map, just in case +# +PM_LOG_PORT_DIR="$PCP_TMP_DIR/pmlogger" +eval $RM -f "$PM_LOG_PORT_DIR"/$pid +$primary && eval $RM -f $PM_LOG_PORT_DIR/primary + +# finally do it, ... +# +cd $dir +$SHOWME && echo "+ cd $dir" +[ "$dir" = . ] && dir=`pwd` +archive=`basename $archive` + +# handle duplicates/aliases (happens when pmlogger is restarted +# within a minute and basename is the same) +# +suff='' +for file in $archive.* +do + [ "$file" = "$archive"'.*' ] && continue + # we have a clash! ... find a new -number suffix for the + # existing files ... we are going to keep $archive for the + # new pmlogger below + # + if [ -z "$suff" ] + then + for xx in 0 1 2 3 4 5 6 7 8 9 + do + for yy in 0 1 2 3 4 5 6 7 8 9 + do + [ "`echo $archive-${xx}${yy}.*`" != "$archive-${xx}${yy}.*" ] && continue + suff=${xx}$yy + break + done + [ ! -z "$suff" ] && break + done + if [ -z "$suff" ] + then + echo "$prog: Error: unable to break duplicate clash for archive basename \"$archive\"" + _abandon + fi + $VERBOSE && echo "Duplicate archive basename ... rename $archive.* files to $archive-$suff.*" + fi + eval $MV -f $file `echo $file | sed -e "s/$archive/&-$suff/"` +done + +$VERBOSE && echo "Launching new pmlogger in directory \"$dir\" as ..." +[ -f $logfile ] && eval $MV -f $logfile $logfile.prior +$VERBOSE && echo "${sock_me}pmlogger $args$archive" + +if $SHOWME +then + echo "+ ${sock_me}pmlogger $args$archive &" + echo "+ ... assume pid is 12345" + new_pid=12345 +else + ${sock_me}pmlogger $args$archive & + new_pid=$! +fi + +# stall a bit ... +# +STALL_TIME=10 +sleep $STALL_TIME + +$VERBOSE && _message restart +if _check_logger $new_pid || $SHOWME +then + $VERBOSE && echo "New pmlogger status ..." + $VERBOSE && _do_cmd "( echo 'connect $new_pid'; echo status ) | $pmlc_prefix pmlc" + + # make the "Latest" archive folio + # + i=0 + failed=true + WAIT_TIME=10 + while [ $i -lt $WAIT_TIME ] + do + if $SHOWME || [ -f $archive.0 -a -f $archive.meta -a $archive.index ] + then + _do_cmd "mkaf $archive.0 >Latest" 2>$tmp/err + if [ -s $tmp/err ] + then + # errors from mkaf typically result from race conditions + # at the start of pmlogger, e.g. + # Warning: cannot extract hostname from archive "..." ... + # + # simply keep trying + : + else + failed=false + break + fi + fi + sleep 1 + i=`expr $i + 1` + done + + if $failed + then + ELAPSED=`expr $STALL_TIME + $WAIT_TIME` + echo "Warning: pmlogger [pid=$new_pid host=$hostname] failed to create archive files within $ELAPSED seconds" + if [ -f $tmp/err ] + then + echo "Warnings/errors from mkaf ..." + cat $tmp/err + fi + fi +else + _abandon +fi + +exit |