Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
snap: auto-import assertions from block devices #2044
Closed
Commits
Jump to file or symbol
Failed to load files and symbols.
| @@ -0,0 +1,127 @@ | ||
| +// -*- Mode: Go; indent-tabs-mode: t -*- | ||
| + | ||
| +/* | ||
| + * Copyright (C) 2014-2016 Canonical Ltd | ||
| + * | ||
| + * This program is free software: you can redistribute it and/or modify | ||
| + * it under the terms of the GNU General Public License version 3 as | ||
| + * published by the Free Software Foundation. | ||
| + * | ||
| + * This program is distributed in the hope that it will be useful, | ||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| + * GNU General Public License for more details. | ||
| + * | ||
| + * You should have received a copy of the GNU General Public License | ||
| + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| + * | ||
| + */ | ||
| + | ||
| +package main | ||
| + | ||
| +import ( | ||
| + "bufio" | ||
| + "fmt" | ||
| + "os" | ||
| + "path/filepath" | ||
| + "strings" | ||
| + "time" | ||
| + | ||
| + "github.com/jessevdk/go-flags" | ||
| + | ||
| + "github.com/snapcore/snapd/i18n" | ||
| + "github.com/snapcore/snapd/osutil" | ||
| +) | ||
| + | ||
| +const autoImportsName = "auto-imports.assert" | ||
| + | ||
| +var mountInfoPath = "/proc/self/mountinfo" | ||
| + | ||
| +func autoImportCandidates() ([]string, error) { | ||
| + var cands []string | ||
| + | ||
| + // see https://www.kernel.org/doc/Documentation/filesystems/proc.txt, | ||
| + // sec. 3.5 | ||
| + f, err := os.Open(mountInfoPath) | ||
| + if err != nil { | ||
| + return nil, err | ||
| + } | ||
| + defer f.Close() | ||
| + | ||
| + scanner := bufio.NewScanner(f) | ||
| + for scanner.Scan() { | ||
| + l := strings.Fields(scanner.Text()) | ||
| + if len(l) == 0 { | ||
| + continue | ||
| + } | ||
| + mountPoint := l[4] | ||
| + mountSrc := l[9] | ||
| + // skip internal mounts | ||
| + if !strings.HasPrefix(mountSrc, "/dev/") { | ||
| + continue | ||
| + } | ||
| + // skip snaps | ||
| + if strings.HasPrefix(mountSrc, "/dev/loop") { | ||
| + continue | ||
| + } | ||
| + cand := filepath.Join(mountPoint, autoImportsName) | ||
| + if osutil.FileExists(cand) { | ||
| + cands = append(cands, cand) | ||
| + } | ||
| + } | ||
| + | ||
| + return cands, scanner.Err() | ||
| + | ||
| +} | ||
| + | ||
| +func autoImportFromAllMounts() error { | ||
| + cands, err := autoImportCandidates() | ||
| + if err != nil { | ||
| + return err | ||
| + } | ||
| + | ||
| + added := 0 | ||
| + for _, cand := range cands { | ||
| + if err := ackFile(cand); err != nil { | ||
| + fmt.Fprintf(Stderr, "cannot import %q: %s\n", cand, err) | ||
| + continue | ||
| + } | ||
| + fmt.Fprintf(Stdout, "acked %q\n", cand) | ||
| + } | ||
| + | ||
| + // FIXME: once we have a way to know if a device is owned | ||
| + // do no longer call this unconditionally | ||
| + if added > 0 { | ||
| + // FIXME: run `snap create-users --known` | ||
| + } | ||
| + | ||
| + return nil | ||
| +} | ||
| + | ||
| +type cmdAutoImport struct{} | ||
| + | ||
| +var shortAutoImportHelp = i18n.G("Imports assertions from mounted devices") | ||
| + | ||
| +var longAutoImportHelp = i18n.G("The auto-import command imports assertions found in the auto-import.assert file in mounted devices.") | ||
| + | ||
| +func init() { | ||
| + cmd := addCommand("auto-import", | ||
| + shortAutoImportHelp, | ||
| + longAutoImportHelp, | ||
| + func() flags.Commander { | ||
| + return &cmdAutoImport{} | ||
| + }, nil, nil) | ||
| + cmd.hidden = true | ||
| +} | ||
| + | ||
| +func (x *cmdAutoImport) Execute(args []string) error { | ||
| + if len(args) > 0 { | ||
| + return ErrExtraArgs | ||
| + } | ||
| + | ||
| + // SUCKS: racy because the mount is not done when the script is | ||
mvo5
Collaborator
|
||
| + // called | ||
| + time.Sleep(1 * time.Second) | ||
| + | ||
| + return autoImportFromAllMounts() | ||
| +} | ||
| @@ -0,0 +1,77 @@ | ||
| +// -*- Mode: Go; indent-tabs-mode: t -*- | ||
| + | ||
| +/* | ||
| + * Copyright (C) 2016 Canonical Ltd | ||
| + * | ||
| + * This program is free software: you can redistribute it and/or modify | ||
| + * it under the terms of the GNU General Public License version 3 as | ||
| + * published by the Free Software Foundation. | ||
| + * | ||
| + * This program is distributed in the hope that it will be useful, | ||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| + * GNU General Public License for more details. | ||
| + * | ||
| + * You should have received a copy of the GNU General Public License | ||
| + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| + * | ||
| + */ | ||
| + | ||
| +package main_test | ||
| + | ||
| +import ( | ||
| + "fmt" | ||
| + "io/ioutil" | ||
| + "net/http" | ||
| + "path/filepath" | ||
| + | ||
| + . "gopkg.in/check.v1" | ||
| + | ||
| + snap "github.com/snapcore/snapd/cmd/snap" | ||
| +) | ||
| + | ||
| +var mockMountInfoFmt = ` | ||
| +24 0 8:18 / %s rw,relatime shared:1 - ext4 /dev/sdb2 rw,errors=remount-ro,data=ordered | ||
| +` | ||
| + | ||
| +func makeMockMountInfo(c *C, content string) string { | ||
| + fn := filepath.Join(c.MkDir(), "mountinfo") | ||
| + err := ioutil.WriteFile(fn, []byte(content), 0644) | ||
| + c.Assert(err, IsNil) | ||
| + return fn | ||
| +} | ||
| + | ||
| +func (s *SnapSuite) TestAutoImportAssertsHappy(c *C) { | ||
| + fakeAssertData := []byte("my-assertion") | ||
| + | ||
| + n := 0 | ||
| + total := 1 | ||
| + s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { | ||
| + switch n { | ||
| + case 0: | ||
| + c.Check(r.Method, Equals, "POST") | ||
| + postData, err := ioutil.ReadAll(r.Body) | ||
| + c.Assert(err, IsNil) | ||
| + c.Check(postData, DeepEquals, fakeAssertData) | ||
| + fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done"}}`) | ||
| + n++ | ||
| + default: | ||
| + c.Fatalf("unexpected request: %v (expected %d got %d)", r, total, n) | ||
| + } | ||
| + | ||
| + }) | ||
| + | ||
| + fakeAssertsFn := filepath.Join(c.MkDir(), "auto-imports.assert") | ||
| + err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644) | ||
| + c.Assert(err, IsNil) | ||
| + | ||
| + content := fmt.Sprintf(mockMountInfoFmt, filepath.Dir(fakeAssertsFn)) | ||
| + snap.MockMountInfoPath(makeMockMountInfo(c, content)) | ||
| + | ||
| + rest, err := snap.Parser().ParseArgs([]string{"auto-import"}) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(rest, DeepEquals, []string{}) | ||
| + c.Check(s.Stdout(), Equals, fmt.Sprintf("acked %q\n", fakeAssertsFn)) | ||
| + c.Check(s.Stderr(), Equals, "") | ||
| + c.Check(n, Equals, total) | ||
| +} |
14
debian/rules
| @@ -0,0 +1,10 @@ | ||
| +[Unit] | ||
| +Description=Auto import assertions from block devices | ||
| +After=snapd.service snapd.socket | ||
| + | ||
| +[Service] | ||
| +Type=oneshot | ||
| +ExecStart=/usr/bin/snap auto-import | ||
| + | ||
| +[Install] | ||
| +WantedBy=multi-user.target |
| @@ -0,0 +1,2 @@ | ||
| +ACTION=="add", SUBSYSTEM=="block" ENV{ID_FS_USAGE}=="filesystem"\ | ||
| + ENV{SYSTEMD_WANTS}="snapd.autoimport.service" |
Can we do something a bit nicer here, such as trying to stat things more than once depending on the resulting error, to account for such delays?