github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/bootloader/withbootassettesting.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 // +build withbootassetstesting 3 4 /* 5 * Copyright (C) 2021 Canonical Ltd 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 3 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 package bootloader 22 23 import ( 24 "bytes" 25 "fmt" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "strings" 30 31 "github.com/snapcore/snapd/bootloader/assets" 32 "github.com/snapcore/snapd/logger" 33 "github.com/snapcore/snapd/snapdenv" 34 ) 35 36 var maybeInjectOsReadlink = os.Readlink 37 38 func MockMaybeInjectOsReadlink(m func(string) (string, error)) (restore func()) { 39 old := maybeInjectOsReadlink 40 maybeInjectOsReadlink = m 41 return func() { 42 maybeInjectOsReadlink = old 43 } 44 } 45 46 func MaybeInjectTestingBootloaderAssets() { 47 // this code is ran only when snapd is built with specific testing tag 48 49 if !snapdenv.Testing() { 50 return 51 } 52 53 // log an info level message, it is a testing build of snapd anyway 54 logger.Noticef("maybe inject boot assets?") 55 56 // is there a marker file at /usr/lib/snapd/ in the snap? 57 selfExe, err := maybeInjectOsReadlink("/proc/self/exe") 58 if err != nil { 59 panic(fmt.Sprintf("cannot readlink: %v", err)) 60 } 61 62 injectPieceRaw, err := ioutil.ReadFile(filepath.Join(filepath.Dir(selfExe), "bootassetstesting")) 63 if os.IsNotExist(err) { 64 logger.Noticef("no boot asset testing marker") 65 return 66 } 67 if len(injectPieceRaw) == 0 { 68 logger.Noticef("boot asset testing snippet is empty") 69 } 70 injectPiece := strings.TrimSpace(string(injectPieceRaw)) 71 72 // with boot assets testing enabled and the marker file present, inject 73 // a mock boot config update 74 75 grubBootconfig := assets.Internal("grub.cfg") 76 if grubBootconfig == nil { 77 panic("no bootconfig") 78 } 79 snippets := assets.SnippetsForEditions("grub.cfg:static-cmdline") 80 if len(snippets) == 0 { 81 panic(fmt.Sprintf("cannot obtain internal grub.cfg:static-cmdline snippets")) 82 } 83 84 internalEdition, err := editionFromConfigAsset(bytes.NewReader(grubBootconfig)) 85 if err != nil { 86 panic(fmt.Sprintf("cannot inject boot config for asset: %v", err)) 87 } 88 // bump the injected edition number 89 injectedEdition := internalEdition + 1 90 91 logger.Noticef("injecting grub boot assets for testing, edition: %v snippet: %q", injectedEdition, injectPiece) 92 93 lastSnippet := string(snippets[len(snippets)-1].Snippet) 94 injectedSnippet := lastSnippet + " " + injectPiece 95 injectedSnippets := append(snippets, 96 assets.ForEditions{FirstEdition: injectedEdition, Snippet: []byte(injectedSnippet)}) 97 98 assets.InjectSnippetsForEditions("grub.cfg:static-cmdline", injectedSnippets) 99 100 origGrubBoot := string(grubBootconfig) 101 bumpedEdition := strings.Replace(origGrubBoot, 102 fmt.Sprintf("%s%d", editionHeader, internalEdition), 103 fmt.Sprintf("%s%d", editionHeader, injectedEdition), 104 1) 105 // see data/grub.cfg for reference 106 bumpedCmdlineAndEdition := strings.Replace(bumpedEdition, 107 fmt.Sprintf(`set snapd_static_cmdline_args='%s'`, lastSnippet), 108 fmt.Sprintf(`set snapd_static_cmdline_args='%s'`, injectedSnippet), 109 1) 110 111 assets.InjectInternal("grub.cfg", []byte(bumpedCmdlineAndEdition)) 112 } 113 114 func init() { 115 MaybeInjectTestingBootloaderAssets() 116 }