diff options
author | Andrew Cady <d@jerkface.net> | 2018-07-16 07:41:33 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2018-07-16 07:41:33 -0400 |
commit | d66f1b658186356df2d3c40994853da596c2b1e5 (patch) | |
tree | 34f45c96815cf298c0cbebf2ed740ee6788f80ce /fsmgr.hs | |
parent | e4520bb718e8eca78b8ce2eee972830afb893b70 (diff) |
basic chroot support
Diffstat (limited to 'fsmgr.hs')
-rw-r--r-- | fsmgr.hs | 69 |
1 files changed, 53 insertions, 16 deletions
@@ -1,11 +1,13 @@ | |||
1 | {-# LANGUAGE ViewPatterns #-} | ||
1 | {-# OPTIONS_GHC -fno-warn-type-defaults #-} | 2 | {-# OPTIONS_GHC -fno-warn-type-defaults #-} |
2 | {-# LANGUAGE ExtendedDefaultRules #-} | 3 | {-# LANGUAGE DuplicateRecordFields #-} |
3 | {-# LANGUAGE LambdaCase #-} | 4 | {-# LANGUAGE ExtendedDefaultRules #-} |
4 | {-# LANGUAGE NamedFieldPuns #-} | 5 | {-# LANGUAGE LambdaCase #-} |
5 | {-# LANGUAGE NoImplicitPrelude #-} | 6 | {-# LANGUAGE NamedFieldPuns #-} |
6 | {-# LANGUAGE OverloadedStrings #-} | 7 | {-# LANGUAGE NoImplicitPrelude #-} |
7 | {-# LANGUAGE RecordWildCards #-} | 8 | {-# LANGUAGE OverloadedStrings #-} |
8 | {-# LANGUAGE ScopedTypeVariables #-} | 9 | {-# LANGUAGE RecordWildCards #-} |
10 | {-# LANGUAGE ScopedTypeVariables #-} | ||
9 | 11 | ||
10 | module Main where | 12 | module Main where |
11 | import Rebase.Prelude hiding (bool, hash, o, (<.>)) | 13 | import Rebase.Prelude hiding (bool, hash, o, (<.>)) |
@@ -17,9 +19,11 @@ import Development.Shake hiding (getEnv) | |||
17 | import Development.Shake.Command () | 19 | import Development.Shake.Command () |
18 | import Development.Shake.FilePath | 20 | import Development.Shake.FilePath |
19 | 21 | ||
20 | import Options.Applicative as Opt | 22 | import qualified Options.Applicative as Opt |
23 | ;import Options.Applicative hiding (action) | ||
21 | 24 | ||
22 | import ConfigFile | 25 | import ConfigFile |
26 | import System.Posix.Process (getProcessID) | ||
23 | 27 | ||
24 | noParent :: BaseImageSpecification -> Bool | 28 | noParent :: BaseImageSpecification -> Bool |
25 | noParent (EmptyImageOfBytes _) = True | 29 | noParent (EmptyImageOfBytes _) = True |
@@ -64,6 +68,8 @@ buildRoot DiskImageConfig{..} finalOut = do | |||
64 | -- assume that this has already happened. | 68 | -- assume that this has already happened. |
65 | cmd_ "selfstrap --skip-update" (("--unpack" `consWhen` unpackOnly) ["-t", mountpoint]) packageNames | 69 | cmd_ "selfstrap --skip-update" (("--unpack" `consWhen` unpackOnly) ["-t", mountpoint]) packageNames |
66 | forM_ debs $ \pkg -> do cmd_ "dpkg -i --root" [mountpoint, pkg] | 70 | forM_ debs $ \pkg -> do cmd_ "dpkg -i --root" [mountpoint, pkg] |
71 | {- 2.5. install apt package cache -} | ||
72 | -- TODO | ||
67 | {- 3. binaries -} | 73 | {- 3. binaries -} |
68 | forM_ (unpack <$> binaries) $ \b -> do | 74 | forM_ (unpack <$> binaries) $ \b -> do |
69 | p <- fromMaybe (fail $ "not found in ${PATH}: " ++ b) <$> liftIO (pathLocate b) | 75 | p <- fromMaybe (fail $ "not found in ${PATH}: " ++ b) <$> liftIO (pathLocate b) |
@@ -95,19 +101,20 @@ createDefaultSubvolume mountpoint = do | |||
95 | when (null subvolId) $ fail "could not obtain btrfs Subvolume ID" | 101 | when (null subvolId) $ fail "could not obtain btrfs Subvolume ID" |
96 | cmd_ "btrfs subvolume set-default" [strip subvolId, mountpoint] | 102 | cmd_ "btrfs subvolume set-default" [strip subvolId, mountpoint] |
97 | 103 | ||
98 | defaultImageName :: String | ||
99 | defaultImageName = "minbase" | ||
100 | |||
101 | stripSuffix :: Text -> String -> String | 104 | stripSuffix :: Text -> String -> String |
102 | stripSuffix s t = fromMaybe t $ fmap unpack $ Text.stripSuffix s $ pack t | 105 | stripSuffix s t = fromMaybe t $ fmap unpack $ Text.stripSuffix s $ pack t |
103 | 106 | ||
104 | data Options = Options { optCommand :: Command } | 107 | data Options = Options { optCommand :: Command } |
105 | data Command = Build BuildOpts | 108 | data Command = Build BuildOpts | Chroot ChrootOpts |
106 | data BuildOpts = BuildOpts { optTarget :: String } | 109 | data BuildOpts = BuildOpts { optTarget :: String } |
110 | data ChrootOpts = ChrootOpts { optTarget :: String, optChrootCommand :: [String] } | ||
107 | 111 | ||
108 | buildOpts :: Parser Command | 112 | buildOpts :: Parser Command |
109 | buildOpts = Build . BuildOpts <$> argument str idm | 113 | buildOpts = Build . BuildOpts <$> argument str idm |
110 | 114 | ||
115 | chrootOpts :: Parser Command | ||
116 | chrootOpts = fmap Chroot $ ChrootOpts <$> argument str idm <*> many (argument str idm) | ||
117 | |||
111 | -- TODO: Fail early on: | 118 | -- TODO: Fail early on: |
112 | -- 1. not running as root | 119 | -- 1. not running as root |
113 | -- 2. no "selfstrap" in PATH | 120 | -- 2. no "selfstrap" in PATH |
@@ -121,16 +128,40 @@ main = customExecParser (prefs showHelpOnEmpty) (info opts desc) >>= run | |||
121 | "which, much like 'debootstrap', creates new installations of Debian."] | 128 | "which, much like 'debootstrap', creates new installations of Debian."] |
122 | parseCommand :: Parser Command | 129 | parseCommand :: Parser Command |
123 | parseCommand = subparser $ Opt.command "build" (info' buildOpts buildDesc) | 130 | parseCommand = subparser $ Opt.command "build" (info' buildOpts buildDesc) |
124 | buildDesc = ["build the image specified by the YAML config file"] | 131 | <> Opt.command "chroot" (info' chrootOpts chrootDesc) |
132 | buildDesc = ["build the image specified by the YAML config file"] | ||
133 | chrootDesc = ["chroot into the mounted image. ", | ||
134 | "Iff the chroot exits with a success value, filesystem changes will persist"] | ||
135 | |||
125 | info' o d = info (helper <*> o) (progDesc $ unwords d) | 136 | info' o d = info (helper <*> o) (progDesc $ unwords d) |
126 | 137 | ||
127 | run :: Options -> IO () | 138 | run :: Options -> IO () |
128 | run (Options (Build (BuildOpts target)))= shakeBuildOneImage (stripSuffix ".yaml" target) | 139 | run (Options (Build (BuildOpts target))) = shakeBuildOneImage target |
140 | run (Options (Chroot (ChrootOpts target args))) = chrootImage target args | ||
141 | |||
142 | chrootImage :: FilePath -> [String] -> IO () | ||
143 | chrootImage target args = | ||
144 | shake shakeOptions {shakeFiles = "_build"} $ do | ||
145 | shakeRules | ||
146 | action $ do | ||
147 | pid <- show <$> liftIO getProcessID | ||
148 | let inp = target -<.> "btrfs" | ||
149 | let tmp = inp <.> "tmp" <.> pid | ||
150 | let mnt = tmp <.> "mnt" | ||
151 | orderOnly [inp] | ||
152 | cmd_ "cp --reflink=always" [inp, tmp] | ||
153 | cmd_ "btrfstune -S0 -f" tmp | ||
154 | cmd_ "mkdir" [mnt] | ||
155 | cmd_ "mount -t btrfs" [tmp, mnt] | ||
156 | cmd_ (WithStderr False) (WithStdout False) "chroot" (mnt:args) -- TODO: cgroup | ||
157 | cmd_ "umount" [mnt] -- TODO: recursive umount | ||
158 | cmd_ "btrfstune -S1" tmp | ||
159 | cmd_ "mv" [tmp, inp] | ||
129 | 160 | ||
130 | shakeBuildOneImage :: FilePath -> IO () | 161 | shakeBuildOneImage :: FilePath -> IO () |
131 | shakeBuildOneImage target = | 162 | shakeBuildOneImage target = |
132 | shake shakeOptions {shakeFiles = "_build"} $ do | 163 | shake shakeOptions {shakeFiles = "_build"} $ do |
133 | want [target <.> "btrfs"] | 164 | want [target -<.> "btrfs"] |
134 | shakeRules | 165 | shakeRules |
135 | 166 | ||
136 | head1 :: String -> String | 167 | head1 :: String -> String |
@@ -148,6 +179,13 @@ pathLocate c = (getEnv "SUDO_USER" >>=) $ fmap (validatePath . head1 . fromStdou | |||
148 | validatePath "" = Nothing | 179 | validatePath "" = Nothing |
149 | validatePath x = Just x | 180 | validatePath x = Just x |
150 | 181 | ||
182 | infix 1 ~%> | ||
183 | (~%>) :: FilePattern -> (FilePath -> Action ()) -> Rules () | ||
184 | filepat ~%> act = phonys f | ||
185 | where | ||
186 | f file | (filepat ?== file) = Just (act file) | ||
187 | f _ = Nothing | ||
188 | |||
151 | shakeRules :: Rules () | 189 | shakeRules :: Rules () |
152 | shakeRules = do | 190 | shakeRules = do |
153 | "_build/*.yaml.canon" %> \out -> do | 191 | "_build/*.yaml.canon" %> \out -> do |
@@ -160,7 +198,6 @@ shakeRules = do | |||
160 | need [cfgFile] | 198 | need [cfgFile] |
161 | cfg <- read <$> readFile' cfgFile | 199 | cfg <- read <$> readFile' cfgFile |
162 | buildRoot cfg out | 200 | buildRoot cfg out |
163 | |||
164 | "*.btrfs" %> \out -> do | 201 | "*.btrfs" %> \out -> do |
165 | need ["_build" </> out] | 202 | need ["_build" </> out] |
166 | -- WithStderr False needed for `cp` to interact with the tty | 203 | -- WithStderr False needed for `cp` to interact with the tty |