#!/usr/bin/bash # SPDX-License-Identifier: GPL-2.0-or-later set -euo pipefail function spdlog() { local level="${1}" messages=("${@:2}") case "${level}" in info) level="$(printf "%s%s%s" "\033[32m" "info" "\e[0m")" ;; warn) level="$(printf "%s%s%s" "\033[33m\033[1m" "warning" "\e[0m")" ;; error) level="$(printf "%s%s%s" "\033[31m\033[1m" "error" "\e[0m")" ;; esac echo -e "[$(date '+%H:%M:%S.%3N')] [${level}]" "${messages[@]}" >&2 } function print-if-executable() { local file="${1}" if [ -x "${file}" ]; then echo "${file}" return 0 fi return 1 } function find-program() { local name="${1}" script_dir="${2}" project_dir="${3:-}" # If the calling script is installed to /usr, the program should be right next to it if print-if-executable "${script_dir}/${name}"; then return 0 fi # Otherwise the script might be called from the cloned git repository if [ -n "${project_dir}" ] && [ -f "${project_dir}/meson_options.txt" ]; then local -r file="$(find "${project_dir}" -type f -executable -name "${name}" | head -n1)" if print-if-executable "${file}"; then spdlog info "Located ${name} at ./$(realpath --relative-to="${PWD}" "${file}")" return 0 fi fi # Fall back to looking in PATH if print-if-executable "$(command -v "${name}" || true)"; then spdlog warn "Located ${name} using PATH" spdlog warn "This script is designed to not use PATH, something might be wrong" return 0 fi return 1 } function run-iptsd-foreach() { local -r script_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" local -r project_dir="$(realpath "${script_dir}/../..")" local device_index="" local device_type="any" local xargs_procs="1" local tmux_socket="" local show_help="" while getopts ":t:i:pmh" args; do case "${args}" in i) device_index="${OPTARG}" ;; t) device_type="${OPTARG}" ;; p) xargs_procs="0" ;; m) xargs_procs="0" tmux_socket="$(mktemp -u)" ;; *) show_help="1" ;; esac done shift $((OPTIND - 1)) if (("$#" == 0)); then show_help="1" fi if [ -n "${show_help}" ]; then echo "Usage: iptsd-foreach [OPTIONS] -- COMMAND" echo "Run commands on all IPTS devices" echo "" echo "The string that is passed as COMMAND is executed in a bash shell." echo "Every occurence of {} will be replaced with the selected device." echo "" echo "Options:" echo " -h Print this help message and exit" echo " -t TEXT:{any,touchscreen,touchpad} Selects all devices with the given type" echo " -i INTEGER Selects a single device by its index" echo " -p Run all commands in parallel" echo " -m Like -p, but runs every instance in seperate tmux windows" return 0 fi if [ -n "${tmux_socket}" ] && [ ! -x "$(command -v tmux)" ]; then spdlog error "tmux is not installed" return 1 fi local -r check_device="$(find-program "iptsd-check-device" "${script_dir}" "${project_dir}")" if [ -z "${check_device}" ]; then spdlog error "Could not locate iptsd-check-device" return 1 fi local devices=() while read -rd $'\n' device; do if [ ! -r "${device}" ]; then spdlog warn "Can't read from ${device}, is the program running as root?" continue fi if ! "${check_device}" --quiet --type="${device_type}" "${device}"; then continue fi devices+=("${device}") done <<<"$(find /dev -maxdepth 1 | grep -E "hidraw[0-9]+" | sort)" # If no devices were found, exit if (("${#devices[@]}" == 0)); then spdlog info "No devices found" return 0 fi if [ -n "${device_index}" ]; then if (("${#devices[@]}" <= "${device_index}")) || (("${device_index}" < 0)); then spdlog error "Device index is out of range" return 1 fi devices=("${devices[${device_index}]}") fi # Ignore the tmux option if there is only one device if (("${#devices[@]}" == 1)); then tmux_socket="" fi local xargs_cmd=("bash" "-c" "$*") if [ -n "${tmux_socket}" ]; then xargs_cmd=("tmux" "-S" "${tmux_socket}" "new-window" "-n" "{}" "${xargs_cmd[@]}") tmux -S "${tmux_socket}" new-session -d -n iptsd fi # Save existing traps local -r saved_traps="$(trap)" # Ignore SIGINT and SIGTERM, these have to be handled by xargs / iptsd trap -- "" SIGINT trap -- "" SIGTERM # Run the supplied commands on all devices echo -n "${devices[@]}" | xargs -d ' ' -P "${xargs_procs}" -i "${xargs_cmd[@]}" # Restore SIGINT and SIGTERM to their defaults trap -- - SIGINT trap -- - SIGTERM # Restore previous traps eval "${saved_traps}" if [ -n "${tmux_socket}" ]; then tmux -S "${tmux_socket}" kill-window -t 0 tmux -S "${tmux_socket}" attach fi return 0 } if [ "${BASH_SOURCE[0]}" = "${0}" ]; then run-iptsd-foreach "$@" fi