Advertisement
Guest User

BackgroundProcess

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