github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/stage1/init/kvm.go (about) 1 // Copyright 2014 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 //+build linux 16 17 package main 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strings" 25 "syscall" 26 27 "github.com/appc/spec/schema" 28 "github.com/coreos/go-systemd/util" 29 "github.com/hashicorp/errwrap" 30 "github.com/rkt/rkt/common" 31 "github.com/rkt/rkt/networking" 32 "github.com/rkt/rkt/pkg/mountinfo" 33 stage1commontypes "github.com/rkt/rkt/stage1/common/types" 34 stage1initcommon "github.com/rkt/rkt/stage1/init/common" 35 "github.com/rkt/rkt/stage1/init/kvm" 36 ) 37 38 const journalDir = "/var/log/journal" 39 40 // Supported hypervisors 41 var hypervisors = [...]string{"lkvm", "qemu"} 42 43 // KvmNetworkingToSystemd generates systemd unit files for a pod according to network configuration 44 func KvmNetworkingToSystemd(p *stage1commontypes.Pod, n *networking.Networking) error { 45 podRoot := common.Stage1RootfsPath(p.Root) 46 47 // networking 48 netDescriptions := kvm.GetNetworkDescriptions(n) 49 if err := kvm.GenerateNetworkInterfaceUnits(filepath.Join(podRoot, stage1initcommon.UnitsDir), netDescriptions); err != nil { 50 return errwrap.Wrap(errors.New("failed to transform networking to units"), err) 51 } 52 53 return nil 54 } 55 56 func mountSharedVolumes(p *stage1commontypes.Pod, ra *schema.RuntimeApp) error { 57 appName := ra.Name 58 59 sharedVolPath, err := common.CreateSharedVolumesPath(p.Root) 60 if err != nil { 61 return err 62 } 63 64 imageManifest := p.Images[appName.String()] 65 mounts, err := stage1initcommon.GenerateMounts(ra, p.Manifest.Volumes, stage1initcommon.ConvertedFromDocker(imageManifest)) 66 if err != nil { 67 return err 68 } 69 for _, m := range mounts { 70 absRoot, err := filepath.Abs(p.Root) // Absolute path to the pod's rootfs. 71 if err != nil { 72 return errwrap.Wrap(errors.New("could not get pod's root absolute path"), err) 73 } 74 75 absAppRootfs := common.AppRootfsPath(absRoot, appName) 76 if err != nil { 77 return fmt.Errorf(`could not evaluate absolute path for application rootfs in app: %v`, appName) 78 } 79 80 mntPath, err := stage1initcommon.EvaluateSymlinksInsideApp(absAppRootfs, m.Mount.Path) 81 if err != nil { 82 return errwrap.Wrap(fmt.Errorf("could not evaluate path %v", m.Mount.Path), err) 83 } 84 absDestination := filepath.Join(absAppRootfs, mntPath) 85 shPath := filepath.Join(sharedVolPath, m.Volume.Name.String()) 86 if err := stage1initcommon.PrepareMountpoints(shPath, absDestination, &m.Volume, m.DockerImplicit); err != nil { 87 return err 88 } 89 90 source := m.Source(p.Root) 91 if cleanedSource, err := filepath.EvalSymlinks(source); err != nil { 92 return errwrap.Wrap(fmt.Errorf("could not resolve symlink for source: %v", source), err) 93 } else if err := ensureDestinationExists(cleanedSource, absDestination); err != nil { 94 return errwrap.Wrap(fmt.Errorf("could not create destination mount point: %v", absDestination), err) 95 } else if err := doBindMount(cleanedSource, absDestination, m.ReadOnly, m.Volume.Recursive); err != nil { 96 return errwrap.Wrap(fmt.Errorf("could not bind mount path %v (s: %v, d: %v)", m.Mount.Path, source, absDestination), err) 97 } 98 } 99 return nil 100 } 101 102 func doBindMount(source, destination string, readOnly bool, recursive *bool) error { 103 var flags uintptr = syscall.MS_BIND 104 105 // Enable recursive by default and remove it if explicitly requested 106 recursiveBool := recursive == nil || *recursive == true 107 if recursiveBool { 108 flags |= syscall.MS_REC 109 } 110 111 if err := syscall.Mount(source, destination, "bind", flags, ""); err != nil { 112 return errwrap.Wrap(fmt.Errorf("error mounting %s", destination), err) 113 } 114 115 // Linux can't bind-mount with readonly in a single operation, so remount +ro 116 if readOnly { 117 if err := syscall.Mount("", destination, "none", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, ""); err != nil { 118 return errwrap.Wrap(fmt.Errorf("error remounting read-only %s", destination), err) 119 } 120 } 121 122 if readOnly && recursiveBool { 123 // Sub-mounts are still read-write, so find them and remount them read-only 124 125 mnts, err := mountinfo.ParseMounts(0) 126 if err != nil { 127 return errwrap.Wrap(fmt.Errorf("error getting mounts under %q from mountinfo", source), err) 128 } 129 mnts = mnts.Filter(mountinfo.HasPrefix(source + "/")) 130 131 for _, mnt := range mnts { 132 innerAbsPath := destination + strings.Replace(mnt.MountPoint, source, "", -1) 133 if err := syscall.Mount("", innerAbsPath, "none", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, ""); err != nil { 134 return errwrap.Wrap(fmt.Errorf("error remounting child mount %s read-only", innerAbsPath), err) 135 } 136 } 137 } 138 139 return nil 140 } 141 142 func ensureDestinationExists(source, destination string) error { 143 fileInfo, err := os.Stat(source) 144 if err != nil { 145 return errwrap.Wrap(fmt.Errorf("could not stat source location: %v", source), err) 146 } 147 148 targetPathParent, _ := filepath.Split(destination) 149 if err := os.MkdirAll(targetPathParent, common.SharedVolumePerm); err != nil { 150 return errwrap.Wrap(fmt.Errorf("could not create parent directory: %v", targetPathParent), err) 151 } 152 153 if fileInfo.IsDir() { 154 if err := os.Mkdir(destination, common.SharedVolumePerm); !os.IsExist(err) { 155 return err 156 } 157 } else { 158 if file, err := os.OpenFile(destination, os.O_CREATE, common.SharedVolumePerm); err != nil { 159 return err 160 } else { 161 file.Close() 162 } 163 } 164 return nil 165 } 166 167 func prepareMountsForApp(p *stage1commontypes.Pod, ra *schema.RuntimeApp) error { 168 // bind mount all shared volumes (we don't use mechanism for bind-mounting given by nspawn) 169 if err := mountSharedVolumes(p, ra); err != nil { 170 return errwrap.Wrap(errors.New("failed to prepare mount point"), err) 171 } 172 173 return nil 174 } 175 176 func KvmPrepareMounts(p *stage1commontypes.Pod) error { 177 for i := range p.Manifest.Apps { 178 ra := &p.Manifest.Apps[i] 179 if err := prepareMountsForApp(p, ra); err != nil { 180 return errwrap.Wrap(fmt.Errorf("failed prepare mounts for app %q", ra.Name), err) 181 } 182 } 183 184 return nil 185 } 186 187 func KvmCheckHypervisor(s1Root string) (string, error) { 188 for _, hv := range hypervisors { 189 if _, err := os.Stat(filepath.Join(s1Root, hv)); err == nil { 190 return hv, nil 191 } 192 } 193 return "", fmt.Errorf("unrecognized hypervisor") 194 } 195 196 func linkJournal(s1Root, machineID string) error { 197 if !util.IsRunningSystemd() { 198 return nil 199 } 200 201 absS1Root, err := filepath.Abs(s1Root) 202 if err != nil { 203 return err 204 } 205 206 // /var/log/journal doesn't exist on the host, don't do anything 207 if _, err := os.Stat(journalDir); os.IsNotExist(err) { 208 return nil 209 } 210 211 machineJournalDir := filepath.Join(journalDir, machineID) 212 podJournalDir := filepath.Join(absS1Root, machineJournalDir) 213 214 hostMachineID, err := util.GetMachineID() 215 if err != nil { 216 return err 217 } 218 219 // unlikely, machine ID is random (== pod UUID) 220 if hostMachineID == machineID { 221 return fmt.Errorf("host and pod machine IDs are equal (%s)", machineID) 222 } 223 224 fi, err := os.Lstat(machineJournalDir) 225 switch { 226 case os.IsNotExist(err): 227 // good, we'll create the symlink 228 case err != nil: 229 return err 230 // unlikely, machine ID is random (== pod UUID) 231 default: 232 if fi.IsDir() { 233 if err := os.Remove(machineJournalDir); err != nil { 234 return err 235 } 236 } 237 238 link, err := os.Readlink(machineJournalDir) 239 if err != nil { 240 return err 241 } 242 243 if link == podJournalDir { 244 return nil 245 } else { 246 if err := os.Remove(machineJournalDir); err != nil { 247 return err 248 } 249 } 250 } 251 252 if err := os.Symlink(podJournalDir, machineJournalDir); err != nil { 253 return err 254 } 255 256 return nil 257 }