#!/bin/bash
set -u
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://workshop.perforce.com/projects/p4sudo/view/main/LICENSE
#------------------------------------------------------------------------------
# p4sudo-help.sh — P4Sudo help interception script
#
# Called by p4broker as a filter script for the 'help' command. Intercepts
# 'p4 help sudo' and 'p4 help sudo <subcommand>'; passes all other help
# topics through to p4d unchanged.
#
# May also be invoked directly with -h, -man, or -V.
#
# Deployment: /p4/common/site/p4sudo/p4sudo-help.sh
# Config: ${P4SUDO_CFG:-/p4/common/site/config/p4sudo.cfg}
# Broker rule:
# command: ^(help)$
# {
# action = filter;
# execute = "/p4/common/site/p4sudo/p4sudo-help.sh";
# }
#
# See doc/broker-rewrite-reference/README.md for the full broker protocol.
#==============================================================================
# Declarations and Environment
declare ThisScript=${0##*/}
declare Version=1.0.0
declare ThisUser=
declare ThisHost=${HOSTNAME%%.*}
declare -i Debug=${SDP_DEBUG:-0}
declare -i ErrorCount=0
declare SDPRoot=${SDP_ROOT:-/p4}
declare SDPCommon="$SDPRoot/common"
declare SDPCommonLib="$SDPCommon/lib"
# P4Sudo config file. Override via the P4SUDO_CFG environment variable.
declare P4SudoCfg="${P4SUDO_CFG:-/p4/common/site/config/p4sudo.cfg}"
# Broker context — populated from stdin in Main Program.
declare -a BrokerArgs=()
declare -i ArgCount=0
# Command registry — populated by load_commands().
declare -A CmdDesc
declare -A CmdUsage
#==============================================================================
# Local Functions
function msg () { echo -e "$*"; }
function dbg () { [[ "$Debug" -eq 0 ]] || msg "DEBUG: $*" >&2; }
function errmsg () { msg "\nError: ${1:-Unknown Error}\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "${2:-1}"; }
#------------------------------------------------------------------------------
# Function: load_commands
#
# Reads the [commands] section of P4SudoCfg and populates CmdDesc[] and
# CmdUsage[] for help text generation.
#------------------------------------------------------------------------------
function load_commands ()
{
local section=
local key=
local val=
local cmdName=
local attr=
[[ -f "$P4SudoCfg" ]] || return 0
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line%%#*}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ -z "$line" ]] && continue
if [[ "$line" =~ ^\[([a-z]+)\]$ ]]; then
section="${BASH_REMATCH[1]}"
continue
fi
[[ "$section" == "commands" ]] || continue
key="${line%%=*}"; key="${key%"${key##*[![:space:]]}"}"
val="${line#*=}"; val="${val#"${val%%[![:space:]]*}"}"
cmdName="${key%%.*}"
attr="${key#*.}"
case "$attr" in
description) CmdDesc["$cmdName"]="$val";;
usage) CmdUsage["$cmdName"]="$val";;
esac
done < "$P4SudoCfg"
}
#==============================================================================
# Load SDP Library Functions.
if [[ -d "$SDPCommonLib" ]]; then
# shellcheck disable=SC1090 disable=SC1091
source "$SDPCommonLib/logging.lib" ||\
bail "Failed to load bash lib [$SDPCommonLib/logging.lib]. Aborting."
# shellcheck disable=SC1090 disable=SC1091
source "$SDPCommonLib/run.lib" ||\
bail "Failed to load bash lib [$SDPCommonLib/run.lib]. Aborting."
fi
# run.lib declares its own Version; restore this script's version.
declare Version=1.0.0
# Override terminate() from logging.lib: stdout is the broker protocol channel.
# shellcheck disable=SC2317
function terminate ()
{
trap - EXIT SIGINT SIGTERM
exit "$ErrorCount"
}
#------------------------------------------------------------------------------
# Function: usage (required function)
#
# Input:
# $1 - style: -h (short form) or -man (man-page form). Default: -h.
# $2 - usageErrorMessage: Optional; displayed before the usage text.
#------------------------------------------------------------------------------
function usage ()
{
local style=${1:--h}
local usageErrorMessage=${2:-Unset}
if [[ "$usageErrorMessage" != Unset ]]; then
msg "\n\nUsage Error:\n\n$usageErrorMessage\n\n"
fi
msg "USAGE for $ThisScript v$Version:
$ThisScript [-d|-D]
or
$ThisScript [-h|-man|-V]
"
if [[ "$style" == -man ]]; then
msg "
DESCRIPTION:
$ThisScript is invoked by p4broker as a filter script for the 'help'
command. It intercepts 'p4 help sudo' and 'p4 help sudo <subcommand>',
returning P4Sudo-specific help text. All other 'p4 help' topics receive
an 'action: PASS' response so that p4d handles them normally.
It may also be run directly with -h, -man, or -V.
OPTIONS:
-d Enable debug output (to stderr).
-D Enable extreme debug mode (bash set -x).
HELP OPTIONS:
-h Display short help message.
-man Display man-style help message.
-V Display version info for this script.
ENVIRONMENT:
P4SUDO_CFG
Override the default p4sudo.cfg path
(default: /p4/common/site/config/p4sudo.cfg).
SDP_ROOT Override the SDP root directory (default: /p4).
FILES:
\${P4SUDO_CFG:-/p4/common/site/config/p4sudo.cfg}
Command registry (description and usage text for each command).
SEE ALSO:
p4sudo.sh(1), doc/broker-rewrite-reference/README.md
"
fi
exit 2
}
#==============================================================================
# Command Line Processing
declare -i ShiftArgs=0
set +u
while [[ $# -gt 0 ]]; do
case $1 in
(-h) usage -h;;
(-man|--help) usage -man;;
(-V|--version) msg "$ThisScript version $Version"; exit 0;;
(-d) Debug=1;;
(-D) Debug=1; set -x;;
(*) usage -h "Unknown arg ($1).";;
esac
shift; while [[ $ShiftArgs -gt 0 ]]; do
[[ $# -eq 0 ]] && usage -h "Incorrect number of arguments."
ShiftArgs=$ShiftArgs-1
shift
done
done
set -u
#==============================================================================
# Command Line Verification
# (No additional verification required for the options above.)
#==============================================================================
# Main Program
trap terminate EXIT SIGINT SIGTERM
ThisUser=$(id -n -u)
dbg "Starting $ThisScript v$Version as $ThisUser@$ThisHost"
#------------------------------------------------------------------------------
# Parse broker stdin.
while IFS= read -r line; do
[[ -z "$line" ]] && break
key="${line%%: *}"
value="${line#*: }"
case "$key" in
argCount) ArgCount="$value";;
Arg*) BrokerArgs+=("$value");;
esac
done
dbg "stdin: argCount=$ArgCount args=${BrokerArgs[*]+"${BrokerArgs[*]}"}"
#------------------------------------------------------------------------------
# If this is not a 'p4 help sudo' request, pass through to p4d.
if (( ArgCount == 0 )) || [[ "${BrokerArgs[0]:-}" != "sudo" ]]; then
echo "action: PASS"
exit 0
fi
#------------------------------------------------------------------------------
# Load command registry from p4sudo.cfg.
load_commands
#------------------------------------------------------------------------------
# Emit help text.
declare Subcmd="${BrokerArgs[1]:-}"
if [[ -z "$Subcmd" ]]; then
# 'p4 help sudo' — list all registered commands.
msg "p4 sudo -- Run a privileged or site-defined Perforce command."
msg ""
msg " p4 sudo <subcommand> [args...]"
msg ""
msg " P4Sudo lets authorized users run privileged or site-defined"
msg " Perforce operations without holding super-user access."
msg " Authorization is controlled by the site p4sudo.cfg policy file."
msg ""
msg " For help on a specific subcommand: p4 help sudo <subcommand>"
msg ""
if (( ${#CmdDesc[@]} > 0 )); then
msg " Available commands:"
while IFS= read -r name; do
printf ' %-20s %s\n' "$name" "${CmdDesc[$name]:-}"
done < <(printf '%s\n' "${!CmdDesc[@]}" | sort)
msg ""
else
msg " (No commands registered in p4sudo.cfg.)"
msg ""
fi
else
# 'p4 help sudo <subcmd>' — show detail for that command.
declare desc="${CmdDesc[$Subcmd]:-}"
declare usageText="${CmdUsage[$Subcmd]:-}"
if [[ -z "$desc" && -z "$usageText" ]]; then
msg "p4sudo: no help available for subcommand '$Subcmd'."
msg " Run 'p4 help sudo' to list available commands."
else
msg "p4 sudo $Subcmd -- ${desc:-}"
msg ""
if [[ -n "$usageText" ]]; then
msg " $usageText"
msg ""
fi
fi
fi
exit "$ErrorCount"
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #2 | 32553 | C. Thomas Tyler |
Added LICENSE file. Adjusted URLs replacing https://swarm.workshop with https://workshop Adusted path to LICENSE to refer to P4Sudo's own project license, not the SDP one. |
||
| #1 | 32547 | bot_Claude_Anthropic |
Add p4sudo.sh dispatcher and p4sudo-help.sh Core broker filter scripts: p4sudo.sh reads broker stdin, validates the requesting user, parses p4sudo.cfg authorization rules, and dispatches to command scripts or native p4 commands. p4sudo-help.sh handles p4 help sudo interception. #review-32548 @robert_cowham @tom_tyler |