github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/pkg/multicall/multicall.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // inspired by github.com/docker/docker/pkg/reexec 16 17 //+build linux 18 19 package multicall 20 21 import ( 22 "fmt" 23 "os" 24 "os/exec" 25 "path" 26 "syscall" 27 ) 28 29 var exePath string 30 31 func init() { 32 // save the program path 33 var err error 34 exePath, err = os.Readlink("/proc/self/exe") 35 if err != nil { 36 panic("cannot get current executable") 37 } 38 } 39 40 type commandFn func() error 41 42 var commands = make(map[string]commandFn) 43 44 // Entrypoint provides the access to a multicall command. 45 type Entrypoint string 46 47 // Add adds a new multicall command. name is the command name and fn is the 48 // function that will be executed for the specified command. It returns the 49 // related Entrypoint. 50 // Packages adding new multicall commands should call Add in their init 51 // function. 52 func Add(name string, fn commandFn) Entrypoint { 53 if _, ok := commands[name]; ok { 54 panic(fmt.Errorf("command with name %q already exists", name)) 55 } 56 commands[name] = fn 57 return Entrypoint(name) 58 } 59 60 // MaybeExec should be called at the start of the program, if the process argv[0] is 61 // a name registered with multicall, the related function will be executed. 62 // If the functions returns an error, it will be printed to stderr and will 63 // exit with an exit status of 1, otherwise it will exit with a 0 exit 64 // status. 65 func MaybeExec() { 66 name := path.Base(os.Args[0]) 67 if fn, ok := commands[name]; ok { 68 if err := fn(); err != nil { 69 fmt.Fprintln(os.Stderr, err) 70 os.Exit(254) 71 } 72 os.Exit(0) 73 } 74 } 75 76 // Cmd will prepare the *exec.Cmd for the given entrypoint, configured with the 77 // provided args. 78 func (e Entrypoint) Cmd(args ...string) *exec.Cmd { 79 // append the Entrypoint as argv[0] 80 args = append([]string{string(e)}, args...) 81 return &exec.Cmd{ 82 Path: exePath, 83 Args: args, 84 SysProcAttr: &syscall.SysProcAttr{ 85 Pdeathsig: syscall.SIGTERM, 86 }, 87 } 88 }