summaryrefslogtreecommitdiff
path: root/src/pmlogger/pmnewlog.sh
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmlogger/pmnewlog.sh')
-rwxr-xr-xsrc/pmlogger/pmnewlog.sh702
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