4040// }
4141//
4242// func main() {
43- // if reexec.Init() {
43+ // if reexec.Init(); ok {
44+ // fmt.Println("an error happened:", err)
45+ //
4446// // Matched a reexec entrypoint; stop normal main execution.
4547// return
4648// }
@@ -80,15 +82,32 @@ import (
8082 "github.com/moby/sys/reexec/internal/reexecoverride"
8183)
8284
83- var entrypoints = make (map [string ]func () )
85+ var entrypoints = make (map [string ]func (context. Context ) error )
8486
8587// Register associates name with an entrypoint function to be executed when
8688// the current binary is invoked with argv[0] equal to name.
8789//
8890// Register is not safe for concurrent use; entrypoints must be registered
89- // during program initialization, before calling [Init].
91+ // during program initialization, before calling [Dispatch] or [ Init].
9092// It panics if name contains a path component or is already registered.
9193func Register (name string , entrypoint func ()) {
94+ registerEntrypoint (name , func (context.Context ) error {
95+ entrypoint ()
96+ return nil
97+ })
98+ }
99+
100+ // RegisterContext is like [Register] but the entrypoint receives a context and
101+ // reports success or failure by returning an error.
102+ //
103+ // RegisterContext is not safe for concurrent use; entrypoints must be
104+ // registered during program initialization, before calling [Dispatch] or [Init].
105+ // It panics if name contains a path component or is already registered.
106+ func RegisterContext (name string , entrypoint func (context.Context ) error ) {
107+ registerEntrypoint (name , entrypoint )
108+ }
109+
110+ func registerEntrypoint (name string , entrypoint func (context.Context ) error ) {
92111 if filepath .Base (name ) != name {
93112 panic (fmt .Sprintf ("reexec func does not expect a path component: %q" , name ))
94113 }
@@ -99,18 +118,45 @@ func Register(name string, entrypoint func()) {
99118 entrypoints [name ] = entrypoint
100119}
101120
121+ // Dispatch checks whether the current process was invoked under a registered
122+ // name (based on filepath.Base(os.Args[0])).
123+ //
124+ // If a matching entrypoint is found, it is executed and Dispatch reports a
125+ // match with ok. If ok is false, err is always nil. If ok is true, err is the
126+ // entrypoint's return value.
127+ //
128+ // In the ok=true case, the caller should stop normal main execution (typically
129+ // by returning from main or calling os.Exit).
130+ func Dispatch () (bool , error ) {
131+ return dispatch (context .Background ())
132+ }
133+
134+ // DispatchContext is like [Dispatch] but includes a context.
135+ func DispatchContext (ctx context.Context ) (bool , error ) {
136+ return dispatch (ctx )
137+ }
138+
102139// Init checks whether the current process was invoked under a registered name
103140// (based on filepath.Base(os.Args[0])).
104141//
105142// If a matching entrypoint is found, it is executed and Init returns true. In
106143// that case, the caller should stop normal main execution. If no match is found,
107144// Init returns false and normal execution should continue.
145+ //
146+ // Init is provided for compatibility with older callers. New code should
147+ // prefer [Dispatch] or [DispatchContext], which also return the entrypoint's
148+ // error and allow the caller to decide how to report failures.
108149func Init () bool {
109- if entrypoint , ok := entrypoints [filepath .Base (os .Args [0 ])]; ok {
110- entrypoint ()
111- return true
150+ ok , _ := dispatch (context .Background ())
151+ return ok
152+ }
153+
154+ func dispatch (ctx context.Context ) (bool , error ) {
155+ entrypoint , ok := entrypoints [filepath .Base (os .Args [0 ])]
156+ if ! ok {
157+ return false , nil
112158 }
113- return false
159+ return true , entrypoint ( ctx )
114160}
115161
116162// Command returns an [*exec.Cmd] configured to re-execute the current binary,
0 commit comments