// WaitOnDevices waits for the devices named in devs to be plugged before returning. func WaitOnDevices(devs []string, stage string) error { conn, err := dbus.NewSystemdConnection() if err != nil { return err } results := map[string]chan string{} for _, dev := range devs { unitName := unit.UnitNamePathEscape(dev + ".device") results[unitName] = make(chan string) if _, err = conn.StartUnit(unitName, "replace", results[unitName]); err != nil { return fmt.Errorf("failed starting device unit %s: %v", unitName, err) } } for unitName, result := range results { s := <-result if s != "done" { return fmt.Errorf("device unit %s %s", unitName, s) } } return nil }
// installNewMountUnit creates and installs a new mount unit in the default // systemd location (/usr/lib/systemd/system) inside the pod stage1 filesystem. // root is pod's absolute stage1 path (from Pod.Root). // beforeAndrequiredBy creates a systemd unit dependency (can be space separated // for multi). // It returns the name of the generated unit. func installNewMountUnit(root, what, where, fsType, options, beforeAndrequiredBy, unitsDir string) (string, error) { opts := []*unit.UnitOption{ unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Mount unit for %s", where)), unit.NewUnitOption("Unit", "DefaultDependencies", "false"), unit.NewUnitOption("Unit", "Before", beforeAndrequiredBy), unit.NewUnitOption("Mount", "What", what), unit.NewUnitOption("Mount", "Where", where), unit.NewUnitOption("Mount", "Type", fsType), unit.NewUnitOption("Mount", "Options", options), unit.NewUnitOption("Install", "RequiredBy", beforeAndrequiredBy), } unitsPath := filepath.Join(root, unitsDir) unitName := unit.UnitNamePathEscape(where + ".mount") if err := writeUnit(opts, filepath.Join(unitsPath, unitName)); err != nil { return "", err } log.Printf("mount unit created: %q in %q (what=%q, where=%q)", unitName, unitsPath, what, where) return unitName, nil }
// AppToSystemdMountUnits prepare bind mount unit for empty or host kind mounting // between stage1 rootfs and chrooted filesystem for application func AppToSystemdMountUnits(root string, appName types.ACName, volumes []types.Volume, ra *schema.RuntimeApp, unitsDir string) error { app := ra.App vols := make(map[types.ACName]types.Volume) for _, v := range volumes { vols[v.Name] = v } mounts := GenerateMounts(ra, vols) for _, m := range mounts { vol := vols[m.Volume] // source relative to stage1 rootfs to relative pod root whatPath := filepath.Join(stage1MntDir, vol.Name.String()) whatFullPath := filepath.Join(root, whatPath) if vol.Kind == "empty" { log.Printf("creating an empty volume folder for sharing: %q", whatFullPath) err := os.MkdirAll(whatFullPath, 0700) if err != nil { return err } } // destination relative to stage1 rootfs and relative to pod root wherePath := filepath.Join(common.RelAppRootfsPath(appName), m.Path) whereFullPath := filepath.Join(root, wherePath) // assertion to make sure that "what" exists (created earlier by PodToSystemdHostMountUnits) log.Printf("checking required source path: %q", whatFullPath) if _, err := os.Stat(whatFullPath); os.IsNotExist(err) { return fmt.Errorf("bug: missing source for volume %v", vol.Name) } // optionally prepare app directory log.Printf("optionally preparing destination path: %q", whereFullPath) err := os.MkdirAll(whereFullPath, 0700) if err != nil { return errwrap.Wrap(fmt.Errorf("failed to prepare dir for mount %v", m.Volume), err) } // install new mount unit for bind mount /mnt/volumeName -> /opt/stage2/{app-id}/rootfs/{{mountPoint.Path}} mu, err := installNewMountUnit( root, // where put a mount unit whatPath, // what - stage1 rootfs /mnt/VolumeName wherePath, // where - inside chroot app filesystem "bind", // fstype "bind", // options serviceUnitName(appName), unitsDir, ) if err != nil { return errwrap.Wrap(fmt.Errorf("cannot install new mount unit for app %q", appName.String()), err) } // TODO(iaguis) when we update util-linux to 2.27, this code can go // away and we can bind-mount RO with one unit file. // http://ftp.kernel.org/pub/linux/utils/util-linux/v2.27/v2.27-ReleaseNotes if IsMountReadOnly(vol, app.MountPoints) { opts := []*unit.UnitOption{ unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Remount read-only unit for %s", wherePath)), unit.NewUnitOption("Unit", "DefaultDependencies", "false"), unit.NewUnitOption("Unit", "After", mu), unit.NewUnitOption("Unit", "Wants", mu), unit.NewUnitOption("Service", "ExecStart", fmt.Sprintf("/usr/bin/mount -o remount,ro %s", wherePath)), unit.NewUnitOption("Install", "RequiredBy", mu), } remountUnitPath := filepath.Join(root, unitsDir, unit.UnitNamePathEscape(wherePath+"-remount.service")) if err := writeUnit(opts, remountUnitPath); err != nil { return err } } } return nil }
func devToUnitName(dev string) string { return "torus-" + unit.UnitNamePathEscape(dev) + ".service" }
// InstantiatedPrepareAppUnitName returns the systemd service unit name for prepare-app // instantiated for the given root. func InstantiatedPrepareAppUnitName(appName types.ACName) string { // Naming respecting escaping rules, see systemd.unit(5) and systemd-escape(1) escapedRoot := unit.UnitNamePathEscape(common.RelAppRootfsPath(appName)) return "prepare-app@" + escapedRoot + ".service" }