Advertisement
Guest User

BackgroundProcess

a guest
Sep 18th, 2021
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 8.68 KB | None | 0 0
  1. #!/usr/bin/bash
  2. # Written by Manuel Iglesias. [email protected]
  3.  
  4. readonly CommandName=${0##*/}
  5. readonly CommandOptionString="-c"
  6. readonly RunInBackgroundOptionString="-b"
  7. readonly KillProcessOptionString="-k"
  8. readonly ProcessFinishedOptionString="-f"
  9. readonly TempDir="/tmp"
  10. readonly KillProcessTimeoutSeconds="1" # Max time for killed process's parent to save child process' info. '$CommandName -k ' fails if parent running after timeout.
  11. readonly KillSignal="TERM"
  12. readonly CommandLineVarName="CommandLine"   # Process info output.
  13. readonly ElapsedTime_mSVarName="ElapsedTime_mS" # Process info output.
  14. readonly ExitCodeVarName="ExitCode"     # Process info output.
  15. readonly StandardOutVarName="StandardOut"   # Process info output.
  16. readonly StandardErrorVarName="StandardError"   # Process info output.
  17.  
  18.  
  19.  
  20. Usage() {
  21. echo -e "'${CommandName}' can be used to launch/control a process running in background.
  22. Usage: '$CommandName $CommandOptionString Command [Param [Param...]]'.
  23.  Launches 'Command [Param [Param...]]' in background and outputs its Pid.
  24.  Exitcode 0 if successful.
  25. Usage: '$CommandName Pid'.
  26.  Outputs background process' information (see below).
  27.  Exitcode 0 if 'Pid' is valid and process no longer running (information valid).
  28.  Exitcode 1 if 'Pid' valid and process still running (information not valid).
  29.  Exitcode non 0 if 'Pid' not valid or any other problem (information not valid).
  30. Usage: '$CommandName $ProcessFinishedOptionString Pid'.
  31.  Checks if process with 'Pid' is running. Does not output any process information.
  32.  Exitcode 0 if 'Pid' is valid and the process is no longer running
  33.  Exitcode 1 if 'Pid' valid and the process is still running.
  34.  Exitcode non 0 if 'Pid' not valid or any other problem.
  35. Usage: '$CommandName $KillProcessOptionString Pid'.
  36.  Kills process with 'Pid' (previously launched by '${CommandName}').
  37.  Outputs background process' information (see below).
  38.  Exitcode 0 if 'Pid' is valid and was killed successfully (or had exited already)
  39.    and if the process' information was read successfully.
  40. Background process' information:
  41.  Text that, when parsed, provides the following variables and values:
  42.    '${CommandLineVarName}': Command and parameters of process when launched.
  43.    '${ElapsedTime_mSVarName}': Process' running time (milliSeconds).
  44.    '${ExitCodeVarName}': Process' exit code.
  45.    '$StandardOutVarName': StdOut generated by process while running.
  46.    '${StandardErrorVarName}': StdErr generated by process while running.
  47.  See System_Docs/System/BashCodeRepository/'Background processes' (parsing).
  48. Normal usage sequence:
  49. -Use '$CommandName $CommandOptionString Command ...' to launch a process and get its Pid.
  50. -Do other tasks.
  51. -Use '${CommandName} $ProcessFinishedOptionString Pid' to check if the background process has exited.
  52. -Use '${CommandName} Pid' to read the process' information when it has exited.
  53. -Parse the background process' information and use Variables' contents.
  54. -If the background process has not exited in the allotted time, kill it with
  55.    '$CommandName $KillProcessOptionString Pid' (and get the killed process' [partial] information)." 1>&2
  56. return 0
  57. }
  58.  
  59.  
  60.  
  61. CommandOption=false;RunInBackgroundOption=false;KillProcessOption=false;ProcessFinishedOption=false
  62. if [[ "$1" == "$CommandOptionString" ]]
  63.   then
  64.     CommandOption=true
  65.     shift
  66.   else
  67.     if [[ "$1" == "$RunInBackgroundOptionString" ]]
  68.       then
  69.         RunInBackgroundOption=true
  70.         shift
  71.       else
  72.         if [[ "$1" == "$KillProcessOptionString" ]]
  73.           then
  74.             KillProcessOption=true
  75.             shift
  76.           else
  77.             if [[ "$1" == "$ProcessFinishedOptionString" ]]
  78.               then
  79.                 ProcessFinishedOption=true
  80.                 shift
  81.             fi
  82.         fi
  83.     fi
  84. fi
  85.  
  86. if [[ $# -lt 1 || "$1" == "-h" || "$1" == "-?" || "$1" == "--help" || "$1" == "-help" ]]
  87.   then
  88.     Usage
  89.     exit 64
  90. fi
  91.  
  92. if $CommandOption
  93.   then
  94.     if ! type "$1" &>/dev/null
  95.       then
  96.         echo -e "${CommandName}: Command '${1}' not in '\$PATH'. Aborting!" 1>&2
  97.         exit 127
  98.     fi
  99. #   'coproc': Equivalent to ending command with '&'. '&' does not work if shell invoked with `` (ProcessId=`BackgroundProcess -c Command`).
  100. #   &>/dev/null so that no error message is given when killed. Also to allow background processes to be launched from this background process (pipes?).
  101.     if coproc $CommandName $RunInBackgroundOptionString "$@" &>/dev/null
  102.       then
  103.         eval "exec ${COPROC[0]}>&-" # Close coproc stdout pipe (See Books/Science/Computer_Science/Linux/Bash_scripting/Bash_Co_Processes).
  104.         eval "exec ${COPROC[1]}<&-" # Close coproc stdin pipe (See Books/Science/Computer_Science/Linux/Bash_scripting/Bash_Co_Processes).
  105.         echo "$!"
  106.         exit 0
  107.       else
  108.         echo -e "${CommandName}: Launching in background with 'coproc' failed with ExitCode: ${?}. Aborting!" 1>&2 # Should never happen but... just in case ;-).
  109.         exit 70
  110.     fi
  111. fi
  112.  
  113. if $RunInBackgroundOption
  114.   then
  115.     TempFile="${TempDir}/${CommandName}_$$"
  116.     TempErrorFile="${TempFile}.Error"
  117.     >$TempErrorFile         # So that other users can not read file.
  118.     chmod 600 $TempErrorFile        # So that other users can not read file.
  119.     TempOutFile="${TempFile}.Out"
  120.     >$TempOutFile           # So that other users can not read file.
  121.     chmod 600 $TempOutFile      # So that other users can not read file.
  122.     cd $TempDir # So that child process uses it as working directory (And does not tie up current wd).
  123.     exec 0<&- # Close stdin.
  124.     exec 1>&- # Close stdout.
  125.     exec 2>&- # Close stderr.
  126.     StartTime=$(date +%s.%N) # Seconds.Nanoseconds since 1970−01−01.
  127.     Decimals=${StartTime##*\.}          # Fast Times1000.
  128.     Decimals=${Decimals:0:3}            # Fast Times1000.
  129.     StartTime="${StartTime%\.*}$Decimals"   # Fast Times1000.
  130.     1>$TempOutFile 2>$TempErrorFile "$@"
  131.     ExitCode=$?
  132.     EndTime=$(date +%s.%N) # Seconds.Nanoseconds since 1970−01−01.
  133.     Decimals=${EndTime##*\.}            # Fast Times1000.
  134.     Decimals=${Decimals:0:3}            # Fast Times1000.
  135.     EndTime="${EndTime%\.*}$Decimals"       # Fast Times1000.
  136.     ((ElapsedTime_mS = EndTime - StartTime))
  137.     echo -e "${CommandLineVarName}=\"$@\"
  138. #
  139. ${ElapsedTime_mSVarName}=${ElapsedTime_mS}
  140. #
  141. ${ExitCodeVarName}=${ExitCode}
  142. #
  143. ${StandardOutVarName}=\"$(< ${TempOutFile})\"
  144. #
  145. ${StandardErrorVarName}=\"$(< ${TempErrorFile})\"
  146. #" >$TempFile
  147.     chmod 600 $TempFile # So that other users can not read file.
  148.     rm -f $TempErrorFile $TempOutFile
  149.     exit 0
  150. fi
  151.  
  152. if [[ $# -ne 1 ]]
  153.   then
  154.     Usage
  155.     exit 64
  156. fi
  157.  
  158. TempFile="${TempDir}/${CommandName}_$1"
  159.  
  160. if ProcessCommandName=$(2>/dev/null ps --pid=$1 h -o comm)
  161.   then
  162.     if [[ "$CommandName" == "${ProcessCommandName}${CommandName#${ProcessCommandName}}" ]] # 'ps' returns only the 15 first characters of command.
  163.       then
  164.         if $KillProcessOption
  165.           then
  166.             for Child in $(ps -o pid --no-headers --ppid $1)
  167.               do
  168.                 pkill -${KillSignal} -P $Child &>/dev/null # Kill BackgroundProcess' Child's children (grandchildren).
  169.               done
  170.             pkill -${KillSignal} -P $1 &>/dev/null # -P means kill processes whose parent process ID is listed. Here: kill BackgroundProcess' children.
  171.             ((Timeout = KillProcessTimeoutSeconds * 10)) # Will be checked every 1/10 seconds.
  172.             while &>/dev/null ps --pid=$1 && [[ $Timeout -gt 0 ]]
  173.               do
  174.                 sleep 0.1
  175.                 ((Timeout--))
  176.               done
  177.             if &>/dev/null ps --pid=$1
  178.               then
  179.                 echo -e "${CommandName}: Warning! Process '${1}' could not be killed." 1>&2
  180.                 exit 70
  181.               else
  182.                 if [[ -f $TempFile ]]
  183.                   then
  184.                     cat $TempFile
  185.                     rm -f $TempFile
  186.                     exit 0
  187.                   else
  188.                     echo -e "${CommandName}: Process killed but temporary file not found. No record of process '${1}'." 1>&2
  189.                     exit 70
  190.                 fi
  191.             fi
  192.         fi
  193.         echo -e "${CommandName}: Process '${1}' still running." 1>&2
  194.         exit 1
  195.       else
  196.         echo -e "${CommandName}: Process '${1}' running but not an instance of '${CommandName}'. Aborting." 1>&2
  197.         exit 70
  198.     fi
  199. fi
  200.  
  201. if [[ -f $TempFile ]]
  202.   then
  203.     if $ProcessFinishedOption
  204.       then
  205.         exit 0
  206.     fi
  207.     if $KillProcessOption
  208.       then
  209.         echo -e "${CommandName}: Process '${1}' has already exited. Kill request ignored." 1>&2
  210.     fi
  211.     cat $TempFile
  212.     rm -f $TempFile
  213.   else
  214.     echo -e "${CommandName}: Process not running and temporary file not found. No record of process '${1}'." 1>&2
  215.     exit 70
  216. fi
  217.  
  218.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement