fant0men

Free RAM script (kills browser tabs)

Dec 1st, 2019 (edited)
149
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2. # This script creates an infinite while loop, which checks the available
  3. # RAM every 1 second, and kills Firefox, Chrome, Chromium and
  4. # Tor Browser if less than 1 GB is available. The script will only kill
  5. # the tabs, but not the main window itself, so the application keeps
  6. # running but RAM is still freed up.
  7. #
  8. # The web browser is always the application that ends up using the most
  9. # RAM on my system. Once the RAM is nearly full, Linux starts swapping
  10. # and gradually slows down more and more, until grinding to a complete
  11. # halt when RAM is completely full. Then Linux calls the Out Of Memory
  12. # (OOM) manager to kill processes to free up RAM. It might kill a
  13. # critical process, a program that's been running for a very long time
  14. # (i.e. video encoding). To prevent that from happening, I created
  15. # this script.
  16.  
  17. # Creates a function called 'now', which will print the date and time.
  18. now () { date '+%F %H:%M:%S'; }
  19.  
  20. regex='^[[:space:]]*$'
  21. regex_rend='--type=renderer'
  22. regex_ext='--extension-process'
  23.  
  24. # Creates a function called 'is_chromium', which checks if Chromium
  25. # or Chrome is running.
  26. is_chromium () {
  27.     for comm in chrome chromium; do
  28.         pgrep "$comm" | tr -d '[:blank:]'
  29.     done
  30. }
  31.  
  32. # Creates a function called 'kill_chromium', which kills all child
  33. # processes belonging to either Chromium or Chrome.
  34. kill_chromium () {
  35.     pid_switch=0
  36.  
  37.     for comm in chrome chromium; do
  38.  
  39.         mapfile -t parent < <(ps -C "$comm" -o ppid | tail -n +2 | tr -d '[:blank:]' | sort -u)
  40.         mapfile -t child < <(ps -C "$comm" -o pid | tail -n +2 | tr -d '[:blank:]')
  41.  
  42.         for (( i = 0; i < ${#child[@]}; i++ )); do
  43.             if [[ ! ${child[${i}]} =~ $regex ]]; then
  44.                 child_pid="${child[${i}]}"
  45.  
  46.                 for (( j = 0; j < ${#parent[@]}; j++ )); do
  47.                     if [[ ! ${parent[${j}]} =~ $regex ]]; then
  48.                         parent_pid="${parent[${j}]}"
  49.  
  50.                         if [[ $parent_pid == $child_pid ]]; then
  51.                             pid_switch=1
  52.                         fi
  53.                     fi
  54.                 done
  55.  
  56. # Adding an extra check, which checks if $child_pid is a renderer / extension
  57. # process. If it's NOT a renderer, set $pid_switch to '1', preventing it from
  58. # being killed. If the process is an extension process, also set $pid_switch to
  59. # '1'. This will keep extensions and downloads running, even though the other
  60. # Chrome child processes are killed. Only renderer processes that are NOT
  61. # extension processes will get killed.
  62.                 chrome_type=$(ps -p "$child_pid" -o args | tail -n +2)
  63.  
  64.                 if [[ ! $chrome_type =~ $regex_rend ]]; then
  65.                     pid_switch=1
  66.                 elif [[ $chrome_type =~ $regex_ext ]]; then
  67.                     pid_switch=1
  68.                 fi
  69.  
  70.                 if [[ $pid_switch -eq 0 ]]; then
  71.                     echo "SIGKILL: ${child_pid}"
  72.                     kill -9 "$child_pid"
  73.                 else
  74.                     pid_switch=0
  75.                 fi
  76.             fi
  77.         done
  78.     done
  79. }
  80.  
  81. # Creates a function called 'kill_firefox', which kills all child
  82. # processes belonging to either Firefox or Tor Browser.
  83. kill_firefox () {
  84.     declare -a pids_tmp pids
  85.  
  86.     for name in 'Web Content' 'WebExtensions'; do
  87.         mapfile -t pids_tmp < <(pgrep -x "$name" | tr -d '[:blank:]')
  88.         pids+=( "${pids_tmp[@]}" )
  89.     done
  90.  
  91.     for name in firefox tor; do
  92.         for (( i = 0; i <= ${#pids[@]}; i++ )); do
  93.             if [[ ! ${pids[${i}]} =~ $regex ]]; then
  94.                 pid="${pids[${i}]}"
  95.                 p_name=$(ps -p "$pid" -o comm | tail -n +2)
  96.                 if [[ $p_name == $name ]]; then
  97.                     unset -v pids[${i}]
  98.                 fi
  99.             fi
  100.         done
  101.     done
  102.  
  103.     kill -9 "${pids[@]}"
  104. }
  105.  
  106. # Creates a file name for the log.
  107. log_killed="${HOME}/firefox_chromium_killed.log"
  108.  
  109. # If $log_killed is not a file, create it.
  110. if [[ ! -f $log_killed ]]; then
  111.     touch "$log_killed"
  112. fi
  113.  
  114. # Creates an infinite while loop.
  115. while true; do
  116. # Sleeps for 1 second.
  117.     sleep 1
  118.  
  119. # Unsets $free_ram, since this might not be the first time the loop is
  120. # run.
  121.     unset -v free_ram
  122.  
  123. # Runs 'free', stores output in the $free_ram array, and sets a couple
  124. # of variables based on that output.
  125.     mapfile -t free_ram < <(free)
  126.     ram=$(sed -E 's/[[:space:]]+/ /g' <<<"${free_ram[1]}" | cut -d' ' -f4)
  127.     swap=$(sed -E 's/[[:space:]]+/ /g' <<<"${free_ram[2]}" | cut -d' ' -f4)
  128.     avail=$(sed -E 's/[[:space:]]+/ /g' <<<"${free_ram[1]}" | cut -d' ' -f7)
  129.  
  130. # Prints the free and available RAM and SWAP.
  131.     echo "FREE (kibibytes)"
  132.     echo "RAM: ${ram}, SWAP: ${swap}"
  133.     echo "***"
  134.     echo "AVAILABLE (kibibytes)"
  135.     echo -e "RAM: ${avail}\n"
  136.  
  137.     # If available RAM is less than 1GB...
  138.     if [[ $avail -lt 1000000 ]]; then
  139. # Checks if Firefox and Chromium are running. $is_chromium is an array,
  140. # since the output probably spans across multiple lines, due to Chromium
  141. # being highly multithreaded and keeping separate threads / processes
  142. # for every window and tab.
  143.         is_firefox=$(pgrep -x firefox)
  144.         is_tor=$(pgrep -x tor)
  145.         mapfile -t is_chromium < <(is_chromium)
  146.  
  147. # If Firefox is running, then kill it, print a message to the screen,
  148. # and append a message to the log.
  149.         if [[ $is_firefox ]]; then
  150.             time=$(now)
  151.  
  152.             echo -e "${time}: Killing Firefox...\n" | tee --append "$log_killed"
  153.             kill_firefox
  154.         fi
  155.  
  156. # If Tor Browser is running, then kill it, print a message to the
  157. # screen, and append a message to the log.
  158.         if [[ $is_tor ]]; then
  159.             time=$(now)
  160.  
  161.             echo -e "${time}: Killing Tor Browser...\n" | tee --append "$log_killed"
  162.             kill_firefox
  163.         fi
  164.  
  165. # If Chromium is running, then...
  166.         if [[ ! ${is_chromium[0]} =~ $regex ]]; then
  167.             time=$(now)
  168.  
  169.             echo -e "${time}: Killing Chromium...\n" | tee --append "$log_killed"
  170.             kill_chromium
  171.         fi
  172.  
  173. # Writes cached writes to disk. Hopefully this will also clear up a
  174. # little RAM.
  175.         sync
  176.     fi
  177. done
  178.  
  179.  
RAW Paste Data