ast_coredumper: Refactor the pid determination process
authorGeorge Joseph <gjoseph@digium.com>
Mon, 24 Dec 2018 17:42:36 +0000 (10:42 -0700)
committerSean Bright <sean.bright@gmail.com>
Mon, 24 Dec 2018 19:17:38 +0000 (14:17 -0500)
In order to get a dump of the running process, we need to find the
pid of the main asterisk process.  This can be tricky if there are
also instances of "asterisk -r" running or if an alternate location
for asterisk.conf was specified on the command line with the -C
option that also specified an alternation location for the pid file.

So now...

1. We find the asterisk executable with "which" or the --asterisk-bin
   command line option.
2. If there's only 1 process with an executable path that matches,
   we use that pid.  If not...
3. We try "<asterisk-bin> -rx 'core show settings'" and parse the
   output to find the pidfile, then read that for the pid.  If that
   didn't work...
4. We get a list of all the pids matching <asterisk-bin> and look
   in /proc/<pid>/cmdline for a -C argument and retry the "core show
   settings" using the same -C option.  We can't parse the output
   of "ps" to get the -C path because it may contain spaces.  The
   contents of /proc/<pid>/cmdline are delimited by NULLs.  For BSDs
   we may have to mount /proc first. :(

ASTERISK-28221
Reported by: Andrew Nagy

Change-Id: I8aa1f3f912f949df2b5348908803c636bde1d57c

contrib/scripts/ast_coredumper

index 0215edb..defb629 100755 (executable)
@@ -372,42 +372,97 @@ fi
 # Timestamp to use for output files
 df=${tarball_uniqueid:-$(${DATEFORMAT})}
 
-if [ -z "$asterisk_bin" ]; then
+if [ x"$asterisk_bin" = x ]; then
        asterisk_bin=$(which asterisk)
 fi
 
 if $running || $RUNNING ; then
        # We need to go through some gyrations to find the pid of the running
        # MAIN asterisk process and not someone or something running asterisk -r.
-       # The pid file may NOT be in /var/run/asterisk so we need to find any
-       # running asterisk process and see if -C was specified on the command
-       # line.  The chances of more than 1 asterisk instance running with
-       # different -C options is so unlikely that we're going to ignore it.
-       #
-       # 'ps axo command' should work on Linux (back to CentOS6) and FreeBSD.
-       # If asterisk was started with -C, get the asterisk.conf file.
-       # If it wasn't, assume /etc/asterisk/asterisk.conf
-       astetcconf=`ps axo command | sed -n -r -e "s/.*asterisk\s+.*-C\s+([^ ]+).*/\1/gp" | tail -1`
-       [ x$astetcconf = x ] && astetcconf=/etc/asterisk/asterisk.conf
-       # Now parse out astrundir and cat asterisk.pid
-       astrundir=$(sed -n -r -e "s/astrundir\s+[=>]+\s+(.*)/\1/gp" $astetcconf)
-       pid=$(cat $astrundir/asterisk.pid 2>/dev/null || : )
-       if [ x$pid = x ] ; then
-               echo "Asterisk is not running"
-       else
-               if $RUNNING ; then
-                       answer=Y
-               else
-                       read -p "WARNING:  Taking a core dump of the running asterisk instance will suspend call processing while the dump is saved.  Do you wish to continue? (y/N) " answer
+
+       unset pid
+
+       # Simplest case first...
+       pids=$(pgrep -f "$asterisk_bin")
+       pidcount=$(echo $pids | wc -w)
+
+       if [ $pidcount -eq 0 ] ; then
+               >&2 echo "Asterisk is not running"
+               exit 1
+       fi
+
+       # Single process, great.
+       if [ $pidcount -eq 1 ] ; then
+               pid=$pids
+               echo "Found a single asterisk instance running as process $pid"
+       fi
+
+       # More than 1 asterisk process running
+       if [ x"$pid" = x ] ; then
+               # More than 1 process running, let's try asking asterisk for it's
+               # pidfile
+               pidfile=$("$asterisk_bin" -rx "core show settings" 2>/dev/null | sed -n -r -e "s/^\s*pid file:\s+(.*)/\1/gpi")
+               # We found it
+               if [ x"$pidfile" != x -a -f "$pidfile" ] ; then
+                       pid=$(cat "$pidfile")
+                       echo "Found pidfile $pidfile with process $pid"
                fi
-               if [[ "$answer" =~ ^[Yy] ]] ; then
-                       cf="${OUTPUTDIR:-/tmp}/core-asterisk-running-$df"
-                       echo "Dumping running asterisk process to $cf"
-                       ${GDB} ${asterisk_bin} -p $pid -q --batch --ex "gcore $cf" >/dev/null 2>&1
-                       COREDUMPS+=("$cf")
-               else
-                       echo "Skipping dump of running process"
+       fi
+
+       # It's possible that asterisk was started with the -C option which means the
+       # control socket and pidfile might not be where we expect.  We're going to
+       # have to parse the process arguments to see if -C was specified.
+       # The first process that has a -C argument determines which config
+       # file to use to find the pidfile of the main process.
+       # NOTE: The ps command doesn't quote command line arguments that it
+       # displays so we need to look in /proc/<pid>/cmdline.
+
+       if [ x"$pid" = x ] ; then
+               # BSDs might not mount /proc by default :(
+               mounted_proc=0
+               if uname -o | grep -qi "bsd" ; then
+                       if ! mount | grep -qi "/proc" ; then
+                               echo "Temporarily mounting /proc"
+                               mounted_proc=1
+                               mount -t procfs proc /proc
+                       fi
                fi
+
+               for p in $pids ; do
+                       # Fields in cmdline are delimited by NULLs
+                       astetcconf=$(sed -n -r -e "s/.*\x00-C\x00([^\x00]+).*/\1/gp" /proc/$p/cmdline)
+                       if [ x"$astetcconf" != x ] ; then
+                               pidfile=$("$asterisk_bin" -C "$astetcconf" -rx "core show settings" 2>/dev/null | sed -n -r -e "s/^\s*pid file:\s+(.*)/\1/gpi")
+                               if [ x"$pidfile" != x -a -f "$pidfile" ] ; then
+                                       pid=$(cat "$pidfile")
+                                       echo "Found pidfile $pidfile the hard way with process $pid"
+                                       break
+                               fi
+                       fi
+               done
+               if [ $mounted_proc -eq 1 ] ; then
+                       echo "Unmounting /proc"
+                       umount /proc
+               fi
+       fi
+
+       if [ x"$pid" = x ] ; then
+               >&2 echo "Can't determine pid of the running asterisk instance"
+               exit 1
+       fi
+
+       if $RUNNING ; then
+               answer=Y
+       else
+               read -p "WARNING:  Taking a core dump of the running asterisk instance will suspend call processing while the dump is saved.  Do you wish to continue? (y/N) " answer
+       fi
+       if [[ "$answer" =~ ^[Yy] ]] ; then
+               cf="${OUTPUTDIR:-/tmp}/core-asterisk-running-$df"
+               echo "Dumping running asterisk process to $cf"
+               ${GDB} ${asterisk_bin} -p $pid -q --batch --ex "gcore $cf" >/dev/null 2>&1
+               COREDUMPS+=("$cf")
+       else
+               echo "Skipping dump of running process"
        fi
 fi