4040// }
4141//
4242// func main() {
43- // if reexec.Init() {
43+ // if ok, _ := reexec.Dispatch(); ok {
4444// // Matched a reexec entrypoint; stop normal main execution.
4545// return
4646// }
@@ -80,15 +80,32 @@ import (
8080 "github.com/moby/sys/reexec/internal/reexecoverride"
8181)
8282
83- var entrypoints = make (map [string ]func () )
83+ var entrypoints = make (map [string ]func (context. Context ) error )
8484
8585// Register associates name with an entrypoint function to be executed when
8686// the current binary is invoked with argv[0] equal to name.
8787//
8888// Register is not safe for concurrent use; entrypoints must be registered
89- // during program initialization, before calling [Init].
89+ // during program initialization, before calling [Dispatch] or [ Init].
9090// It panics if name contains a path component or is already registered.
9191func Register (name string , entrypoint func ()) {
92+ registerEntrypoint (name , func (context.Context ) error {
93+ entrypoint ()
94+ return nil
95+ })
96+ }
97+
98+ // RegisterContext is like [Register] but the entrypoint receives a context and
99+ // reports success or failure by returning an error.
100+ //
101+ // RegisterContext is not safe for concurrent use; entrypoints must be
102+ // registered during program initialization, before calling [Dispatch] or [Init].
103+ // It panics if name contains a path component or is already registered.
104+ func RegisterContext (name string , entrypoint func (context.Context ) error ) {
105+ registerEntrypoint (name , entrypoint )
106+ }
107+
108+ func registerEntrypoint (name string , entrypoint func (context.Context ) error ) {
92109 if filepath .Base (name ) != name {
93110 panic (fmt .Sprintf ("reexec func does not expect a path component: %q" , name ))
94111 }
@@ -99,18 +116,45 @@ func Register(name string, entrypoint func()) {
99116 entrypoints [name ] = entrypoint
100117}
101118
119+ // Dispatch checks whether the current process was invoked under a registered
120+ // name (based on filepath.Base(os.Args[0])).
121+ //
122+ // If a matching entrypoint is found, it is executed and Dispatch reports a
123+ // match with ok. If ok is false, err is always nil. If ok is true, err is the
124+ // entrypoint's return value.
125+ //
126+ // In the ok=true case, the caller should stop normal main execution (typically
127+ // by returning from main or calling os.Exit).
128+ func Dispatch () (bool , error ) {
129+ return dispatch (context .Background ())
130+ }
131+
132+ // DispatchContext is like [Dispatch] but includes a context.
133+ func DispatchContext (ctx context.Context ) (bool , error ) {
134+ return dispatch (ctx )
135+ }
136+
102137// Init checks whether the current process was invoked under a registered name
103138// (based on filepath.Base(os.Args[0])).
104139//
105140// If a matching entrypoint is found, it is executed and Init returns true. In
106141// that case, the caller should stop normal main execution. If no match is found,
107142// Init returns false and normal execution should continue.
143+ //
144+ // Init is provided for compatibility with older callers. New code should
145+ // prefer [Dispatch] or [DispatchContext], which also return the entrypoint's
146+ // error and allow the caller to decide how to report failures.
108147func Init () bool {
109- if entrypoint , ok := entrypoints [filepath .Base (os .Args [0 ])]; ok {
110- entrypoint ()
111- return true
148+ ok , _ := dispatch (context .Background ())
149+ return ok
150+ }
151+
152+ func dispatch (ctx context.Context ) (bool , error ) {
153+ entrypoint , ok := entrypoints [filepath .Base (os .Args [0 ])]
154+ if ! ok {
155+ return false , nil
112156 }
113- return false
157+ return true , entrypoint ( ctx )
114158}
115159
116160// Command returns an [*exec.Cmd] configured to re-execute the current binary,
0 commit comments