Running a function as process with a set process name or id

I have a bash script set up to monitor a number of UDP streams and convert it into actionable data. My problem is that I need to set the script to periodically check to see if the stream capture is running and restart it if it isn’t.

The challenge is to create a new process name or ID for each stream capture and check to see if it’s running.

Here’s a watered down version of what I’ve got. I’m hoping someone can tell me if I’m on the right track or not:

Subscriber () {
    processName="$1$2$4";
    echo "$processName";
    pgrep $processName;
    if [[ $? -ne 0 ]] ; then
        echo "Subcription for $1 with IP $2 not found, restarting." ;
        while read -re -t 43200 doc; do
            <Code to analyze stream>
            done < <(bash -c "exec -a $processName <Commands to capture stream as JSON doc>")
    else
        echo "Subcription for $1 wtih IP $2 found to be running, skipping." ;
    fi
}
while read line; do
    Subscriber $line;
done < $flatFile

Ideally, I’d like to get a process ID or name for the entire string of commands listed after exec -a, but it currently only taking the first command, which seems to be sort of working, but I’m not confident it will do what I’m wanting it to do.

The flatFile reference is a dynamically updated flatfile listing several hundred streams I’m monitoring.

Asked By: Dan D

||

The name of a process on Linux at least is changed every time the process executes a command, but is changed to the basename of the file that is executed, not to the argv[0] that bash allows passing with exec -a.

pgrep can match on the arg list (joined with spaces) with the -f option though, but note that like for grep, pgrep does regular expression matching, so you’d need to construct a regexp to match the argv[0] you want.

Or you could make sure the name you pass to exec -a doesn’t contain regexp characters (including . common in IPv4 addresses).

Subscriber () {
  process_arg0="$1$2$4"

  # change all regex operators and space to _
  process_arg0="${process_arg0//[][ .\+?$^()*{}]/_}"

  printf>&2 '%sn' "$process_arg0"
  if ! LC_ALL=C pgrep -f "^$process_arg0( |$)"; then
    printf>&2 '%sn' "Subscription for $1 with IP $2 not found, restarting."
    while IFS= read -r -t 43200 doc; do
      <Code to analyze stream>
    done < <(
      exec -a "$process_arg0" <Commands to capture stream as JSON doc>
    )
  else
    printf>&2 '%sn' "Subscription for $1 with IP $2 found to be running, skipping."
  fi
}

(also removing some other obvious errors like unquoted quotes, usage of echo, nonsensical -e option for read, missing IFS= for read, variable data embedded in interpreter code).

In any case, matching processes by name or arg list is very brittle and dangerous as any process can assign themselves any name or arg list they want, and the approach used here introduces race conditions. Using proper process overseers / supervisors such as your init system (systemd, upstart and co) or dedicated tools such as runit, daemontools, supervisor, start-stop-daemon would likely be more appropriate.

Answered By: Stéphane Chazelas