#!/usr/bin/env bash # # This script will spawn a new Chrome process restricted to a profile that # is created on demand and (optionally) deleted on exit. If a root profile # directory is given, it will be used as the template for the newly created # profile. # # NOTES: # # Chrome spawning takes place irrespective of any currently running Chrome # processes. The script will always spawn a new Chrome process. # # If a root profile directory is not configured/passed-in as an argument, the # created profile will be a fresh, first-time profile that does not contain # any configuration data or extensions that may be present in a different # profile for the same installed Chrome browser. # # To create a root profile, execute `chrome-private.sh --name name --keep', # and after installing extensions and modifying browser configuration, you # can copy/rename its directory and set it as ROOT_PROFILE_DIR (or pass it # as an argument with --root-profile). # # This is free and unencumbered software released into the public domain. # xristos@sdf.org set -em # # Configuration # CONFIG=~/.chrome-private.rc if [ -f "${CONFIG}" ]; then source "${CONFIG}" fi # All of the following user configuration variables can be set in # ~/.chrome-private.rc : "${CHROME:=google-chrome}" : "${TMP:=/tmp}" # A root chrome profile directory, if it exists it will be used as a template : "${ROOT_PROFILE_DIR:=}" # GNU mktemp is required, set this to gmktemp (coreutils) on macOS : "${MKTEMP:=mktemp}" : "${PROFILE_MKTEMP:=chrome.priv.prof_XXXXXXXXXX}" : "${PROXY=socks5://127.0.0.1:5060}" # Using a ramdisk rather than TMP is a good idea, but doubly so if you're going # to go heavy on number of disposable profiles. hdiutil can be used to create # a ramdisk on macOS. : "${RAMDISK:=}" # Remote debugging port : "${DEBUG_PORT:=}" # # End of user configuration # CHROME_ARGS=(--disable-bundled-ppapi-flash --disable-offline-load-stale-cache --disk-cache-size=1 --media-cache-size=1 --disk-cache-dir=/dev/null --no-first-run --no-referrers --save-page-as-mhtml --no-default-browser-check) CHROME_NO_GPU_ARGS=(--disable-gpu) CHROME_NO_3D_ARGS=(--disable-3d-apis --disable-webgl) keep=0 delete=0 incognito=0 use_gpu=0 use_3d=0 use_proxy=0 profile= profile_name= chrome_pid=0 color=0 start_color="\e[1;34m" end_color="\e[0m" basename="$(basename "${ROOT_PROFILE_DIR}")" if [ -t 1 ]; then color=1 fi # # Functions # function usage { echo "Usage: $0 [--name name] [--temp-name (${PROFILE_MKTEMP})] [--keep] [--delete] [--gpu] [--3d] [--incognito] [--root-profile dir] [--profile dir] [--port port] [--proxy (${PROXY})] -- chrome-arguments" echo -e " --name name of created profile directory" echo -e " this will override --temp-name if given" echo -e " --temp-name this will be passed to mktemp(1) to generate a" echo -e " disposable profile directory name" echo echo -e " --keep do not delete/rename profile directory on exit" echo -e " (implied with --profile)" echo -e " --delete delete rather than rename profile directory" echo -e " (ignored with --keep / --profile)" echo echo -e " --gpu enable gpu acceleration" echo -e " --3d enable WebGL / 3D APIs" echo echo -e " --incognito start Chrome in incognito mode" echo -e " --root-profile use given directory as root profile directory" echo -e " --profile dir use given directory as profile directory (needs to exist)" echo -e " (--root-profile, --name and --temp-name are ignored)" echo -e " --proxy if given, Chrome will use a proxy (default or specified)" echo -e " --port use given port as remote debugging port" echo } function msg { if [ "${color}" -eq 1 ]; then printf "${start_color}$1${end_color}\n" else printf "%s\n" "$1" fi } function err { echo "Error: $*" >&2 } function setup { # Optional, executed first when --profile is not given. # Currently used to setup the ram disk (if configured). if [ -d "${RAMDISK}" ]; then if [ -d "${ROOT_PROFILE_DIR}" ]; then if [ ! -d "${RAMDISK}/${basename}" ]; then # Copy the root profile to ramdisk msg "Copying reference profile from: ${ROOT_PROFILE_DIR} to: ${RAMDISK}/${basename}" cp -R "${ROOT_PROFILE_DIR}/." "${RAMDISK}/${basename}" fi ROOT_PROFILE_DIR="${RAMDISK}/${basename}" fi TMP="${RAMDISK}" fi } function cleanup { if [ -d "${profile}" ]; then if [ "${keep}" -eq 0 ]; then if [ "${delete}" -eq 0 ]; then mv "${profile}" "${profile}.deleted" msg "Renamed: ${profile}" else rm -rf "${profile}" msg "Deleted: ${profile}" fi fi else err "Directory ${profile} does not exist" fi } function interrupt { if [ "${chrome_pid}" -ne 0 ] && ps "${chrome_pid}" &>/dev/null; then kill "${chrome_pid}" wait fi } # # Argument parsing # while :; do case "$1" in -h|--help) usage exit 0 ;; --keep) keep=1 ;; --delete) delete=1 ;; --gpu) use_gpu=1 ;; --3d) use_3d=1 ;; --incognito) incognito=1 ;; --name) if [ "$2" ] && [[ "$2" != --* ]]; then profile_name="$2" shift else err "--name requires an argument" exit 1 fi ;; --temp-name) if [ "$2" ] && [[ "$2" != --* ]]; then PROFILE_MKTEMP="$2" shift else err "--temp-name requires an argument" exit 1 fi ;; --root-profile) if [ "$2" ] && [[ "$2" != --* ]]; then ROOT_PROFILE_DIR="$2" basename="$(basename "${ROOT_PROFILE_DIR}")" shift else err "--root-profile requires a profile directory" exit 1 fi ;; --profile) if [ "$2" ] && [[ "$2" != --* ]]; then profile="$2" shift else err "--profile requires a profile directory" exit 1 fi ;; --proxy) use_proxy=1 if [ "$2" ] && [[ "$2" != --* ]]; then PROXY="$2" shift fi ;; --port) if [ "$2" ] && [[ "$2" != --* ]]; then DEBUG_PORT="$2" shift fi ;; --) shift break ;; -?*) err "Unknown option: $1" exit 1 ;; *) break esac shift done # # End of argument parsing # builtin type -P "${CHROME}" &>/dev/null || { err "${CHROME}" not found; exit 1; } if [ "${profile}" ]; then keep=1 if [ -d "${profile}" ]; then msg "Using existing profile dir: ${profile}" else err "Directory ${profile} does not exist" exit 1 fi else setup if [ "${profile_name}" ]; then profile="${profile_name}" else profile="$("${MKTEMP}" -d -p "${TMP}" "${PROFILE_MKTEMP}")" fi if [ -d "${ROOT_PROFILE_DIR}" ]; then msg "Copying reference profile from: ${ROOT_PROFILE_DIR} to: ${profile}" cp -R "${ROOT_PROFILE_DIR}/." "${profile}" else msg "Using new profile dir: ${profile}" fi fi MY_ARGS=(--user-data-dir="${profile}") if [ "${use_proxy}" -eq 1 ]; then msg "Using proxy: ${PROXY}" MY_ARGS+=(--proxy-server="${PROXY}") fi if [ "${incognito}" -eq 1 ]; then msg "Incognito mode enabled" MY_ARGS+=(--incognito) fi if [ "${use_gpu}" -eq 0 ]; then CHROME_ARGS=("${CHROME_ARGS[@]}" "${CHROME_NO_GPU_ARGS[@]}") else msg "GPU enabled" fi if [ "${use_3d}" -eq 0 ]; then CHROME_ARGS=("${CHROME_ARGS[@]}" "${CHROME_NO_3D_ARGS[@]}") else msg "3D APIs enabled" fi if [ "${DEBUG_PORT}" ]; then MY_ARGS+=(--remote-debugging-port="${DEBUG_PORT}") msg "Remote debugging port: ${DEBUG_PORT}" fi # # Execution # trap cleanup EXIT trap interrupt INT "${CHROME}" "${CHROME_ARGS[@]}" "${MY_ARGS[@]}" "$@" 2>/dev/null & # # Post-execution # chrome_pid=$! msg "Chrome PID: ${chrome_pid}" wait