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  }