github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/cmd/install.go (about) 1 // Copyright 2019 The gVisor 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 package cmd 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "io/ioutil" 22 "log" 23 "os" 24 "path" 25 26 "github.com/google/subcommands" 27 "github.com/SagerNet/gvisor/runsc/flag" 28 ) 29 30 // Install implements subcommands.Command. 31 type Install struct { 32 ConfigFile string 33 Runtime string 34 Experimental bool 35 } 36 37 // Name implements subcommands.Command.Name. 38 func (*Install) Name() string { 39 return "install" 40 } 41 42 // Synopsis implements subcommands.Command.Synopsis. 43 func (*Install) Synopsis() string { 44 return "adds a runtime to docker daemon configuration" 45 } 46 47 // Usage implements subcommands.Command.Usage. 48 func (*Install) Usage() string { 49 return `install [flags] <name> [-- [args...]] -- if provided, args are passed to the runtime 50 ` 51 } 52 53 // SetFlags implements subcommands.Command.SetFlags. 54 func (i *Install) SetFlags(fs *flag.FlagSet) { 55 fs.StringVar(&i.ConfigFile, "config_file", "/etc/docker/daemon.json", "path to Docker daemon config file") 56 fs.StringVar(&i.Runtime, "runtime", "runsc", "runtime name") 57 fs.BoolVar(&i.Experimental, "experimental", false, "enable experimental features") 58 } 59 60 // Execute implements subcommands.Command.Execute. 61 func (i *Install) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { 62 // Grab the name and arguments. 63 runtimeArgs := f.Args() 64 65 // Extract the executable. 66 path, err := os.Executable() 67 if err != nil { 68 log.Fatalf("Error reading current exectuable: %v", err) 69 } 70 71 // Load the configuration file. 72 c, err := readConfig(i.ConfigFile) 73 if err != nil { 74 log.Fatalf("Error reading config file %q: %v", i.ConfigFile, err) 75 } 76 77 // Add the given runtime. 78 var rts map[string]interface{} 79 if i, ok := c["runtimes"]; ok { 80 rts = i.(map[string]interface{}) 81 } else { 82 rts = make(map[string]interface{}) 83 c["runtimes"] = rts 84 } 85 rts[i.Runtime] = struct { 86 Path string `json:"path,omitempty"` 87 RuntimeArgs []string `json:"runtimeArgs,omitempty"` 88 }{ 89 Path: path, 90 RuntimeArgs: runtimeArgs, 91 } 92 93 // Set experimental if required. 94 if i.Experimental { 95 c["experimental"] = true 96 } 97 98 // Write out the runtime. 99 if err := writeConfig(c, i.ConfigFile); err != nil { 100 log.Fatalf("Error writing config file %q: %v", i.ConfigFile, err) 101 } 102 103 // Success. 104 log.Printf("Added runtime %q with arguments %v to %q.", i.Runtime, runtimeArgs, i.ConfigFile) 105 return subcommands.ExitSuccess 106 } 107 108 // Uninstall implements subcommands.Command. 109 type Uninstall struct { 110 ConfigFile string 111 Runtime string 112 } 113 114 // Name implements subcommands.Command.Name. 115 func (*Uninstall) Name() string { 116 return "uninstall" 117 } 118 119 // Synopsis implements subcommands.Command.Synopsis. 120 func (*Uninstall) Synopsis() string { 121 return "removes a runtime from docker daemon configuration" 122 } 123 124 // Usage implements subcommands.Command.Usage. 125 func (*Uninstall) Usage() string { 126 return `uninstall [flags] <name> 127 ` 128 } 129 130 // SetFlags implements subcommands.Command.SetFlags. 131 func (u *Uninstall) SetFlags(fs *flag.FlagSet) { 132 fs.StringVar(&u.ConfigFile, "config_file", "/etc/docker/daemon.json", "path to Docker daemon config file") 133 fs.StringVar(&u.Runtime, "runtime", "runsc", "runtime name") 134 } 135 136 // Execute implements subcommands.Command.Execute. 137 func (u *Uninstall) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { 138 log.Printf("Removing runtime %q from %q.", u.Runtime, u.ConfigFile) 139 140 c, err := readConfig(u.ConfigFile) 141 if err != nil { 142 log.Fatalf("Error reading config file %q: %v", u.ConfigFile, err) 143 } 144 145 var rts map[string]interface{} 146 if i, ok := c["runtimes"]; ok { 147 rts = i.(map[string]interface{}) 148 } else { 149 log.Fatalf("runtime %q not found", u.Runtime) 150 } 151 if _, ok := rts[u.Runtime]; !ok { 152 log.Fatalf("runtime %q not found", u.Runtime) 153 } 154 delete(rts, u.Runtime) 155 156 if err := writeConfig(c, u.ConfigFile); err != nil { 157 log.Fatalf("Error writing config file %q: %v", u.ConfigFile, err) 158 } 159 return subcommands.ExitSuccess 160 } 161 162 func readConfig(path string) (map[string]interface{}, error) { 163 // Read the configuration data. 164 configBytes, err := ioutil.ReadFile(path) 165 if err != nil && !os.IsNotExist(err) { 166 return nil, err 167 } 168 169 // Unmarshal the configuration. 170 c := make(map[string]interface{}) 171 if len(configBytes) > 0 { 172 if err := json.Unmarshal(configBytes, &c); err != nil { 173 return nil, err 174 } 175 } 176 177 return c, nil 178 } 179 180 func writeConfig(c map[string]interface{}, filename string) error { 181 // Marshal the configuration. 182 b, err := json.MarshalIndent(c, "", " ") 183 if err != nil { 184 return err 185 } 186 187 // Copy the old configuration. 188 old, err := ioutil.ReadFile(filename) 189 if err != nil { 190 if !os.IsNotExist(err) { 191 return fmt.Errorf("error reading config file %q: %v", filename, err) 192 } 193 } else { 194 if err := ioutil.WriteFile(filename+"~", old, 0644); err != nil { 195 return fmt.Errorf("error backing up config file %q: %v", filename, err) 196 } 197 } 198 199 // Make the necessary directories. 200 if err := os.MkdirAll(path.Dir(filename), 0755); err != nil { 201 return fmt.Errorf("error creating config directory for %q: %v", filename, err) 202 } 203 204 // Write the new configuration. 205 if err := ioutil.WriteFile(filename, b, 0644); err != nil { 206 return fmt.Errorf("error writing config file %q: %v", filename, err) 207 } 208 209 return nil 210 }