# p4broker REWRITE Feature — Reference and Examples This directory contains reference files from the Perforce CBD (Component Based Development) project, used here as concrete examples of the p4broker REWRITE feature that P4Sudo is built on. **Source:** https://workshop.perforce.com/projects/perforce-software-cbd **License:** Perforce Consulting Services Agreement (original); included here for reference under open-source terms of the CBD project. --- ## Files | File | Description | |------|-------------| | `p4broker.cfg` | CBD broker config — shows the `action = filter; execute = ...` pattern | | `wssync.sh` | Tiny shell wrapper that invokes `wssync.py` | | `wssync.py` | Main broker entry point — shows how to read broker stdin and call logic | | `Cbd.py` | Full CBD implementation — shows REWRITE/PASS/REJECT output format | The business logic in `Cbd.py` is specific to CBD (component-based sync path rewriting) and is not relevant to P4Sudo. What matters is the protocol it demonstrates. --- ## The Broker Filter Script Protocol ### Broker Config Pattern ``` command: ^(sudo)$ { action = filter; execute = "/p4/common/site/p4sudo/p4sudo.sh"; } ``` - `action = filter` — intercept the command and call the script - `execute` — absolute path to the filter script - The command regex matches the p4 command name (not including `p4` itself) ### Input: What the Script Receives on stdin The broker injects command context as `name: value` lines on stdin: ``` command: sudo workspace: alice.workstation cwd: /home/alice/myproject brokerTargetPort: ssl:ppn-p4d-01:1666 argCount: 3 Arg0: mkblackbelt Arg1: AcmeCorp Arg2: --dry-run ``` | Field | Description | |-------|-------------| | `command` | The p4 command being intercepted (e.g. `sudo`) | | `workspace` | The user's active client workspace name | | `cwd` | User's current working directory (local OS syntax, may be Windows or Unix) | | `brokerTargetPort` | The p4d port the broker forwards to | | `argCount` | Number of arguments that follow | | `Arg0`…`ArgN` | Command arguments, zero-indexed | Additional fields available (from broker documentation and Tom Tyler, 2026): `user` (P4USER), `clientIp` (user's IP address). **Parsing pattern (Python):** ```python import sys, re vals = {} for line in sys.stdin.readlines(): m = re.match(r"^(.+?):\s+(.+)$", line) if m: vals[m.group(1)] = m.group(2) command = vals['command'] workspace = vals['workspace'] cwd = vals['cwd'] p4port = vals['brokerTargetPort'] arg_count = int(vals['argCount']) args = [vals['Arg' + str(i)] for i in range(arg_count)] ``` **Parsing pattern (bash):** ```bash while IFS=': ' read -r key value; do case "$key" in command) CMD="$value" ;; workspace) WORKSPACE="$value" ;; cwd) CWD="$value" ;; brokerTargetPort) P4PORT="$value" ;; argCount) ARG_COUNT="$value" ;; Arg*) ARGS+=("$value") ;; esac done < /dev/stdin ``` ### Output: What the Script Writes to stdout The script's stdout controls what happens next. All output is buffered and returned to the user at once — there is no streaming/incremental feedback. #### PASS — Forward the original command to p4d unchanged ``` action: PASS ``` #### REJECT — Deny with an error message shown to the user ``` action: REJECT message: You are not authorized to run 'p4 sudo mkblackbelt'. ``` #### REWRITE — Execute different command(s) against p4d instead ``` action: REWRITE command: sync arg: //ComponentA/...@2053 arg: //ComponentB/...@9876 ``` Multiple REWRITE blocks can be emitted sequentially to execute multiple commands as the result of a single intercepted command. #### Custom output (no action directive) For commands that p4d doesn't know about (like `p4 sudo`), the script can simply print text to stdout without an `action:` directive. That text is returned directly to the p4 client as the command's output. This is the primary mechanism for P4Sudo: the script does its work, prints status/results, and exits. --- ## Key Constraints (Broker Tips) 1. **No interactive prompts.** The script must complete its task and return output exactly once per invocation. You cannot prompt the user for additional input mid-execution. All needed information must come from the command arguments or the broker-injected stdin fields. 2. **All output arrives at once.** The user sees no scrolling progress text while the script runs. All stdout is buffered and delivered together when the script exits. For long-running operations, design accordingly (e.g. write progress to a log file, return a summary at the end). 3. **Script must be executable and reachable** by the p4broker process user. Use absolute paths in the broker config `execute` directive. 4. **The broker config regex matches the command name only** — not `p4` itself. `^(sudo)$` matches `p4 sudo ...`. --- ## How P4Sudo Uses This P4Sudo intercepts `p4 sudo ` and routes to a dispatcher script: ``` command: ^(sudo)$ { action = filter; execute = "/p4/common/site/p4sudo/p4sudo.sh"; } ``` The dispatcher (`p4sudo.sh`) reads the broker stdin to get the authenticated user and arguments, checks authorization against `p4sudo.cfg`, and either dispatches to the appropriate command script or rejects with an error. Because `sudo` is not a native p4d command, the script handles everything itself and prints results to stdout. No REWRITE to p4d is needed (though individual command scripts may run p4 commands internally using the service account). `p4 help sudo` interception uses a separate broker rule: ``` command: ^(help)$ { action = filter; execute = "/p4/common/site/p4sudo/p4sudo-help.sh"; } ``` The help script checks if the argument is `sudo` (or `sudo `) and returns the P4Sudo help text. For any other `p4 help` topic, it emits `action: PASS` to let p4d handle it normally.