41. pepsi-stage-dot-forward¶
Process per-user ~/.forward files.
41.1. Role¶
pepsi-stage-dot-forward lets a local account owner redirect their own mail
with a ~/.forward file. For each envelope recipient that is local (the same
LOCAL_DOMAINS / TARGETS / RECIPIENT_DELIMITER test as
pepsi-stage-relay-to-maildir) it runs that user’s ~/.forward via the
setuid-root pepsi-helper-dot-forward(1) helper, which drops to the
user first. Non-local recipients are left untouched. Normally placed just before
local delivery. Reference: pepsi-stage-dot-forward(1).
41.2. Features¶
41.2.1. Per-recipient outcomes¶
No ~/.forward — the recipient is a passthrough and advances to
NEXT_STAGE(typically local delivery).Forwarded — the recipient is replaced by the addresses the
~/.forwardnamed; those restart the pipeline atRESTART_STAGE(defaultinit) so they are re-authenticated and re-routed like fresh mail. An empty result (the message was consumed by a|pipe//filedirective) drops the recipient.Failed — a failed
~/.forward(pipe command error, file write error, or a disabled directive) routes the recipient toBOUNCE_STAGE.Host problem — the helper could not act at all (not installed setuid-root, a passwd lookup failure, a home directory or
~/.forwardthat is not exclusively the user’s). Nothing is bounced: the message stays queued and is retried once the host is fixed.
A message with several recipients can mix these; the forwarded addresses, the per-recipient bounces and the kept recipients are reconciled in one database round-trip.
41.2.2. ~/.forward directives¶
The helper acts on each non-empty, non-# line as the user: a bare address
becomes a forwarding address; |command pipes the message to a shell when
ALLOW_PIPE is on; /absolute/path appends the message to a file when
ALLOW_FILE is on. When both ALLOW_PIPE and ALLOW_FILE are off the
message body is not even handed to the helper.
An address written with sendmail’s leading \ means “deliver to this name and
expand it no further”, and is not restarted at RESTART_STAGE. The
canonical keep-a-local-copy line — \bob in ~bob/.forward — simply keeps
that envelope recipient on this row, so it advances to NEXT_STAGE and is
delivered locally; any other \name becomes a sibling row at NEXT_STAGE.
That is also what stops a \ entry re-entering this stage.
41.2.3. Loop prevention¶
Because forwarding restarts the pipeline, every forwarded row carries its
forwarding chain in state["dot-forwarders"] (see The message state): the chain
of the row it came from plus the login whose ~/.forward forwarded it. A
recipient whose user is already in its row’s chain is routed to
BOUNCE_STAGE without re-running its ~/.forward (a null-sender message is
dropped instead), so a forwarding cycle terminates and the sender is told. The
chain is per forwarding path: several recipients of one message that forward
each get a sibling row carrying only their own chain.
41.3. Privileges¶
The stage must be installed setgid pepsi-forward (mode 2550, owner
pepsi:pepsi-forward — owner-execute, so no other local user reaches the
setgid bit); that group
membership is what lets the dispatcher’s pepsi worker exec the
4750 root:pepsi-forward helper, which alone runs the ~/.forward as the
target user and refuses to act for root. Carrying that bit is why the stage
is a standalone binary rather than one of the programs folded into the
unified pepsi binary.
41.4. Configuration¶
[stage-<name>]: PROGRAM = pepsi-stage-dot-forward, NEXT_STAGE (where
a passthrough recipient goes — normally local delivery), BOUNCE_STAGE (where
a failed ~/.forward recipient goes), RESTART_STAGE (default init),
ALLOW_PIPE and ALLOW_FILE (both default yes), HELPER (default
pepsi-helper-dot-forward) and the shared locality options LOCAL_DOMAINS
(defaulting to [pepsi-ingress] ACCEPTED_DOMAINS), TARGETS and
RECIPIENT_DELIMITER. pepsi-setup checks that RESTART_STAGE names a
real stage. See pepsi-stage-dot-forward(1).
41.5. State¶
Inputs:
state["dot-forwarders"](the loop guard) andstate.dsn.Outputs:
state["dot-forwarders"]extended with each login whose~/.forwardran, on every row the fan-out produces;state.dsn.rcptis rebuilt in lockstep with each row’s recipients, and a bounced recipient’s row carriesstate.bounce.
41.6. See also¶
pepsi-stage-relay-to-maildir, pepsi-stage-aliases, pepsi-stage-bounce, Supported Features, pepsi-stage-dot-forward(1), pepsi-helper-dot-forward(1).