github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/runsc/cmd/metricserver/metricserver.go (about) 1 package metricserver 2 3 import ( 4 "bytes" 5 "compress/flate" 6 7 "fmt" 8 "io" 9 "os" 10 "path" 11 "runtime" 12 "syscall" 13 14 _ "embed" 15 "golang.org/x/sys/unix" 16 ) 17 18 // BinaryName is the name of the embedded binary. 19 const BinaryName = "metricserver" 20 21 //go:embed metricserver.flate 22 var compressedBinary []byte 23 24 // Options is the set of options to execute the embedded binary. 25 type Options struct { 26 // Argv is the set of arguments to exec with. 27 // `Argv[0]` is the name of the binary as invoked. 28 // If Argv is empty, it will default to a single-element slice, with 29 // `Argv[0]` being the binary name. 30 Argv []string 31 32 // Envv is the set of environment variables to pass to the executed process. 33 Envv []string 34 35 // Files is the set of file descriptors to pass to forked processes. 36 // Only used when forking, not pure exec'ing. 37 Files []uintptr 38 } 39 40 // run decompresses and run the embedded binary with the given arguments. 41 // If fork is true, the binary runs in a separate process, and its PID is 42 // returned. 43 // Otherwise, the binary is exec'd, so the current process stops executing. 44 func run(options Options, fork bool) (int, error) { 45 if len(options.Argv) == 0 { 46 options.Argv = []string{BinaryName} 47 } 48 decompressed := flate.NewReader(bytes.NewReader(compressedBinary)) 49 runtime.LockOSThread() 50 defer runtime.UnlockOSThread() 51 myPID := os.Getpid() 52 oldMask := unix.Umask(0077) 53 defer unix.Umask(oldMask) 54 tmpDir, err := os.MkdirTemp("", "gvisor.*.tmp") 55 if err != nil { 56 return 0, fmt.Errorf("cannot create temp directory: %w", err) 57 } 58 tmpDirHandle, err := os.Open(tmpDir) 59 if err != nil { 60 return 0, fmt.Errorf("cannot open temp directory: %w", err) 61 } 62 defer tmpDirHandle.Close() 63 binPath := path.Join(tmpDir, BinaryName) 64 tmpFile, err := os.OpenFile(binPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0700) 65 if err != nil { 66 return 0, fmt.Errorf("cannot open temp file: %w", err) 67 } 68 if err := os.RemoveAll(tmpDir); err != nil { 69 return 0, fmt.Errorf("cannot remove temp directory: %w", err) 70 } 71 unix.Umask(oldMask) 72 if _, err := io.Copy(tmpFile, decompressed); err != nil { 73 tmpFile.Close() 74 return 0, fmt.Errorf("cannot decompress embedded binary or write it to temporary file: %w", err) 75 } 76 77 tmpFileReadOnly, err := os.OpenFile(fmt.Sprintf("/proc/%d/fd/%d", myPID, tmpFile.Fd()), os.O_RDONLY, 0700) 78 if err != nil { 79 tmpFile.Close() 80 return 0, fmt.Errorf("cannot re-open temp file for reading: %w", err) 81 } 82 if err := tmpFile.Close(); err != nil { 83 return 0, fmt.Errorf("cannot close temp file: %w", err) 84 } 85 defer tmpFileReadOnly.Close() 86 tmpFD := tmpFileReadOnly.Fd() 87 if _, err := unix.Seek(int(tmpFD), 0, unix.SEEK_SET); err != nil { 88 return 0, fmt.Errorf("cannot seek temp file back to 0: %w", err) 89 } 90 fdPath := fmt.Sprintf("/proc/%d/fd/%d", myPID, tmpFD) 91 if fork { 92 return syscall.ForkExec(fdPath, options.Argv, &syscall.ProcAttr{ 93 Env: options.Envv, 94 Files: options.Files, 95 }) 96 } 97 if err := unix.Exec(fdPath, options.Argv, options.Envv); err != nil { 98 return 0, fmt.Errorf("cannot exec embedded binary: %w", err) 99 } 100 panic("unreachable") 101 } 102 103 // Exec execs the embedded binary. The current process is replaced. 104 // This function only returns if unsuccessful. 105 func Exec(options Options) error { 106 _, err := run(options, false) 107 return err 108 } 109 110 // ForkExec runs the embedded binary in a separate process. 111 // Returns the PID of the child process. 112 func ForkExec(options Options) (int, error) { 113 return run(options, true) 114 }